aboutsummaryrefslogtreecommitdiff
path: root/vendor/voclient/libvotable/votParse.c
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /vendor/voclient/libvotable/votParse.c
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'vendor/voclient/libvotable/votParse.c')
-rw-r--r--vendor/voclient/libvotable/votParse.c4017
1 files changed, 4017 insertions, 0 deletions
diff --git a/vendor/voclient/libvotable/votParse.c b/vendor/voclient/libvotable/votParse.c
new file mode 100644
index 00000000..1aaf5f4a
--- /dev/null
+++ b/vendor/voclient/libvotable/votParse.c
@@ -0,0 +1,4017 @@
+/**
+ * VOTPARSE.C -- Public interface procedures for the libVOTable parser.
+ *
+ * @file votParse.c
+ * @author Mike Fitzpatrick and Eric Timmermann
+ * @date 8/03/09
+ *
+ * @brief Public interface procedures for the libVOTable parser.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#define _GNU_SOURCE
+#include <string.h>
+#include <expat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include "votParseP.h"
+#include "votParse.h"
+#ifdef HAVE_CFITSIO
+#include "fitsio.h"
+#endif
+
+
+#define BUFSIZE 4096
+
+
+extern char *strcasestr();
+
+
+/* Private procedures
+ */
+static Element *vot_elementDup (handle_t element_h);
+static handle_t vot_nodeCreate (int type);
+static char *vot_deWS (char *in);
+#ifdef USE_VALIDITY
+static int vot_validParents (int type);
+static int vot_validChildren (int type);
+#endif
+
+static void vot_attachToNode (handle_t parent, handle_t new);
+static void vot_attachSibling (handle_t big_brother, handle_t new);
+static void vot_dumpXML (Element *node, int level, int indent, FILE *fd);
+
+static void vot_htmlHeader (FILE *fd, char *fname);
+static void vot_htmlTableMeta (FILE *fd, handle_t res, char *ifname);
+static void vot_htmlTableData (FILE *fd, handle_t res, char *ifname);
+static void vot_htmlFooter (FILE *fd);
+
+static int vot_simpleGetURL (char *url, char *ofname);
+
+#ifdef USE_DEBUG
+static void vot_printData (Element *tdata);
+static void votBob (void);
+#endif
+
+
+/** *************************************************************************
+ * Public Interface
+ *
+ * vot = vot_openVOTABLE (filename|str|NULL)
+ * vot_closeVOTABLE (vot)
+ *
+ * res = vot_getRESOURCE (vot|res)
+ * tab = vot_getTABLE (res)
+ * field = vot_getFIELD (tab)
+ *
+ * data = vot_getDATA (tab)
+ *
+ * tdata = vot_getTABLEDATA (data) // data elements
+ * tr = vot_getTR (tdata)
+ * td = vot_getTD (tr)
+ * bin = vot_getBINARY (data)
+ * bin = vot_getBINARY2 (data)
+ * fits = vot_getFITS (data)
+ *
+ * group = vot_getGROUP (vot|res|tab|group)
+ * fldref = vot_getFIELDRef (group)
+ * parref = vot_getPARAMRef (group)
+ *
+ * desc = vot_getDESCRIPTION (handle)
+ * param = vot_getPARAM (handle)
+ * info = vot_getINFO (handle)
+ * stream = vot_getSTREAM (bin|fits)
+ *
+ * val = vot_getVALUES (field|param|info)
+ * min = vot_getMIN (val)
+ * max = vot_getMAX (val)
+ * opt = vot_getOPTION (val)
+ *
+ * link = vot_getLINK (res|info|param|field|table)
+ *
+ * sys = vot_getCOOSYS (vot) // Compatability
+ *
+ *
+ * h = vot_newRESOURCE (handle_t parent_h)
+ * h = vot_newTABLE (handle_t parent_h)
+ * h = vot_newFIELD (handle_t parent_h)
+ * h = vot_newDATA (handle_t parent_h)
+ * h = vot_newTABLEDATA (handle_t parent_h)
+ * h = vot_newTR (handle_t parent_h)
+ * h = vot_newTD (handle_t parent_h)
+ * h = vot_newBINARY (handle_t parent_h)
+ * h = vot_newBINARY2 (handle_t parent_h)
+ * h = vot_newFITS (handle_t parent_h)
+ * h = vot_newGROUP (handle_t parent_h)
+ * h = vot_newFIELDRef (handle_t parent_h)
+ * h = vot_newPARAMRef (handle_t parent_h)
+ * h = vot_newDESCRIPTION (handle_t parent_h)
+ * h = vot_newPARAM (handle_t parent_h)
+ * h = vot_newINFO (handle_t parent_h)
+ * h = vot_newSTREAM (handle_t parent_h)
+ * h = vot_newVALUES (handle_t parent_h)
+ * h = vot_newMIN (handle_t parent_h)
+ * h = vot_newMAX (handle_t parent_h)
+ * h = vot_newOPTION (handle_t parent_h)
+ * h = vot_newLINK (handle_t parent_h)
+ * h = vot_newCOOSYS (handle_t parent_h)
+ *
+ *
+ * handle = vot_newNode (parent, type)
+ * vot_freeNode (node)
+ * vot_deleteNode (node)
+ * vot_attachNode (parent, new)
+ * handle = vot_copyElement (handle_t source_h, handle_t parent_h)
+ *
+ *
+ * val = vot_getValue (handle)
+ * stat = vot_setValue (handle, value)
+ *
+ * attr = vot_getAttr (handle, attr)
+ * stat = vot_setAttr (handle, attr, value)
+ *
+ *
+ * type = vot_getDATAType (data) // Utilities
+ * typeStr = vot_getDATATypeString (data)
+ *
+ * nc = vot_getNCols (tdata_h)
+ * nr = vot_getNRows (tdata_h)
+ * val = vot_getTableCell (tdata_h, row, col)
+ * stat = vot_sortTable (tdata_h, col, string_sort, sort_order)
+ *
+ * len = vot_getLength (elem_h)
+ * N = vot_getNumberOf (elem_h, type)
+ *
+ * col = vot_colByName (tab_h, name, alt)
+ * col = vot_colByUCD (tab_h, name, alt)
+ * col = vot_colByID (tab_h, name, alt)
+ *
+ * handle = vot_findByAttr (parent, name, value)
+ * handle *vot_findInGroup (group, type)
+ * handle = vot_getNext (handle)
+ * handle = vot_getSibling (handle)
+ * handle = vot_getChild (handle)
+ * handle = vot_getParent (handle)
+ * handle = vot_getChildOfType (handle, int type)
+ *
+ * int = vot_valueOf (handle)
+ * type = vot_typeOf (handle)
+ * vot_setWarnings (value)
+ *
+ * vot_writeVOTable (handle, char *fname, int indent)
+ * vot_writeHTML (handle, char *fname)
+ * vot_writeSHTML (handle, char *fname)
+ * vot_writeFITS (handle, char *fname)
+ * vot_writeASV (handle, char *fname, int hdr)
+ * vot_writeBSV (handle, char *fname, int hdr)
+ * vot_writeCSV (handle, char *fname, int hdr)
+ * vot_writeTSV (handle, char *fname, int hdr)
+ * vot_writeDelimited (handle, char *fname, char delim, int hdr)
+ *
+ *
+ ** *************************************************************************/
+
+
+Stack *element_stack = NULL; /* This holds a stack of elements. Should be
+ * empty most of the time.
+ */
+
+Element *vot_struct = NULL; /* This will hold all the VOTs. The first
+ * Element in this structure is a ROOT Element.
+ */
+
+char *votELevel = ""; /* Error Message Level
+ */
+
+int votWarn = 0; /* Warn about parsing issues. Values:
+ * 0 No messages (lax parsing)
+ * 1 Warning messages
+ * 2 Strict parsing
+ */
+
+
+
+/**
+ * vot_openVOTABLE -- Parse a VOTable and return a handle to it
+ *
+ * @brief Parse a VOTable and return a handle to it
+ * @fn handle_t vot_openVOTABLE (char *arg)
+ *
+ * @param arg The source of the table
+ * @return The root node handle of the VOTable
+ */
+handle_t
+vot_openVOTABLE (char *arg)
+{
+ FILE *fd = (FILE *) NULL;
+ Element *my_element;
+ char buf[BUFSIZE], *ip, urlFname[BUFSIZE];
+ size_t len, nleft = 0, fsize = -1, nread = 0;
+ int done, ret_handle, nerrs;
+ XML_Parser parser;
+
+ struct stat st;
+
+
+ memset (buf, 0, BUFSIZE);
+ memset (urlFname, 0, BUFSIZE);
+
+
+ vot_newHandleTable (); /* initialize the handle table */
+ if (element_stack == NULL)
+ element_stack = vot_newStack ();
+
+ if (vot_struct == NULL)
+ vot_struct = vot_newElem (TY_ROOT);
+
+ votPush (element_stack, vot_struct);
+
+ if (arg == NULL) {
+ my_element = vot_newElem (TY_VOTABLE);
+
+ if (vot_struct->child)
+ vot_struct->last_child->next = my_element;
+ else
+ vot_struct->child = my_element;
+
+ vot_struct->last_child = my_element;
+
+ vot_clearStack (element_stack);
+
+ my_element->parent = vot_struct;
+
+ return (vot_setHandle (my_element));
+
+ } else if (strncmp (arg, "http://", 7) == 0) { /* input from URL */
+ extern int vot_getURL (char *url, char *fname, int *nbytes);
+ int tfd = 0;
+
+ /* Open a temp file for the downloaded URL.
+ */
+ strcpy (urlFname, "/tmp/votXXXXXX");
+ if ((tfd = mkstemp (urlFname) < 0))
+ strcpy (urlFname, "/tmp/votquery");
+ close (tfd);
+
+ nerrs = vot_simpleGetURL (arg, urlFname);
+ if ( !(fd = fopen (urlFname, "r")) ) {
+ fprintf (stderr, "Unable to open url '%s'\n", arg);
+ return (0); /* cannot open file error */
+ }
+ fstat (fileno(fd), &st);
+ fsize = (size_t) st.st_size;
+
+ } else if (strcmp (arg, "-") == 0 || strncasecmp (arg, "stdin", 5) == 0) {
+ /* input from stdin */
+ fd = stdin;
+ fsize = -1;
+
+ } else if (strncmp (arg, "file://", 7) == 0) { /* input from URL */
+ len = strlen (&arg[7]);
+ if (!(fd = fopen (&arg[7], "r"))) {
+ fprintf (stderr, "Unable to open input file '%s'\n", &arg[7]);
+ return (0); /* cannot open file error */
+ }
+ fstat (fileno(fd), &st);
+ fsize = (size_t) st.st_size;
+
+ } else if (access (arg, R_OK) == 0) { /* input from file */
+ len = strlen (arg);
+ if (!(fd = fopen (arg, "r"))) {
+ fprintf (stderr, "Unable to open input file '%s'\n", arg);
+ return (0); /* cannot open file error */
+ }
+ fstat (fileno(fd), &st);
+ fsize = (size_t) st.st_size;
+
+ } else if (strcasestr (arg, "votable")) {
+ /* input argument is XML string */
+ ip = arg;
+ len = strlen (arg);
+
+ } else {
+ fprintf (stderr, "openVOTable(): Invalid input arg '%s'\n", arg);
+ return (-1);
+ }
+
+
+ /* Create the parser and set the input handlers.
+ */
+ parser = XML_ParserCreate (NULL);
+ XML_SetElementHandler (parser, vot_startElement, vot_endElement);
+ XML_SetCdataSectionHandler (parser, vot_startCData, vot_endCData);
+ XML_SetCharacterDataHandler (parser, vot_charData);
+
+ ip = arg; /* initialize */
+ done = 0;
+ nleft = fsize;
+ nread = 0;
+
+ if (fd) {
+ do {
+ memset (buf, 0, BUFSIZE);
+ len = fread (buf, 1, sizeof(buf), fd);
+
+ if (nread == 0) {
+ /* Check that this actually is a VOTable.
+ */
+ if (strcasestr (buf, "<votable") == (char *) NULL) {
+ if (fd != stdin) fclose (fd);
+ if (urlFname[0]) unlink (urlFname);
+ return (-1); /* not a votable */
+ }
+ }
+ nread += len;
+
+ if (fd != stdin) {
+ nleft -= len;
+ done = nread >= fsize;
+ } else
+ done = (len == 0 ? feof (stdin) : 0);
+
+
+ if (done && buf[len-1] == '\0') /* trim trailing null */
+ while (len && !buf[len-1])
+ len--;
+ if (done && buf[len-1] != '\n') /* no newline on file */
+ buf[len] = '\n';
+
+ if (!XML_Parse (parser, buf, len, done)) {
+ fprintf (stderr, "Error: %s at line %d\n",
+ XML_ErrorString (XML_GetErrorCode (parser)),
+ (int)XML_GetCurrentLineNumber (parser));
+ return (0); /* parse error */
+ }
+ } while (!done);
+
+ } else {
+ if (!XML_Parse (parser, ip, len, 1)) {
+ fprintf (stderr, "Error: %s at line %d\n",
+ XML_ErrorString (XML_GetErrorCode (parser)),
+ (int)XML_GetCurrentLineNumber (parser));
+ return (0); /* parse error */
+ }
+ }
+ XML_ParserFree (parser);
+
+ if (fd && fd != stdin)
+ fclose (fd);
+ if (urlFname[0])
+ unlink (urlFname);
+
+ vot_clearStack (element_stack);
+
+ ret_handle = vot_lookupHandle (vot_struct->last_child);
+
+ return (ret_handle);
+}
+
+
+/**
+ * vot_closeVOTABLE -- Destroy the root node and all of it's children.
+ *
+ * @brief Destroy the root node and all of it's children.
+ * @fn vot_closeVOTABLE (handle_t vot)
+ *
+ * @param vot A handle to the Element that you want deleted
+ * @return nothing
+ *
+ * @warning Destroys the node and all of it's children.
+ */
+void
+vot_closeVOTABLE (handle_t vot)
+{
+ Element *elem = vot_getElement (vot);
+ int my_type = vot_elemType (elem);
+
+
+ if ((my_type != TY_VOTABLE)) {
+ votEmsg ("closeVOTABLE() arg must be a VOTABLE tag\n");
+ return;
+ }
+ vot_deleteNode (vot);
+}
+
+
+
+
+/*****************************************************************************
+ * Routines to get nodes of a VOTable as a handle.
+ ****************************************************************************/
+
+/**
+ * vot_getRESOURCE -- Gets the RESOURCE node from the parent handle
+ *
+ * @brief Gets the RESOURCE node from the parent handle
+ * @fn handle_t vot_getRESOURCE (handle_t handle)
+ *
+ * @param handle Parent handle containing a RESOURCE
+ * @return A handle to the first RESOURCE node, or zero
+ */
+handle_t
+vot_getRESOURCE (handle_t handle)
+{
+ Element *elem, *child;
+ int my_type;
+
+
+ /* Get Element pointer for the element.
+ */
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ /* Make sure it's a node that can have a RESOURCE.
+ */
+ if ((my_type != TY_VOTABLE) && (my_type != TY_RESOURCE)) {
+ votEmsg ("RESOURCE must be child of a RESOURCE or VOTABLE tag\n");
+ return (0);
+ }
+
+ /* Go through children until a RESOURCE is found.
+ */
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_RESOURCE)
+ break;
+
+ /* If nothing found return 0, if found return handle to found Element.
+ */
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getTABLE -- Gets the TABLE node from the parent handle
+ *
+ * @brief Gets the TABLE node from the parent handle
+ * @fn handle_t vot_getTABLE (handle_t handle)
+ *
+ * @param handle Parent handle containing a TABLE
+ * @return A handle to the first TABLE node, or zero
+ */
+handle_t
+vot_getTABLE (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_RESOURCE) {
+ votEmsg ("TABLE must be child of a RESOURCE tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_TABLE)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getFIELD -- Gets the FIELD node from the parent handle
+ *
+ * @brief Gets the FIELD node from the parent handle
+ * @fn handle_t vot_getFIELD (handle_t handle)
+ *
+ * @param handle Parent handle containing a FIELD
+ * @return A handle to the first FIELD node, or zero
+ */
+handle_t
+vot_getFIELD (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_TABLE) {
+ votEmsg ("FIELD must be child of a TABLE tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_FIELD)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getDATA -- Gets the DATA node from the parent handle
+ *
+ * @brief Gets the DATA node from the parent handle
+ * @fn handle_t vot_getDATA (handle_t handle)
+ *
+ * @param handle Parent handle containing a DATA
+ * @return A handle to the first DATA node, or zero
+ */
+handle_t
+vot_getDATA (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_TABLE) {
+ votEmsg ("DATA must be child of a TABLE tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_DATA)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getTABLEDATA -- Gets the TABLEDATA node from the parent handle
+ *
+ * @brief Gets the TABLEDATA node from the parent handle
+ * @fn handle_t vot_getTABLEDATA (handle_t handle)
+ *
+ * @param handle Parent handle containing a TABLEDATA
+ * @return A handle to the first TABLEDATA node, or zero
+ */
+handle_t
+vot_getTABLEDATA (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_DATA) {
+ votEmsg ("TABLEDATA must be child of a DATA tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_TABLEDATA)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getTR -- Gets the TR node from the parent handle
+ *
+ * @brief Gets the TR node from the parent handle
+ * @fn handle_t vot_getTR (handle_t handle)
+ *
+ * @param handle Parent handle containing a TR
+ * @return A handle to the first TR node, or zero
+ */
+handle_t
+vot_getTR (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_TABLEDATA) {
+ votEmsg ("TR must be child of a TABLEDATA tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_TR)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getTD -- Gets the TD node from the parent handle
+ *
+ * @brief Gets the TD node from the parent handle
+ * @fn handle_t vot_getTD (handle_t handle)
+ *
+ * @param handle Parent handle containing a TD
+ * @return A handle to the first TD node, or zero
+ */
+handle_t
+vot_getTD (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_TR) {
+ votEmsg ("TD must be child of a TR tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_TD)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getBINARY -- Gets the BINARY node from the parent handle
+ *
+ * @brief Gets the BINARY node from the parent handle
+ * @fn handle_t vot_getBINARY (handle_t handle)
+ *
+ * @param handle Parent handle containing a BINARY
+ * @return A handle to the first BINARY node, or zero
+ */
+handle_t
+vot_getBINARY (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_DATA) {
+ votEmsg ("BINARY must be child of a DATA tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_BINARY || child->type == TY_BINARY2)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getBINARY2 -- Gets the BINARY2 node from the parent handle
+ *
+ * @brief Gets the BINARY2 node from the parent handle
+ * @fn handle_t vot_getBINARY2 (handle_t handle)
+ *
+ * @param handle Parent handle containing a BINARY2
+ * @return A handle to the first BINARY2 node, or zero
+ */
+handle_t
+vot_getBINARY2 (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_DATA) {
+ votEmsg ("BINARY2 must be child of a DATA tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_BINARY2)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getFITS -- Gets the FITS node from the parent handle
+ *
+ * @brief Gets the FITS node from the parent handle
+ * @fn handle_t vot_getFITS (handle_t handle)
+ *
+ * @param handle Parent handle containing a FITS
+ * @return A handle to the first FITS node, or zero
+ */
+handle_t
+vot_getFITS (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_DATA) {
+ votEmsg ("FITS must be child of a DATA tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_FITS)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getGROUP -- Gets the GROUP node from the parent handle
+ *
+ * @brief Gets the GROUP node from the parent handle
+ * @fn handle_t vot_getGROUP (handle_t handle)
+ *
+ * @param handle Parent handle containing a GROUP
+ * @return A handle to the first GROUP node, or zero
+ */
+handle_t
+vot_getGROUP (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if ((my_type != TY_VOTABLE) && (my_type != TY_RESOURCE) &&
+ (my_type != TY_TABLE) && (my_type != TY_GROUP)) {
+ votEmsg (
+ "GROUP must be child of a RESOURCE, TABLE, GROUP or VOTABLE\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_GROUP)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getFIELDref -- Gets the FIELDref node from the parent handle
+ *
+ * @brief Gets the FIELDref node from the parent handle
+ * @fn handle_t vot_getFIELDRef (handle_t handle)
+ *
+ * @param handle Parent handle containing a FIELDref
+ * @return A handle to the first FIELDref node, or zero
+ */
+handle_t
+vot_getFIELDRef (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_GROUP) {
+ votEmsg ("FIELDref must be child of a GROUP tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_FIELDREF)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getPARAMRef -- Gets the PARAMref node from the parent handle
+ *
+ * @brief Gets the PARAMRef node from the parent handle
+ * @fn handle_t vot_getPARAMRef (handle_t handle)
+ *
+ * @param handle Parent handle containing a PARAMRef
+ * @return A handle to the first PARAMRef node, or zero
+ */
+handle_t
+vot_getPARAMRef (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_GROUP) {
+ votEmsg ("PARAMref must be child of a GROUP tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_PARAMREF)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getDESCRIPTION -- Gets the DESCRIPTION node from the parent handle
+ *
+ * @brief Gets the DESCRIPTION node from the parent handle
+ * @fn handle_t vot_getDESCRIPTION (handle_t handle)
+ *
+ * @param handle Parent handle containing a DESCRIPTION
+ * @return A handle to the first DESCRIPTION node, or zero
+ */
+handle_t
+vot_getDESCRIPTION (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+
+ elem = vot_getElement (handle);
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_DESCRIPTION)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getPARAM -- Gets the PARAM node from the parent handle
+ *
+ * @brief Gets the PARAM node from the parent handle
+ * @fn handle_t vot_getPARAM (handle_t handle)
+ *
+ * @param handle Parent handle containing a PARAM
+ * @return A handle to the first PARAM node, or zero
+ */
+handle_t
+vot_getPARAM (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+
+ elem = vot_getElement (handle);
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_PARAM)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getINFO -- Gets the INFO node from the parent handle
+ *
+ * @brief Gets the INFO node from the parent handle
+ * @fn handle_t vot_getINFO (handle_t handle)
+ *
+ * @param handle Parent handle containing a INFO
+ * @return A handle to the first INFO node, or zero
+ */
+handle_t
+vot_getINFO (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+
+ elem = vot_getElement (handle);
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_INFO)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getSTREAM -- Gets the STREAM node from the parent handle
+ *
+ * @brief Gets the STREAM node from the parent handle
+ * @fn handle_t vot_getSTREAM (handle_t handle)
+ *
+ * @param handle Parent handle containing a STREAM
+ * @return A handle to the first STREAM node, or zero
+ */
+handle_t
+vot_getSTREAM (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if ((my_type != TY_BINARY) &&
+ (my_type != TY_BINARY2) &&
+ (my_type != TY_FITS)) {
+ votEmsg ("STREAM must be child of a BINARY(2) or FITS tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_STREAM)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getVALUES -- Gets the VALUES node from the parent handle
+ *
+ * @brief Gets the VALUES node from the parent handle
+ * @fn handle_t vot_getVALUES (handle_t handle)
+ *
+ * @param handle Parent handle containing a VALUES
+ * @return A handle to the first VALUES node, or zero
+ */
+handle_t
+vot_getVALUES (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if ((my_type != TY_FIELD) && (my_type != TY_PARAM) &&
+ (my_type != TY_INFO)) {
+ votEmsg ("VALUES must be child of a FIELD, PARAM or INFO tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_VALUES)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getMIN -- Gets the MIN node from the parent handle
+ *
+ * @brief Gets the MIN node from the parent handle
+ * @fn handle_t vot_getMIN (handle_t handle)
+ *
+ * @param handle Parent handle containing a MIN
+ * @return A handle to the first MIN node, or zero
+ */
+handle_t
+vot_getMIN (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_VALUES) {
+ votEmsg ("MIN must be child of a VALUES tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_MIN)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getMAX -- Gets the MAX node from the parent handle
+ *
+ * @brief Gets the MAX node from the parent handle
+ * @fn handle_t vot_getMAX (handle_t handle)
+ *
+ * @param handle Parent handle containing a MAX
+ * @return A handle to the first MAX node, or zero
+ */
+handle_t
+vot_getMAX (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_VALUES) {
+ votEmsg ("MAX must be child of a VALUES tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_MAX)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getOPTION -- Gets the OPTION node from the parent handle
+ *
+ * @brief Gets the OPTION node from the parent handle
+ * @fn handle_t vot_getOPTION (handle_t handle)
+ *
+ * @param handle Parent handle containing a OPTION
+ * @return A handle to the first OPTION node, or zero
+ */
+handle_t
+vot_getOPTION (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if (my_type != TY_VALUES) {
+ votEmsg ("OPTION must be child of a VALUES tag\n");
+ return (0);
+ }
+
+ for (child = elem->child; child; child = child->next)
+ if (child->type == TY_OPTION)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getLINK -- Gets the LINK node from the parent handle
+ *
+ * @brief Gets the LINK node from the parent handle
+ * @fn handle_t vot_getLINK (handle_t handle)
+ *
+ * @param handle Parent handle containing a LINK
+ * @return A handle to the first LINK node, or zero
+ */
+handle_t
+vot_getLINK (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if ((my_type != TY_RESOURCE) && (my_type != TY_TABLE) &&
+ (my_type != TY_FIELD) && (my_type != TY_PARAM) &&
+ (my_type != TY_INFO)) {
+ votEmsg (
+ "LINK must be child of a RESOURCE, TABLE, FIELD, PARAM or INFO\n");
+ return (0);
+ }
+
+ for (child=elem->child; child; child = child->next)
+ if (child->type == TY_LINK)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getCOOSYS -- Gets the COOSYS node from the parent handle
+ *
+ * @brief Gets the COOSYS node from the parent handle
+ * @fn handle_t vot_getCOOSYS (handle_t handle)
+ *
+ * @param handle Parent handle containing a COOSYS
+ * @return A handle to the first COOSYS node, or zero
+ */
+handle_t
+vot_getCOOSYS (handle_t handle)
+{
+ /* Refer to vot_getRESOURCE for detailed comments on function workings. */
+
+ Element *elem, *child;
+ int my_type;
+
+ elem = vot_getElement (handle);
+ my_type = vot_elemType (elem);
+
+ if ((my_type != TY_VOTABLE) && (my_type != TY_RESOURCE)) {
+ votEmsg ("COOSYS must be child of a RESOURCE or VOTABLE tag\n");
+ return (0);
+ }
+
+ for (child=elem->child; child; child = child->next)
+ if (child->type == TY_COOSYS)
+ break;
+
+ return (vot_lookupHandle (child));
+}
+
+
+/**
+ * vot_getDATAType -- Returns the type of the DATA element.
+ *
+ * @brief Returns the type of the DATA element.
+ * @fn char *vot_getDATAType (handle_t data_h)
+ *
+ * @param data_h A handle_t to a DATA
+ * @return The type as an int
+ */
+int
+vot_getDATAType (handle_t data_h)
+{
+ Element *elem = vot_getElement (data_h);
+
+ return (elem->child->type);
+}
+
+
+/**
+ * vot_getDATATypeString -- Returns the type of the DATA element as a string.
+ *
+ * @brief Returns the type of the DATA element as a string.
+ * @fn char *vot_getDATATypeString (handle_t data_h)
+ *
+ * @param data_h A handle_t to a DATA
+ * @return The type as an string
+ */
+char *
+vot_getDATATypeString (handle_t data_h)
+{
+ Element *elem = vot_getElement (data_h);
+
+ return ((elem ? vot_elemName (elem->child) : "none"));
+}
+
+
+
+
+/*****************************************************************************
+ * Routines to create new nodes of a VOTable as a handle.
+ ****************************************************************************/
+
+/**
+ * vot_newRESOURCE -- Create new RESOURCE node under the parent handle
+ *
+ * @brief Create new RESOURCE node under the parent handle
+ * @fn handle_t vot_newRESOURCE (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a RESOURCE
+ * @return A handle to the first RESOURCE node, or zero
+ */
+handle_t
+vot_newRESOURCE (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_RESOURCE));
+}
+
+
+/**
+ * vot_newTABLE -- Create new TABLE node under the parent handle
+ *
+ * @brief Create new TABLE node under the parent handle
+ * @fn handle_t vot_newTABLE (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a TABLE
+ * @return A handle to the first TABLE node, or zero
+ */
+handle_t
+vot_newTABLE (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_TABLE));
+}
+
+
+/**
+ * vot_newFIELD -- Create new FIELD node under the parent handle
+ *
+ * @brief Create new FIELD node under the parent handle
+ * @fn handle_t vot_newFIELD (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a FIELD
+ * @return A handle to the first FIELD node, or zero
+ */
+handle_t
+vot_newFIELD (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_FIELD));
+}
+
+
+/**
+ * vot_newDATA -- Create new DATA node under the parent handle
+ *
+ * @brief Create new DATA node under the parent handle
+ * @fn handle_t vot_newDATA (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a DATA
+ * @return A handle to the first DATA node, or zero
+ */
+handle_t
+vot_newDATA (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_DATA));
+}
+
+
+/**
+ * vot_newTABLEDATA -- Create new TABLEDATA node under the parent handle
+ *
+ * @brief Create new TABLEDATA node under the parent handle
+ * @fn handle_t vot_newTABLEDATA (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a TABLEDATA
+ * @return A handle to the first TABLEDATA node, or zero
+ */
+handle_t
+vot_newTABLEDATA (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_TABLEDATA));
+}
+
+
+/**
+ * vot_newTR -- Create new TR node under the parent handle
+ *
+ * @brief Create new TR node under the parent handle
+ * @fn handle_t vot_newTR (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a TR
+ * @return A handle to the first TR node, or zero
+ */
+handle_t
+vot_newTR (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_TR));
+}
+
+
+/**
+ * vot_newTD -- Create new TD node under the parent handle
+ *
+ * @brief Create new TD node under the parent handle
+ * @fn handle_t vot_newTD (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a TD
+ * @return A handle to the first TD node, or zero
+ */
+handle_t
+vot_newTD (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_TD));
+}
+
+
+/**
+ * vot_newBINARY -- Create new BINARY node under the parent handle
+ *
+ * @brief Create new BINARY node under the parent handle
+ * @fn handle_t vot_newBINARY (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a BINARY
+ * @return A handle to the first BINARY node, or zero
+ */
+handle_t
+vot_newBINARY (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_BINARY));
+}
+
+
+/**
+ * vot_newBINARY2 -- Create new BINARY2 node under the parent handle
+ *
+ * @brief Create new BINARY2 node under the parent handle
+ * @fn handle_t vot_newBINARY2 (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a BINARY2
+ * @return A handle to the first BINARY2 node, or zero
+ */
+handle_t
+vot_newBINARY2 (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_BINARY2));
+}
+
+
+/**
+ * vot_newFITS -- Create new FITS node under the parent handle
+ *
+ * @brief Create new FITS node under the parent handle
+ * @fn handle_t vot_newFITS (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a FITS
+ * @return A handle to the first FITS node, or zero
+ */
+handle_t
+vot_newFITS (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_FITS));
+}
+
+
+/**
+ * vot_newGROUP -- Create new GROUP node under the parent handle
+ *
+ * @brief Create new GROUP node under the parent handle
+ * @fn handle_t vot_newGROUP (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a GROUP
+ * @return A handle to the first GROUP node, or zero
+ */
+handle_t
+vot_newGROUP (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_GROUP));
+}
+
+
+/**
+ * vot_newFIELDref -- Create new FIELDref node under the parent handle
+ *
+ * @brief Create new FIELDref node under the parent handle
+ * @fn handle_t vot_newFIELDRef (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a FIELDref
+ * @return A handle to the first FIELDref node, or zero
+ */
+handle_t
+vot_newFIELDRef (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_FIELDREF));
+}
+
+
+/**
+ * vot_newPARAMRef -- Create new PARAMref node under the parent handle
+ *
+ * @brief Create new PARAMRef node under the parent handle
+ * @fn handle_t vot_newPARAMRef (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a PARAMRef
+ * @return A handle to the first PARAMRef node, or zero
+ */
+handle_t
+vot_newPARAMRef (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_PARAMREF));
+}
+
+
+/**
+ * vot_newDESCRIPTION -- Create new DESCRIPTION node under the parent handle
+ *
+ * @brief Create new DESCRIPTION node under the parent handle
+ * @fn handle_t vot_newDESCRIPTION (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a DESCRIPTION
+ * @return A handle to the first DESCRIPTION node, or zero
+ */
+handle_t
+vot_newDESCRIPTION (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_DESCRIPTION));
+}
+
+
+/**
+ * vot_newPARAM -- Create new PARAM node under the parent handle
+ *
+ * @brief Create new PARAM node under the parent handle
+ * @fn handle_t vot_newPARAM (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a PARAM
+ * @return A handle to the first PARAM node, or zero
+ */
+handle_t
+vot_newPARAM (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_PARAM));
+}
+
+
+/**
+ * vot_newINFO -- Create new INFO node under the parent handle
+ *
+ * @brief Create new INFO node under the parent handle
+ * @fn handle_t vot_newINFO (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a INFO
+ * @return A handle to the first INFO node, or zero
+ */
+handle_t
+vot_newINFO (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_INFO));
+}
+
+
+/**
+ * vot_newSTREAM -- Create new STREAM node under the parent handle
+ *
+ * @brief Create new STREAM node under the parent handle
+ * @fn handle_t vot_newSTREAM (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a STREAM
+ * @return A handle to the first STREAM node, or zero
+ */
+handle_t
+vot_newSTREAM (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_STREAM));
+}
+
+
+/**
+ * vot_newVALUES -- Create new VALUES node under the parent handle
+ *
+ * @brief Create new VALUES node under the parent handle
+ * @fn handle_t vot_newVALUES (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a VALUES
+ * @return A handle to the first VALUES node, or zero
+ */
+handle_t
+vot_newVALUES (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_VALUES));
+}
+
+
+/**
+ * vot_newMIN -- Create new MIN node under the parent handle
+ *
+ * @brief Create new MIN node under the parent handle
+ * @fn handle_t vot_newMIN (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a MIN
+ * @return A handle to the first MIN node, or zero
+ */
+handle_t
+vot_newMIN (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_MIN));
+}
+
+
+/**
+ * vot_newMAX -- Create new MAX node under the parent handle
+ *
+ * @brief Create new MAX node under the parent handle
+ * @fn handle_t vot_newMAX (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a MAX
+ * @return A handle to the first MAX node, or zero
+ */
+handle_t
+vot_newMAX (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_MAX));
+}
+
+
+/**
+ * vot_newOPTION -- Create new OPTION node under the parent handle
+ *
+ * @brief Create new OPTION node under the parent handle
+ * @fn handle_t vot_newOPTION (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a OPTION
+ * @return A handle to the first OPTION node, or zero
+ */
+handle_t
+vot_newOPTION (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_OPTION));
+}
+
+
+/**
+ * vot_newLINK -- Create new LINK node under the parent handle
+ *
+ * @brief Create new LINK node under the parent handle
+ * @fn handle_t vot_newLINK (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a LINK
+ * @return A handle to the first LINK node, or zero
+ */
+handle_t
+vot_newLINK (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_LINK));
+}
+
+
+/**
+ * vot_newCOOSYS -- Create new COOSYS node under the parent handle
+ *
+ * @brief Create new COOSYS node under the parent handle
+ * @fn handle_t vot_newCOOSYS (handle_t parent_h)
+ *
+ * @param parent_h Parent handle of a COOSYS
+ * @return A handle to the first COOSYS node, or zero
+ */
+handle_t
+vot_newCOOSYS (handle_t parent_h)
+{
+ return (vot_newNode (parent_h, TY_COOSYS));
+}
+
+
+
+/****************************************************************************/
+
+struct {
+ int type; /** element type */
+ int parents; /** allowed parent types */
+ int children; /** allowed child types */
+} elemParents[] = {
+ { TY_ROOT,
+ 0,
+ TY_VOTABLE
+ },
+ { TY_VOTABLE,
+ TY_ROOT,
+ TY_DESCRIPTION|TY_COOSYS|TY_INFO|TY_PARAM|TY_GROUP|TY_RESOURCE
+ },
+ { TY_RESOURCE,
+ TY_VOTABLE|TY_RESOURCE,
+ TY_DESCRIPTION|TY_COOSYS|TY_INFO|TY_PARAM|TY_GROUP|TY_RESOURCE|TY_LINK|TY_TABLE
+ },
+ { TY_TABLE,
+ TY_RESOURCE,
+ TY_DESCRIPTION|TY_FIELD|TY_INFO|TY_PARAM|TY_GROUP|TY_LINK|TY_DATA
+ },
+ { TY_INFO,
+ TY_VOTABLE|TY_RESOURCE|TY_DATA|TY_TABLE,
+ TY_DESCRIPTION|TY_VALUES|TY_LINK
+ },
+ { TY_STREAM,
+ TY_BINARY|TY_BINARY2|TY_FITS,
+ 0
+ },
+ { TY_FITS,
+ TY_DATA,
+ 0
+ },
+ { TY_TD,
+ TY_TR,
+ 0
+ },
+ { TY_TR,
+ TY_TABLEDATA,
+ TY_TD
+ },
+ { TY_COOSYS,
+ TY_VOTABLE|TY_RESOURCE,
+ 0
+ },
+ { TY_DESCRIPTION,
+ TY_VOTABLE|TY_RESOURCE|TY_TABLE|TY_FIELD|TY_PARAM|TY_GROUP|TY_INFO,
+ 0
+ },
+ { TY_DEFINITIONS,
+ 0,
+ 0
+ },
+ { TY_DATA,
+ TY_TABLE,
+ TY_TABLEDATA|TY_BINARY|TY_BINARY2|TY_FITS
+ },
+ { TY_TABLEDATA,
+ TY_DATA,
+ TY_TR
+ },
+ { TY_GROUP,
+ TY_VOTABLE|TY_RESOURCE|TY_TABLE|TY_GROUP,
+ TY_DESCRIPTION|TY_FIELDREF|TY_PARAM|TY_PARAMREF|TY_GROUP
+ },
+ { TY_PARAM,
+ TY_VOTABLE|TY_RESOURCE|TY_TABLE,
+ TY_DESCRIPTION|TY_VALUES|TY_LINK
+ },
+ { TY_FIELD,
+ TY_TABLE,
+ TY_DESCRIPTION|TY_VALUES|TY_LINK
+ },
+ { TY_FIELDREF,
+ TY_GROUP,
+ 0
+ },
+ { TY_PARAMREF,
+ TY_GROUP,
+ 0
+ },
+ { TY_MIN,
+ TY_VALUES,
+ 0
+ },
+ { TY_MAX,
+ TY_VALUES,
+ 0
+ },
+ { TY_OPTION,
+ TY_VALUES|TY_OPTION,
+ 0
+ },
+ { TY_VALUES,
+ TY_PARAM|TY_INFO,
+ TY_MIN|TY_MAX|TY_OPTION
+ },
+ { TY_LINK,
+ TY_RESOURCE|TY_TABLE|TY_FIELD|TY_PARAM|TY_INFO,
+ 0
+ },
+ { -1, 0, 0 }
+};
+
+
+
+/**
+ * vot_newNode -- Creates a new blank unlinked node.
+ *
+ * @brief Creates a new blank unlinked node.
+ * @fn handle_t vot_newNode (handle_t parent, int type)
+ *
+ * @param parent A handle to the Element that you want to add a node to
+ * @param type The type of node you wish to create
+ * @return A handle to the created node
+ */
+handle_t
+vot_newNode (handle_t parent, int type)
+{
+ /* Refer to vot_newRESOURCE for detailed comments on function workings.
+ */
+ handle_t elem_h = 0;
+
+
+ /* Check that parent is proper for the type we create.
+ assert ( (parent & vot_validParents (type)) );
+ */
+
+ /* Attach the new node to the parent.
+ */
+ elem_h = vot_nodeCreate (type);
+ vot_attachToNode (parent, elem_h);
+
+ return (elem_h);
+}
+
+
+/**
+ * vot_attachNode -- Adds a node as a child of parent.
+ *
+ * @brief Adds a node as a child of parent.
+ * @fn vot_attachNode (handle_t parent, handle_t new)
+ *
+ * @param parent A handle to the Element that you want to add a node to
+ * @param new A handle to the Element that you want to add
+ * @return nothing
+ */
+void
+vot_attachNode (handle_t parent, handle_t new)
+{
+ Element *parent_ptr, *new_ptr;
+ handle_t copy;
+
+ if ((parent == 0) || (new == 0))
+ return;
+
+ /* Make a copy of the Element and it's children. */
+ copy = vot_copyElement (new, 0);
+
+ /* Get pointers. */
+ parent_ptr = vot_getElement (parent);
+ new_ptr = vot_getElement (copy);
+
+ new_ptr->ref_count++;
+
+ /* Make the links, the attached nodes are copies not the original. */
+ if (parent_ptr->child)
+ parent_ptr->last_child->next = new_ptr;
+ else
+ parent_ptr->child = new_ptr;
+
+ parent_ptr->last_child = new_ptr;
+
+ new_ptr->parent = parent_ptr;
+}
+
+
+/**
+ * vot_freeNode -- Destroys the node and all of it's children.
+ *
+ * @brief Destroys the node and all of it's children.
+ * @fn vot_freeNode (handle_t node)
+ *
+ * @param node A handle to the Element that you want deleted
+ * @return nothing
+ */
+void
+vot_freeNode (handle_t node)
+{
+ /* Recursive function to delete the Element and it's children. */
+ Element *node_ptr;
+ handle_t child_handle, sibling_handle;
+
+
+ if (! (node_ptr = vot_getElement (node)) )
+ return;
+
+ if (node_ptr->child) {
+ child_handle = vot_lookupHandle (node_ptr->child);
+ vot_freeNode (child_handle);
+ }
+
+ if (node_ptr->next) {
+ sibling_handle = vot_lookupHandle (node_ptr->next);
+ vot_freeNode (sibling_handle);
+ }
+
+ /* Clean the handle and free the memory.
+ */
+ vot_freeHandle (node);
+ free (node_ptr);
+}
+
+
+/**
+ * vot_deleteNode -- Destroys the node and all of it's children.
+ *
+ * @brief Destroys the node and all of it's children.
+ * @fn vot_deleteNode (handle_t element)
+ *
+ * @param element A handle to the Element that you want deleted
+ * @return nothing
+ */
+void
+vot_deleteNode (handle_t element)
+{
+ /* Delete the node but update the tree. */
+ Element *element_ptr, *parent, *prev;
+
+ element_ptr = vot_getElement (element);
+ parent = element_ptr->parent;
+
+ /* Make sure the node is not still reference. Should never be the case. */
+ if (element_ptr->ref_count > 1) {
+ element_ptr->ref_count--;
+ return;
+ }
+
+ if (parent) {
+ if (parent->child == element_ptr) {
+ parent->child = element_ptr->next;
+ element_ptr->next = NULL;
+ } else {
+ for (prev=parent->child; prev->next != element_ptr; prev=prev->next)
+ ;
+ prev->next = element_ptr->next;
+ element_ptr->next = NULL;
+
+ if (parent->last_child == element_ptr)
+ parent->last_child = prev;
+ }
+ }
+
+ vot_freeNode (element);
+}
+
+
+/**
+ * vot_copyElement -- Adds a node as a child of parent.
+ *
+ * @brief Adds a node as a child of parent.
+ * @fn handle_t vot_copyElement (handle_t src_h, handle_t parent_h)
+ *
+ * @param src_h A handle to the Element to copy
+ * @param parent_h A handle to the Elements parent
+ * @return A handle_t of the copy of the structure
+ */
+handle_t
+vot_copyElement (handle_t src_h, handle_t parent_h)
+{
+ /* A recurseive function to copy a node and it's children. */
+ Element *src_ptr, *return_ptr;
+ handle_t return_handle, parent;
+ handle_t src_child_h, src_next_h;
+
+
+ src_ptr = vot_getElement (src_h);
+
+ if (src_ptr == 0)
+ return (0);
+
+ return_ptr = vot_elementDup (src_h); /* copy the source Element */
+ if (!return_ptr)
+ return (0);
+ return_handle = vot_lookupHandle (return_ptr); /* get the copies handle */
+
+ if (src_ptr->child) { /* process children */
+ parent = return_handle;
+ src_child_h = vot_copyElement (
+ vot_lookupHandle (src_ptr->child), parent);
+
+ /* Actually attach the node. No copy.
+ */
+ vot_attachToNode (return_handle, src_child_h);
+ }
+
+ if (src_ptr->next) { /* process siblings */
+ src_next_h = vot_copyElement (
+ vot_lookupHandle (src_ptr->next), parent);
+
+ if (parent_h != 0)
+ return_ptr->parent = vot_getElement (parent);
+
+ /* Attach the sibling, no copy.
+ */
+ vot_attachSibling (return_handle, src_next_h);
+ }
+
+ return (return_handle);
+}
+
+
+/** **************************************************************************
+ * Utility methods
+ ** *************************************************************************/
+
+/**
+ * vot_getNCols -- Return the nuber of columns in the table structure.
+ *
+ * @brief Return the nuber of columns in the table structure.
+ * @fn int vot_getNCols (handle_t tdata_h)
+ *
+ * @param tdata_h A handle_t to a TABLEDATA
+ * @return The number of cols
+ */
+int
+vot_getNCols (handle_t tdata_h)
+{
+ Element *tdata = vot_getElement (tdata_h);
+
+ if (tdata)
+ return ((atoi(vot_attrGet (tdata->parent->parent->attr, "NCOLS"))));
+
+ return (0);
+}
+
+
+/**
+ * vot_getNRows -- Return the nuber of columns in the table structure.
+ *
+ * @brief Return the nuber of columns in the table structure.
+ * @fn int vot_getNRows (handle_t tdata_h)
+ *
+ * @param tdata_h A handle_t to a TABLEDATA
+ * @return The number of cols
+ */
+int
+vot_getNRows (handle_t tdata_h)
+{
+ Element *tdata = vot_getElement (tdata_h);
+
+ if (tdata)
+ return ( (atoi(vot_attrGet (tdata->parent->parent->attr, "NROWS"))) );
+
+ return (0);
+}
+
+
+/**
+ * vot_getTableCell -- Return the nuber of columns in the structure.
+ *
+ * @brief Return the nuber of columns in the structure.
+ * @fn char *vot_getTableCell (handle_t tdata_h, int row, int col)
+ *
+ * @param tdata_h A handle_t to a TABLEDATA
+ * @param row An int for a row
+ * @param col An int for a col
+ * @return The content of the cell
+ */
+char *
+vot_getTableCell (handle_t tdata_h, int row, int col)
+{
+ Element *tdata;
+ char *s;
+ int cols, rows;
+
+
+ cols = vot_getNCols (tdata_h);
+ rows = vot_getNRows (tdata_h);
+
+ tdata = vot_getElement (tdata_h);
+
+ if ( (row < rows) && (col < cols) ) {
+ if (tdata) {
+ s = tdata->data[(row * cols) + col];
+ return ((s ? s : ""));
+ }
+ }
+
+ return ("");
+}
+
+
+/**
+ * vot_sortTable -- Sort a data table based on the specified column.
+ *
+ * @brief Sort a data table based on the specified column.
+ * @fn int vot_sortTable (handle_t tdata_h, int col, int strsort,
+ * int order))
+ *
+ * @param tdata_h A handle_t to a TABLEDATA
+ * @param col An int for a col
+ * @param strsort String sort?
+ * @param order Sort order (1=ascending, -1=descending);
+ * @return return status
+ */
+
+static int sort_strings = 0;
+static int sort_column = -1;
+static int sort_order = 1;
+
+int
+vot_tableCompare (const void *row1, const void *row2)
+{
+ char **a = (char **)row1,
+ **b = (char **)row2;
+ char *s1, *s2;
+ double x1, x2;
+ int result;
+
+
+ s1 = a[sort_column];
+ s2 = b[sort_column];
+
+ if (!s1 || !s2)
+ return (-1);
+ if (sort_strings)
+ result = (sort_order * strcasecmp (s1, s2));
+ else {
+ x1 = atof (s1);
+ x2 = atof (s2);
+ result = (sort_order * ((x1 < x2) ? -1 : (x1 == x2) ? 0 : 1));
+ }
+ return (result);
+}
+
+
+int
+vot_sortTable (handle_t tdata_h, int col, int strsort, int order)
+{
+ Element *tdata = vot_getElement (tdata_h);
+ int cols = vot_getNCols (tdata_h),
+ rows = vot_getNRows (tdata_h);
+ handle_t tr, td;
+ int i = 0;
+
+
+ if (col < 0)
+ return (ERR);
+
+ sort_column = col;
+ sort_strings = strsort;
+ sort_order = order;
+
+ /* Sort the compiled table of TDATA strings by rows. The comparison
+ * function decides which column is used for the sort.
+ */
+ qsort (&tdata->data[0], rows, (cols * sizeof(char *)), vot_tableCompare);
+
+
+ /* Rewrite the TDATA elements. We've re-ordered the compiled string
+ * table, now we need to reset the <TD> contents accordingly. Do not
+ * free existing pointers first since we've simply moved around the
+ * values.
+ */
+ i = 0;
+ for (tr = vot_getTR (tdata_h); tr; tr = vot_getNext(tr)) {
+ for (td = vot_getTD(tr); td; td = vot_getNext(td)) {
+ Element *e = vot_getElement (td);
+ char *s = tdata->data[i++];
+ e->content = (s ? strdup (s) : NULL);
+ }
+ }
+
+ return (OK);
+}
+
+
+/**
+ * vot_getLength -- Return the number of sibling Elements of the same type.
+ *
+ * @brief Return the number of sibling Elements of the same type.
+ * @fn int vot_getLength (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return The status of the set
+ */
+int
+vot_getLength (handle_t elem_h)
+{
+ Element *elem;
+ int type, total = 0;
+
+
+ if ( (elem = vot_getElement (elem_h)) )
+ type = elem->type;
+ else
+ return (0);
+
+ while (elem) {
+ if (elem->type == type)
+ total++;
+ elem = elem->next;
+ }
+
+ return (total);
+}
+
+
+/**
+ * vot_getNumberOf -- Return the number of sibling Elements of the type.
+ *
+ * @brief Return the number of sibling Elements of the type.
+ * @fn int vot_getNumberOf (handle_t elem_h, int type)
+ *
+ * @param elem_h A handle_t the Element
+ * @param type An int of the type of element you wish to count
+ * @return The status of the set
+ */
+int
+vot_getNumberOf (handle_t elem_h, int type)
+{
+ Element *elem = vot_getElement (elem_h);
+ int total = 0;
+
+
+ if (elem == NULL)
+ return (0);
+
+ while (elem) {
+ if (elem->type == type)
+ total++;
+ elem = elem->next;
+ }
+
+ return (total);
+}
+
+
+/**
+ * vot_colByAttr -- Get the column number (0-indexed) by named attribute.
+ *
+ * @brief Get the column number (0-indexed) by named attribute.
+ * @fn int vot_colByAttr (handle_t tab, char *attr, char *name, char *alt)
+ *
+ * @param tab A handle_t the parent <TABLE> element
+ * @param attr A string holding the attribute name
+ * @param name A string holding the name attribute
+ * @param value A string holding the alternate name attribute string
+ * @return The matching column number or (-1) if not found
+ */
+int
+vot_colByAttr (int tab, char *attr, char *name, char *alt)
+{
+ int n, col = 0, field, ntest = ((alt && alt[0]) ? 2 : 1);
+ char cname[SZ_FNAME], *ctest, *atest;
+
+ for (n=0; n < ntest; n++) {
+ ctest = ((n == 0) ? name : alt);
+ for (col=0,field=vot_getFIELD (tab); field; field=vot_getNext (field)) {
+ memset (cname, 0, SZ_FNAME);
+ if ((atest = vot_getAttr (field, attr)))
+ strcpy (cname, atest);
+ if (cname[0] && strcasecmp (ctest, cname) == 0)
+ return (col);
+ col++;
+ }
+ }
+ return (-1);
+}
+
+
+/**
+ * vot_colByName -- Get the column number (0-indexed) by 'name' attribute.
+ *
+ * @brief Get the column number (0-indexed) by 'name' attribute.
+ * @fn int vot_colByName (handle_t tab, char *name, char *alt)
+ *
+ * @param tab A handle_t the parent <TABLE> element
+ * @param name A string holding the name attribute
+ * @param value A string holding the alternate name attribute string
+ * @return The matching column number or (-1) if not found
+ */
+int
+vot_colByName (int tab, char *name, char *alt)
+{
+ return ( vot_colByAttr (tab, "name", name, alt) );
+}
+
+
+/**
+ * vot_colByUCD -- Get the column number (0-indexed) by 'ucd' attribute.
+ *
+ * @brief Get the column number (0-indexed) by 'ucd' attribute.
+ * @fn int vot_colByUCD (handle_t tab, char *name, char *alt)
+ *
+ * @param tab A handle_t the parent <TABLE> element
+ * @param name A string holding the ucd attribute
+ * @param value A string holding the alternate ucd attribute string
+ * @return The matching column number or (-1) if not found
+ */
+int
+vot_colByUCD (int tab, char *name, char *alt)
+{
+ return ( vot_colByAttr (tab, "ucd", name, alt) );
+}
+
+
+/**
+ * vot_colByID -- Get the column number (0-indexed) by 'id' attribute.
+ *
+ * @brief Get the column number (0-indexed) by 'id' attribute.
+ * @fn int vot_colByID (handle_t tab, char *name, char *alt)
+ *
+ * @param tab A handle_t the parent <TABLE> element
+ * @param name A string holding the id attribute
+ * @param value A string holding the alternate id attribute string
+ * @return The matching column number or (-1) if not found
+ */
+int
+vot_colByID (int tab, char *name, char *alt)
+{
+ return ( vot_colByAttr (tab, "id", name, alt) );
+}
+
+
+/**
+ * vot_findByAttr -- Get a handle to an Element with the requested attribute.
+ *
+ * @brief Get a handle to an Element with the requested attribute.
+ * @fn handle_t vot_findByAttr (handle_t parent, char *name, char *value)
+ *
+ * @param parent A handle_t the parent Element
+ * @param name A string holding the Value type
+ * @param value A string holding the Value value
+ * @return The handle to the element
+ */
+handle_t
+vot_findByAttr (handle_t parent, char *name, char *value)
+{
+ Element *elem, *my_parent;
+ char *elem_value;
+ handle_t return_h = 0;
+
+
+ my_parent = vot_getElement (parent);
+ elem = my_parent->child;
+
+ if ((elem == NULL) || (name == NULL) || (value == NULL))
+ return (0);
+
+ while (elem) {
+ elem_value = vot_attrGet (elem->attr, name);
+
+ if ((elem_value != NULL) && (strcasecmp(elem_value, value) == 0)) {
+ return_h = vot_lookupHandle (elem);
+ break;
+ }
+
+ elem = elem->next;
+ }
+
+ return (return_h);
+}
+
+
+/**
+ * vot_findInGroup -- Return a handle array of the requested Element type.
+ *
+ * @brief Return a handle array of the requested Element type.
+ * @fn handle_t *vot_findInGroup (handle_t group, int type)
+ *
+ * @param group A handle_t the parent Element
+ * @param type Value of the type
+ * @return An array of handles
+ */
+handle_t
+*vot_findInGroup (handle_t group, int type)
+{
+ Element *my_childs, *my_parent;
+ int numberOf = 0;
+ handle_t my_childs_h;
+ handle_t *handles = NULL;
+ int type_parent;
+
+
+ my_parent = vot_getElement (group);
+ my_childs = my_parent->child;
+
+ type_parent = vot_elemType (my_parent);
+
+ if ((type_parent != TY_FIELD) && (type_parent != TY_PARAM))
+ return (NULL);
+
+ if ((my_childs == NULL) || (type >= NUM_ELEMENTS))
+ return (NULL);
+
+ my_childs_h = vot_lookupHandle (my_childs);
+ numberOf = vot_getNumberOf (my_childs_h, type);
+
+ if (numberOf <= 0)
+ return (NULL);
+
+ handles = (handle_t *) calloc (numberOf, sizeof (handle_t));
+
+ /* FIXME
+ *
+ * The way this should work in the API is to return a new 'handle'
+ * where a vot_getNext() behaves as with all other handles to return
+ * a next member of the group. Where we might have a problem is in
+ * free-ing the handles in that this new group handle is left dangling.
+ *
+ * If the concept is that the vot_struct is a root element for the
+ * handle table in each votable then we avoid the above problem by
+ * clearing all handles for a 'vot' which would include any newly
+ * allocated group handles. The (better) alternative is to allocate a
+ * handle during the parsing and here we just return the group handle.
+ * Otherwise we might be left dealing with terminating handles in the
+ * vot_struct idea, and would complicate adding nodes to a 'vot' when
+ * editing. (MJF, 8/4/09)
+ */
+ while (my_childs) {
+ if (vot_elemType (my_childs) == type) {
+ numberOf--;
+ handles[numberOf] = vot_lookupHandle (my_childs);
+ }
+
+ my_childs = my_childs->next;
+ }
+
+ return (handles);
+}
+
+
+/**
+ * vot_getNext -- Return a handle_t of the next Element of the same type.
+ *
+ * @brief Return a handle_t of the next Element of the same type.
+ * @fn handle_t vot_getNext (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return A handle of the next Element of the same type
+ */
+handle_t
+vot_getNext (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+ int type;
+
+ type = vot_elemType (elem);
+ for (elem=elem->next; elem; elem = elem->next) {
+ if (vot_elemType (elem) == type)
+ break;
+ }
+
+ return (vot_lookupHandle (elem));
+}
+
+
+/**
+ * vot_getSibling -- Return a handle_t of the next signling Element.
+ *
+ * @brief Return a handle_t of the next Element.
+ * @fn handle_t vot_getSibling (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return A handle of the next Element
+ */
+handle_t
+vot_getSibling (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+
+ return (vot_lookupHandle (elem->next));
+}
+
+
+/**
+ * vot_getChild -- Return a handle_t of the child Element.
+ *
+ * @brief Return a handle_t of the child Element.
+ * @fn handle_t vot_getChild (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return A handle of the child Element
+ */
+handle_t
+vot_getChild (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+
+ return (vot_lookupHandle (elem->child));
+}
+
+
+/**
+ * vot_getParent -- Return the handle of the parent Element.
+ *
+ * @brief Return the handle of the parent Element.
+ * @fn handle_t vot_getParent (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return A handle of the paretn Element
+ */
+handle_t
+vot_getParent (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+
+ return (vot_lookupHandle (elem->parent));
+}
+
+
+/**
+ * vot_getChildOfType -- Get the handle of the next Element of the same type.
+ *
+ * @brief Get the handle of the next Element of the same type.
+ * @fn handle_t vot_getChildOfType (handle_t elem_h, int type)
+ *
+ * @param elem_h A handle_t the Element
+ * @param type An integer of the Element type for find
+ * @return A handle of the Element
+ */
+handle_t
+vot_getChildOfType (handle_t elem_h, int type)
+{
+ Element *elem;
+
+ elem = vot_getElement (elem_h);
+ type = vot_elemType (elem);
+
+ for (elem = elem->child; elem; elem = elem->next) {
+ if (vot_elemType (elem) == type)
+ break;
+ }
+
+ return (vot_lookupHandle (elem));
+}
+
+
+/**
+ * vot_valueOf -- Return type of the Element.
+ *
+ * @brief Return type of the Element.
+ * @fn int vot_valueOf (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return An integer of the type
+ */
+int
+vot_valueOf (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+ return (vot_elemType (elem)); /* ???? FIXME */
+}
+
+
+/**
+ * vot_typeOf -- Return type of the Element.
+ *
+ * @brief Return type of the Element.
+ * @fn int vot_typeOf (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the Element
+ * @return An integer of the type
+ */
+int
+vot_typeOf (handle_t elem_h)
+{
+ return ( vot_elemType (vot_getElement (elem_h)) );
+}
+
+
+/****************************************************************************
+ *
+ ***************************************************************************/
+
+
+/**
+ * vot_setValue -- Set the Value for the ELEMENT.
+ *
+ * @brief Set the Value for the ELEMENT.
+ * @fn int vot_setValue (handle_t elem_h, char *value)
+ *
+ * @param elem_h A handle_t the ELEMENT
+ * @param value A string holding the value
+ * @return The status of the set
+ */
+int
+vot_setValue (handle_t elem_h, char *value)
+{
+ Element *cur = vot_getElement (elem_h);
+ int len = strlen (value) + 1;
+
+
+ if (value) {
+ if(cur->content != NULL)
+ free (cur->content);
+
+ cur->content = (char *) calloc (len, sizeof (char));
+
+ if (cur->content == NULL) {
+ fprintf (stderr, "ERROR: CALLOC failed for vot_setValue.\n");
+ return (0);
+ }
+
+ strncat (cur->content, value, len);
+ return (1);
+
+ } else
+ return (0);
+}
+
+
+/**
+ * vot_getValue -- Get the Value for the ELEMENT.
+ *
+ * @brief Get the Value for the ELEMENT.
+ * @fn char *vot_getValue (handle_t elem_h)
+ *
+ * @param elem_h A handle_t the ELEMENT
+ * @return A string of the value or the Value
+ */
+char *
+vot_getValue (handle_t elem_h)
+{
+ Element *elem = vot_getElement (elem_h);
+
+#ifdef USE_NULL_VALUE
+ return ((elem ? elem->content : NULL));
+#else
+ return ((elem ? elem->content : ""));
+#endif
+}
+
+
+/**
+ * vot_setAttr -- Set the attribute for the Element.
+ *
+ * @brief Set the attribute for the Element.
+ * @fn int vot_setAttr (handle_t elem_h, char *attr, char *value)
+ *
+ * @param elem_h A handle_t the Element
+ * @param attr A string holding the attribute name
+ * @param value A string holding the attribute value
+ * @return The status of the set
+ */
+int
+vot_setAttr (handle_t elem_h, char *attr, char *value)
+{
+ Element *elem = vot_getElement (elem_h);
+
+ return (vot_attrSet (elem->attr, attr, value));
+}
+
+
+/**
+ * vot_getAttr -- Return the attribute for the Element.
+ *
+ * @brief Return the attribute for the Element.
+ * @fn char * vot_getAttr (handle_t elem_h, char *attr)
+ *
+ * @param elem_h A handle_t the Element
+ * @param attr A string holding the attribute name
+ * @return A string of the value or the attr
+ */
+char *
+vot_getAttr (handle_t elem_h, char *attr)
+{
+ Element *elem = vot_getElement (elem_h);
+
+ return (vot_attrGet (elem->attr, attr));
+}
+
+
+/**
+ * vot_writeVOTable -- Write the VOTable to the file descriptor.
+ *
+ * @brief Write the VOTable to the file descriptor.
+ * @fn vot_writeVOTable (handle_t node, char *fname, int indent)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param indent Number of spaces to indent at each level
+ * @return nothing
+ */
+void
+vot_writeVOTable (handle_t node, char *fname, int indent)
+{
+ FILE *fd = (FILE *) NULL;
+
+ if (strcasecmp (fname, "stdout") == 0 || strncmp (fname, "-", 1) == 0)
+ fd = stdout;
+ else {
+ if ((fd = fopen (fname, "w+")) == (FILE *) NULL) {
+ fprintf (stderr, "Cannot open XML file '%s'\n", fname);
+ return;
+ }
+ }
+
+
+ fprintf (fd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s",
+ (indent ? "\n" : ""));
+ vot_dumpXML (vot_getElement (node), 0, indent, fd);
+ fprintf (fd, "\n");
+
+ fflush (fd);
+ if (fd != stdout)
+ fclose (fd);
+}
+
+
+/**
+ * vot_writeHTML -- Write the VOTable to the file descriptor as HTML.
+ *
+ * @brief Write the VOTable to the file descriptor as HTML.
+ * @fn vot_writeHTML (handle_t node, char *ifname, char *ofname)
+ *
+ * @param node A handle to an Element that you to print
+ * @param ifname Input filename
+ * @param ofname Output filename (or "stdout" or "-" for STDOUT)
+ * @return nothing
+ */
+void
+vot_writeHTML (handle_t node, char *ifname, char *ofname)
+{
+ FILE *fd = (FILE *) NULL;
+ handle_t res = vot_getRESOURCE (node);
+ handle_t sub_res = 0, top_res = 0;
+
+
+ if (strcasecmp (ofname, "stdout") == 0 || strncmp (ofname, "-", 1) == 0)
+ fd = stdout;
+ else {
+ if ((fd = fopen (ofname, "w+")) == (FILE *) NULL) {
+ fprintf (stderr, "Cannot open HTML file '%s'\n", ofname);
+ return;
+ }
+ }
+
+ /* Write the header.
+ */
+ vot_htmlHeader (fd, ifname);
+
+ /* Loop over all the <RESOURCE> elements.
+ */
+ while (res) {
+
+ /* Make sure we have a <TABLE> element in the <RESOURCE>. If not,
+ * look for a child resource to process. Only do one level of
+ * nesting. -- FIXME --
+ */
+ if (!sub_res && vot_getTABLE (res) <= 0) {
+ if ((sub_res = vot_getChildOfType (res, TY_RESOURCE))) {
+ top_res = res;
+ res = sub_res;
+ }
+ }
+
+ vot_htmlTableMeta (fd, res, ifname);
+ vot_htmlTableData (fd, res, NULL);
+
+ res = vot_getNext (res);
+ if (res)
+ fprintf (fd, "<hr noshade='3'>\n");
+ else if (sub_res) {
+ /* End of child resources, go back up.
+ */
+ sub_res = 0;
+ res = vot_getNext (top_res);
+ }
+ }
+
+ /* Write the footer and close the file.
+ */
+ vot_htmlFooter (fd);
+ fflush (fd);
+ if (fd != stdout)
+ fclose (fd);
+}
+
+
+/**
+ * vot_writeSHTML -- Write the VOTable to the file descriptor as an HTML table.
+ *
+ * @brief Write the VOTable to the file descriptor as an HTML table.
+ * @fn vot_writeSHTML (handle_t node, char *ifname, char *ofname)
+ *
+ * @param node A handle to an Element that you to print
+ * @param ifname Input filename
+ * @param ofname Output filename (or "stdout" or "-" for STDOUT)
+ * @return nothing
+ */
+void
+vot_writeSHTML (handle_t node, char *ifname, char *ofname)
+{
+ FILE *fd = (FILE *) NULL;
+ handle_t res = vot_getRESOURCE (node);
+
+
+ if (!res) /* error */
+ return;
+
+ if (strcasecmp (ofname, "stdout") == 0 || strncmp (ofname, "-", 1) == 0)
+ fd = stdout;
+ else {
+ if ((fd = fopen (ofname, "w+")) == (FILE *) NULL) {
+ fprintf (stderr, "Cannot open standalone HTML file '%s'\n", ofname);
+ return;
+ }
+ }
+
+ /* Write just the <TABLEDATA> portion of the file.
+ */
+ vot_htmlTableData (fd, res, ifname);
+
+ fflush (fd);
+ if (fd != stdout)
+ fclose (fd);
+}
+
+
+/**
+ * VOT_SIMPLEGETURL -- Utility routine to do a simple URL download to the file.
+ */
+static int
+vot_simpleGetURL (char *url, char *ofname)
+{
+ int stat = 0;
+ char lockfile[SZ_FNAME], errBuf[CURL_ERROR_SIZE], fname[SZ_FNAME];
+ FILE *fd;
+ CURL *curl_handle;
+
+
+
+ /* For the CURL operation to download the file.
+ */
+ curl_global_init (CURL_GLOBAL_ALL); /* init curl session */
+ curl_handle = curl_easy_init ();
+
+ if ((fd = fopen (ofname, "wb")) == NULL) { /* open the output file */
+ fprintf (stderr, "Error: cannot open output file '%s'\n", ofname);
+ curl_easy_cleanup (curl_handle);
+ return (1);
+ }
+
+ /* Set cURL options
+ */
+ curl_easy_setopt (curl_handle, CURLOPT_URL, url);
+ curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1L);
+ curl_easy_setopt (curl_handle, CURLOPT_WRITEDATA, fd);
+ curl_easy_setopt (curl_handle, CURLOPT_ERRORBUFFER, errBuf);
+ curl_easy_setopt (curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+
+ /* Do the download.
+ */
+ if ((stat = curl_easy_perform (curl_handle)) != 0) {
+ /* Error in download, clean up.
+ */
+ unlink (fname);
+ fclose (fd); /* close the file */
+ curl_easy_cleanup (curl_handle); /* cleanup curl stuff */
+ return (1);
+ }
+
+ fflush (fd);
+ fclose (fd); /* close the file */
+ curl_easy_cleanup (curl_handle); /* cleanup curl stuff */
+
+
+ /* Remove the lock file to indicate we are done.
+ */
+ unlink (lockfile);
+
+ return (0);
+}
+
+
+/**
+ * vot_writeFITS -- Write the VOTable to the file descriptor as a FITS table.
+ *
+ * @brief Write the VOTable to the file descriptor as an FITS table.
+ * @fn vot_writeFITS (handle_t node, char *fname)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @return nothing
+ */
+
+#ifdef HAVE_CFITSIO
+
+/* Use native code and CFITSIO to write the file.
+ */
+
+#define MAX_FIELDS 256
+
+static int vot_addFITSMeta (int handle, fitsfile *fp, char *meta, int index);
+static int vot_addFieldMeta (int handle, fitsfile *fp, int index);
+static int vot_writeFITSData (fitsfile *fp, char **data, char *fmt[],
+ int nrows, int ncols);
+static void vot_printerror (int status);
+
+
+void
+vot_writeFITS (handle_t vot, char *oname)
+{
+ char *name, *unit, *dtype, *width, **cells, *cell, *asize, *tname;
+ char *ttype[MAX_FIELDS], *tform[MAX_FIELDS], *tunit[MAX_FIELDS], *ch;
+ char extname[SZ_LINE], col[SZ_FNAME];
+ int res, tab, data, tdata, field, handle, hdutype, *widths, *spaces;
+ int i, j, len, ncols, nrows, status = 0, resnum = 1, bitpix = 8;
+ long naxis = 0, naxes[2] = { 0, 0 };
+ fitsfile *fp; /* CFITSIO descriptor */
+
+
+ if (access (oname, F_OK) == 0) /* remove an existing file */
+ unlink (oname);
+
+
+ if (fits_create_file (&fp, oname, &status)) /* create new FITS file */
+ vot_printerror (status);
+
+ if ( fits_create_img (fp, bitpix, naxis, naxes, &status) )
+ vot_printerror (status);
+
+
+ /* Loop over all <RESOURCE> elements in the file, creating an new
+ * extension for each one.
+ */
+ for (res=vot_getRESOURCE (vot); res; res=vot_getNext(res) ) {
+
+ /* Get handles for the current resource.
+ */
+ if ((tab = vot_getTABLE (res)) <= 0)
+ continue;
+ if ((data = vot_getDATA (tab)) <= 0)
+ continue;
+ if ((tdata = vot_getTABLEDATA (data)) <= 0)
+ return;
+ nrows = vot_getNRows (tdata);
+ ncols = vot_getNCols (tdata);
+
+
+ /* Allocate space for the data cells. Read in the cells so we can
+ * convert it for output. Also collect the widths so we can
+ * properly size the table.
+ */
+ cells = (char **) calloc (1, (nrows * ncols) * sizeof (char *));
+ widths = (int *) calloc (1, ncols * sizeof (int));
+ spaces = (int *) calloc (1, ncols * sizeof (int));
+ for (i = 0; i < nrows; i++) {
+ for (j = 0; j < ncols; j++) {
+ cell = cells[i*ncols+j] = vot_getTableCell(tdata, i, j);
+
+ if ((len = strlen (cell)) > widths[j])
+ widths[j] = len;
+
+ if (cell[0] && strchr (cell, (int)' ') && len > 1 && i < 1) {
+ /* trim whitespace */
+ for (ch=&cell[len-1]; isspace(*ch) && ch > cell; ch--)
+ *ch = '\0';
+ for (ch=cell; isspace(*ch); )
+ ch++;
+ for ( ; *ch; ch++) {
+ if (*ch == ' ')
+ spaces[j]++;
+ }
+ }
+ }
+ }
+
+ memset (&ttype[0], 0, (MAX_FIELDS * sizeof (char *)));
+ memset (&tform[0], 0, (MAX_FIELDS * sizeof (char *)));
+ memset (&tunit[0], 0, (MAX_FIELDS * sizeof (char *)));
+
+ /* Move to proper extension HDU.
+ */
+ if (fits_movabs_hdu (fp, resnum++, &hdutype, &status))
+ vot_printerror (status);
+
+ /* Get the column attributes and set them in the header.
+ */
+ i = 0;
+ for (field=vot_getFIELD(tab); field; field=vot_getNext (field)) {
+ dtype = vot_getAttr (field, "datatype");
+ width = vot_getAttr (field, "width");
+ asize = vot_getAttr (field, "arraysize");
+
+ memset (col, 0, SZ_FNAME); /* dummy column name */
+ sprintf (col, "col%d", i + 1); /* one-indexed */
+
+ /* Required columns.
+ */
+ ttype[i] = ((name=vot_getAttr(field,"name")) ? name : strdup(col));
+ tunit[i] = ((unit=vot_getAttr(field, "unit")) ? unit : strdup(""));
+
+ tform[i] = calloc (1, 16);
+
+ if (strncasecmp (dtype, "char", 4) == 0 ||
+ strncasecmp (dtype, "bool", 4) == 0 ||
+ strncasecmp (dtype, "unsignedByte", 12) == 0) {
+
+ if (asize && asize[0] && widths[i]) {
+ sprintf (tform[i], "%dA",
+ (asize[0] == '*' ? widths[i] : atoi (asize)));
+ } else
+ strcpy (tform[i], "A");
+
+ } else if (strncasecmp (dtype, "float", 4) == 0) {
+ if (spaces[i])
+ sprintf (tform[i], "%dE", spaces[i]+1);
+ else
+ strcpy (tform[i], "E");
+
+ } else if (strncasecmp (dtype, "double", 4) == 0) {
+ if (spaces[i])
+ sprintf (tform[i], "%dD", spaces[i]+1);
+ else
+ strcpy (tform[i], "D");
+
+ } else if (strncasecmp (dtype, "short", 5) == 0 ||
+ strncasecmp (dtype, "unicodeChar", 11) == 0) {
+ if (spaces[i])
+ sprintf (tform[i], "%dI", spaces[i]+1);
+ else
+ strcpy (tform[i], "I");
+
+ } else if (strncasecmp (dtype, "int", 3) == 0) {
+ if (spaces[i])
+ sprintf (tform[i], "%dJ", spaces[i]+1);
+ else
+ strcpy (tform[i], "J");
+
+ } else if (strncasecmp (dtype, "long", 4) == 0) {
+ if (spaces[i])
+ sprintf (tform[i], "%dJ", spaces[i]+1);
+ else
+ strcpy (tform[i], "J");
+ }
+
+ if (dtype) free ( (void *) dtype);
+ if (width) free ( (void *) width);
+ if (asize) free ( (void *) asize);
+ i++;
+ }
+
+ /* Append a new empty binary table onto the FITS file
+ */
+
+ /* See if the table has a name attribute, if so, use it as the
+ * extension name.
+ */
+ memset (extname, 0, SZ_LINE);
+ tname = vot_getAttr (tab, "name");
+ if (!tname || strchr (tname, (int)'?') || strchr (tname, (int)'&'))
+ sprintf (extname, "ext%d", resnum);
+ else {
+ strcpy (extname, tname);
+ free ((void *) tname);
+ }
+
+ if (fits_create_tbl (fp, BINARY_TBL, nrows, ncols, ttype, tform,
+ tunit, extname, &status))
+ vot_printerror (status);
+
+
+ /* Add UCD and UTYPE keywords for the FIELDs if defined.
+ */
+ for (i=1,field=vot_getFIELD(tab); field; field=vot_getNext (field))
+ vot_addFieldMeta (field, fp, i++);
+
+ /* Add keywords for all the <INFO> and <PARAM> elements.
+ */
+ if ((handle = vot_getINFO (res))) {
+ for (i=1, len=vot_getLength (handle); i <= len; i++) {
+ vot_addFITSMeta (handle, fp, "INFO", i);
+ handle = vot_getNext (handle);
+ }
+ }
+
+ if ((handle = vot_getPARAM (res))) {
+ for (i=1, len=vot_getLength (handle); i <= len; i++) {
+ vot_addFITSMeta (handle, fp, "PARAM", i);
+ handle = vot_getNext (handle);
+ }
+ }
+
+
+ /* Write the data to the file.
+ */
+ if (nrows > 0)
+ vot_writeFITSData (fp, cells, tform, nrows, ncols);
+
+ /* Free the allocated pointers.
+ */
+ for (i=0; i < ncols; i++) {
+ if (ttype[i]) free ((void *) ttype[i]);
+ if (tunit[i]) free ((void *) tunit[i]);
+ if (tform[i]) free ((void *) tform[i]);
+ }
+ if (cells) free ((void *) cells);
+ if (widths) free ((void *) widths);
+ if (spaces) free ((void *) spaces);
+ }
+
+
+ vot_closeVOTABLE (vot); /* close the VOTable */
+ if (fits_close_file (fp, &status)) /* close the FITS file */
+ vot_printerror (status);
+}
+
+
+static int
+vot_addFITSMeta (int handle, fitsfile *fp, char *meta, int index)
+{
+ char *id, *nam, *val, *unit, keyw[SZ_FNAME], comment[SZ_FNAME];
+ int status = 0;
+
+
+ if ( (nam = vot_getAttr (handle, "name")) ) { /* NAME attribute */
+ memset (keyw, 0, SZ_FNAME);
+ memset (comment, 0, SZ_FNAME);
+ sprintf (keyw, "%3.3sNAM%d", meta, index);
+ sprintf (comment, "%s name attribute", meta);
+
+ if (fits_update_key (fp, TSTRING, keyw, nam, comment, &status))
+ vot_printerror ( status );
+ }
+
+ if ( (val = vot_getAttr (handle, "value")) ) { /* VALUE attribute */
+ memset (keyw, 0, SZ_FNAME);
+ memset (comment, 0, SZ_FNAME);
+ sprintf (keyw, "%3.3sVAL%d", meta, index);
+ sprintf (comment, "%s val attribute", meta);
+
+ if (fits_update_key (fp, TSTRING, keyw, val, comment, &status))
+ vot_printerror ( status );
+ }
+
+ if ( (id = vot_getAttr (handle, "id")) ) { /* ID attribute */
+ memset (keyw, 0, SZ_FNAME);
+ memset (comment, 0, SZ_FNAME);
+ sprintf (keyw, "%3.3sID%d", meta, index);
+ sprintf (comment, "%s id attribute", meta);
+
+ if (fits_update_key (fp, TSTRING, keyw, id, comment, &status))
+ vot_printerror ( status );
+ }
+
+ if ( (unit = vot_getAttr (handle, "unit")) ) { /* UNIT attribute */
+ memset (keyw, 0, SZ_FNAME);
+ memset (comment, 0, SZ_FNAME);
+ sprintf (keyw, "%3.3sUNI%d", meta, index);
+ sprintf (comment, "%s unit attribute", meta);
+
+ if (fits_update_key (fp, TSTRING, keyw, unit, comment, &status))
+ vot_printerror ( status );
+ }
+
+ return (0);
+}
+
+
+static int
+vot_addFieldMeta (int handle, fitsfile *fp, int index)
+{
+ char *ucd, *utype, *id, keyw[SZ_FNAME];
+ int status = 0;
+
+
+ if ( (id = vot_getAttr (handle, "id")) ) { /* ID attribute */
+ memset (keyw, 0, SZ_FNAME);
+ sprintf (keyw, "TID%d", index);
+
+ if (fits_update_key (fp, TSTRING, keyw, id, "ID attribute", &status))
+ vot_printerror ( status );
+ }
+
+ if ( (ucd = vot_getAttr (handle, "ucd")) ) { /* UCD attribute */
+ memset (keyw, 0, SZ_FNAME);
+ sprintf (keyw, "TUCD%d", index);
+
+ if (fits_update_key (fp, TSTRING, keyw, ucd, "UCD attribute", &status))
+ vot_printerror ( status );
+ }
+
+ if ( (utype = vot_getAttr (handle, "utype")) ) { /* UTYPE attribute */
+ memset (keyw, 0, SZ_FNAME);
+ sprintf (keyw, "TUTYPE%d", index);
+
+ if (fits_update_key (fp, TSTRING, keyw, utype, "UTYPE attribute",
+ &status))
+ vot_printerror ( status );
+ }
+
+ return (0);
+}
+
+
+static int
+vot_writeFITSData (fitsfile *fp, char **data, char *fmt[], int nrows, int ncols)
+{
+ int i, j, n, type, width, status = 0;
+ char **ccol, *d, *tform, cell[1024], *tok, *sep = " ", *brkt = NULL;
+ float *fcol;
+ double *dcol;
+ long *icol;
+ short *scol;
+ long frow = 1, felem = 1, nr = nrows;
+
+
+ for (j = 0; j < ncols; j++) {
+
+ tform = fmt[j];
+ width = atoi (tform);
+ type = strlen (tform) - 1;
+
+ switch (tform[type]) {
+ case 'A': /* CHAR */
+// ccol = (char **) calloc (1, (nrows * (width+1) * sizeof (char *)));
+ ccol = (char **) calloc (1, (nrows * (SZ_LINE) * sizeof (char *)));
+
+ for (i = 0; i < nrows; i++) {
+ d = data[i * ncols + j];
+ ccol[i] = ((d && *d) ? d : (char *) "");
+ }
+
+ fits_write_col (fp, TSTRING, j+1, frow,felem, nr, ccol, &status);
+ free ((void *) ccol);
+ break;
+
+ case 'D': /* DOUBLE */
+ dcol = (double *) calloc (1, (nrows * (width+1) * sizeof (double)));
+
+ if (width == 0) {
+ for (i = 0; i < nrows; i++) {
+ if (strncasecmp (data[i*ncols+j], "NaN", 3) == 0)
+ ((double *) dcol)[i] = (double) atof("");
+ else
+ ((double *) dcol)[i] = (double) atof(data[i*ncols + j]);
+ }
+ fits_write_col (fp, TDOUBLE, j+1, frow,felem, nr, dcol,
+ &status);
+ } else {
+ double *dp = dcol, *dpr = dp;
+
+ for (i = 0; i < nrows; i++) {
+ brkt = NULL;
+ memset (cell, 0, 1024);
+ strcpy (cell, data[i * ncols + j]);
+
+ dpr = dp;
+ for (n=1, tok=strtok_r (cell, sep, &brkt); tok;
+ tok=strtok_r (NULL, sep, &brkt), n++)
+ *dp++ = (double) atof (tok);
+ for ( ; n < width; n++) /* missing values */
+ *dp++ = (double) 0.0;
+
+ fits_write_col (fp, TDOUBLE, j+1, i+1,felem, width, dpr,
+ &status);
+ }
+ }
+
+ free ((void *) dcol);
+ break;
+
+ case 'E': /* FLOAT */
+ fcol = (float *) calloc (1, (nrows * (width+1) * sizeof (float)));
+
+ if (width == 0) {
+ for (i = 0; i < nrows; i++)
+ ((float *) fcol)[i] = (float) atof (data[i * ncols + j]);
+ fits_write_col (fp, TFLOAT, j+1, frow,felem, nr, fcol, &status);
+ } else {
+ float *rp = fcol, *rpr = rp;
+
+ for (i = 0; i < nrows; i++) {
+ brkt = NULL;
+ memset (cell, 0, 1024);
+ strcpy (cell, data[i * ncols + j]);
+
+ rpr = rp;
+ for (n=1, tok=strtok_r (cell, sep, &brkt); tok;
+ tok=strtok_r (NULL, sep, &brkt), n++)
+ *rp++ = (float) atof (tok);
+ for ( ; n < width; n++) /* missing values */
+ *rp++ = (float) 0.0;
+
+ fits_write_col (fp, TFLOAT, j+1, i+1,felem, width, rpr,
+ &status);
+ }
+ }
+
+ free ((void *) fcol);
+ break;
+
+ case 'S': /* SHORT */
+ case 'I': /* SHORT */
+ scol = (short *) calloc (1, (nrows * (width+1) * sizeof (short)));
+
+ if (width == 0) {
+ for (i = 0; i < nrows; i++)
+ ((short *) scol)[i] = (short) atoi (data[i * ncols + j]);
+ fits_write_col (fp, TSHORT, j+1, frow,felem, nr, scol, &status);
+ } else {
+ short *sp = scol, *spr = sp;
+
+ for (i = 0; i < nrows; i++) {
+ brkt = NULL;
+ memset (cell, 0, 1024);
+ strcpy (cell, data[i * ncols + j]);
+
+ spr = sp;
+ for (n=1, tok=strtok_r (cell, sep, &brkt); tok;
+ tok=strtok_r (NULL, sep, &brkt), n++)
+ *sp++ = (short) atoi (tok);
+ for ( ; n < width; n++) /* missing values */
+ *sp++ = (short) 0;
+
+ fits_write_col (fp, TSHORT, j+1, i+1,felem, width, spr,
+ &status);
+ }
+ }
+
+ free ((void *) scol);
+ break;
+
+ case 'J': /* INT */
+ icol = (long *) calloc (1, (nrows * (width+1) * sizeof (long)));
+
+ if (width == 0) {
+ for (i = 0; i < nrows; i++)
+ ((long *) icol)[i] = (long) atoi (data[i * ncols + j]);
+ fits_write_col (fp, TLONG, j+1, frow,felem, nr, icol, &status);
+ } else {
+ long *ip = icol, *ipr = ip;
+
+ for (i = 0; i < nrows; i++) {
+ brkt = NULL;
+ memset (cell, 0, 1024);
+ strcpy (cell, data[i * ncols + j]);
+
+ ipr = ip;
+ for (n=1, tok=strtok_r (cell, sep, &brkt); tok;
+ tok=strtok_r (NULL, sep, &brkt), n++)
+ *ip++ = (long) atoi (tok);
+ for ( ; n < width; n++) /* missing values */
+ *ip++ = (long) 0;
+
+ fits_write_col (fp, TLONG, j+1, i+1,felem, width, ipr,
+ &status);
+ }
+ }
+
+ free ((void *) icol);
+ break;
+
+ default:
+ fprintf (stderr, "Invalid column type '%c'\n", tform[type]);
+ continue;
+ }
+ }
+
+ return (0);
+}
+
+
+static void
+vot_printerror (int status)
+{
+ if (status) {
+ fits_report_error (stderr, status); /* print error report */
+ exit (status);
+ }
+ return;
+}
+
+
+
+#else
+
+#ifdef USE_STILTS
+
+/* Use an extern STILTS command to do the conversion.
+ */
+void
+vot_writeFITS (handle_t node, char *fname)
+{
+ char tmp[SZ_FNAME], cmd[SZ_FNAME];
+
+ memset (cmd, 0, SZ_FNAME);
+ memset (tmp, 0, SZ_FNAME);
+
+ sprintf (tmp, "/tmp/vo%d", (int)getpid());
+
+ vot_writeVOTable (node, tmp, 0);
+
+ /* FIXME */
+ sprintf (cmd,
+ "/usr/local/bin/stilts -stderr /dev/null tcopy %s %s ofmt=fits-basic",
+ tmp, fname);
+
+ system (cmd);
+
+ unlink (tmp);
+}
+
+#else
+
+/* No-Op version of method.
+ */
+void
+vot_writeFITS (handle_t node, char *fname)
+{
+}
+
+#endif
+#endif
+
+
+
+/***************************************
+ * START of HTML Utility procedures.
+ ***************************************/
+
+
+/**
+ * VOT_HTMLHEADER -- Printer the HTML header.
+ */
+static void
+vot_htmlHeader (FILE *fd, char *fname)
+{
+ fprintf (fd,
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
+ fprintf (fd, "<html><head><title>File: %s</title></head><body>\n", fname);
+ fflush (fd);
+}
+
+
+/**
+ * VOT_HTMLTABLEMETA -- Print the VOTable metadata for the RESOURCE.
+ */
+static void
+vot_htmlTableMeta (FILE *fd, handle_t res, char *ifname)
+{
+ handle_t desc = vot_getDESCRIPTION (res);
+ handle_t info = vot_getINFO (res);
+ handle_t param = vot_getPARAM (res);
+
+ char *val = NULL;
+
+
+ /*
+ RESOURCE ID=<id> type=<type> name=<name>
+
+ Description:
+
+ INFO <name> <value>
+ PARAM <name> <value>
+ : : :
+ */
+
+ fprintf (fd, "<table border='0' width='90%%'><tbody><tr>");
+
+ /* Print general votable metadata.
+ */
+ fprintf (fd, "<td><b>FILE NAME</b>:</td><td colspan='3'>%s</td></tr>",
+ ifname);
+ fprintf (fd, "<tr><td><b>RESOURCE</b>:</td><td colspan='3'>");
+ if ((val = vot_getAttr (res, "ID"))) fprintf (fd, " ID='%s'", val);
+ if ((val = vot_getAttr (res, "name"))) fprintf (fd, " name='%s'", val);
+ if ((val = vot_getAttr (res, "type"))) fprintf (fd, " type='%s'", val);
+ fprintf (fd, "</td></tr>\n");
+
+ fprintf (fd, "<tr><td valign='top'><b>DESCRIPTION:</b></td>");
+ fprintf (fd, "<td colspan='3'>%s</td></tr>",
+ (desc ? vot_getValue (desc) : ""));
+
+ fprintf (fd, "<tr><td>&nbsp;</td><td colspan='3'/></tr>\n");
+
+ /* Print the RESOURCE-specific <INFO> elements.
+ */
+ while (info) {
+ fprintf (fd, "<tr><td><b>INFO</b></td><td>%s</td><td>%s</td>",
+ vot_getAttr (info, "name"), vot_getAttr (info, "value"));
+ if ((desc = vot_getDESCRIPTION(info)))
+ fprintf (fd, "<td>%s</td>", vot_getValue (desc));
+ else
+ fprintf (fd, "<td/>");
+ fprintf (fd, "</tr>");
+
+ info = vot_getNext (info);
+ }
+
+ /* Print the RESOURCE-specific <PARAM> elements.
+ */
+ while (param) {
+ fprintf (fd, "<tr><td><b>PARAM</b></td><td>%s</td><td>%s</td>",
+ vot_getAttr (param, "name"), vot_getAttr (param, "value"));
+ if ((desc = vot_getDESCRIPTION (param)))
+ fprintf (fd, "<td>%s</td>", vot_getValue (desc));
+ else
+ fprintf (fd, "<td/>");
+ fprintf (fd, "</tr>");
+
+ param = vot_getNext (param);
+ }
+ fprintf (fd, "<tr><td>&nbsp;</td><td colspan='3'/></tr>\n");
+
+ fprintf (fd, "</tr></tbody></table>\n");
+ fflush (fd);
+}
+
+
+/**
+ * VOT_HTMLTABLEDATA -- Print the VOTable TABLEDATA for the RESOURCE.
+ */
+static void
+vot_htmlTableData (FILE *fd, handle_t res, char *ifname)
+{
+ handle_t tab, data, tdata, field, tr, td;
+ register int i, nrows, ncols;
+ char *name, *id, *ucd, *s;
+
+
+ /* Display options. For the moment, these are hardcoded values, later
+ * we'll make these settable from the application.
+ */
+ int tborder = 1;
+ char *hcolor = "eec"; /* header color */
+ char *ecolor = "ccc"; /* even-row color */
+ char *ocolor = "eee"; /* odd-row color */
+
+
+ if ((tab = vot_getTABLE (res)) <= 0) /* get handles */
+ return;
+ if ((data = vot_getDATA (tab)) <= 0)
+ return;
+ if ((tdata = vot_getTABLEDATA (data)) <= 0)
+ return;
+ nrows = vot_getNRows (tdata);
+ ncols = vot_getNCols (tdata);
+
+
+ fprintf (fd, "<table border='%d'>\n", tborder);
+ if (ifname)
+ fprintf (fd, "<tcaption>File: %s</tcaption>\n", ifname);
+
+ /* Print the Column header names, find or make a reasonable value.
+ */
+ fprintf (fd, "<thead><tr style=\"background:#%s\">\n", hcolor);
+ for (field=vot_getFIELD (tab),i=0; field; field = vot_getNext (field),i++) {
+ name = vot_getAttr (field, "name");
+ id = vot_getAttr (field, "id");
+ ucd = vot_getAttr (field, "ucd");
+
+ if (name || id || ucd)
+ fprintf (fd, "<th>%s</th>", (name ? name : (id ? id : ucd)) );
+ else
+ fprintf (fd, "<th>col%d</th>", i);
+ }
+ fprintf (fd, "</tr></thead>\n");
+
+
+ /* Now dump the data.
+ */
+ fprintf (fd, "<tbody>\n");
+ for (tr=vot_getTR (tdata),i=0; tr; tr=vot_getNext(tr),i++) {
+ fprintf (fd, " <tr style=\"background:#%s\">\n",
+ (((i % 2) == 0) ? ecolor : ocolor));
+
+ /* Print table cells. If we have a url, make it a link.
+ */
+ for (td=vot_getTD(tr),i=0; td; td=vot_getNext(td),i++) {
+ s = ((s = vot_getValue (td)) ? s : "");
+ if (strncasecmp ("http://", s, 7) == 0)
+ fprintf (fd, " <td><a href=\"%s\">%s</a></td>\n", s, s);
+ else
+ fprintf (fd, " <td>%s</td>\n", s);
+ }
+ fprintf (fd, " </tr>\n");
+ }
+ fprintf (fd, "</tbody>\n");
+
+ fprintf (fd, "</table>\n");
+ fflush (fd);
+}
+
+
+/**
+ * VOT_HTMLFOOTER -- Printer the HTML footer.
+ */
+static void
+vot_htmlFooter (FILE *fd)
+{
+ fprintf (fd, "</body>\n</html>\n"); /* write the footer */
+ fflush (fd);
+}
+
+
+/***************************************
+ * END of HTML Utility procedures.
+ ***************************************/
+
+
+
+/**
+ * vot_writeDelimited -- Write the VOTable as a delimited text file.
+ *
+ * @brief Write the VOTable as a delimited text file.
+ * @fn vot_writeDelimited (handle_t vot, FILE *fd, char delim, int hdr)
+ *
+ * @param vot A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param hdr Write an output header?
+ * @return nothing
+ */
+void
+vot_writeDelimited (handle_t vot, char *fname, char delim, int hdr)
+{
+ char *name, *id, *ucd, *s;
+ int res, tab, data, tdata, field, tr, td; /* handles */
+ int i=0, ncols=0;
+ FILE *fd = (FILE *) NULL;
+
+
+ if (strcasecmp (fname, "stdout") == 0 || strncmp (fname, "-", 1) == 0)
+ fd = stdout;
+ else
+ fd = fopen (fname, "w+");
+
+ res = vot_getRESOURCE (vot); /* get RESOURCES */
+ if (vot_getLength (res) > 1) {
+ fprintf (stderr, "Error: multiple RESOURCES not supported\n");
+ return;
+ }
+
+ if ((tab = vot_getTABLE (res)) <= 0)
+ return;
+ if ((data = vot_getDATA (tab)) <= 0)
+ return;
+ if ((tdata = vot_getTABLEDATA (data)) <= 0)
+ return;
+ ncols = vot_getNCols (tdata);
+
+ /* Print the Column header names.
+ */
+ if (hdr) {
+ fprintf (fd, "# ");
+ i = 0;
+ for (field=vot_getFIELD (tab); field; field = vot_getNext (field)) {
+ name = vot_getAttr (field, "name"); /* find reasonable value */
+ id = vot_getAttr (field, "id");
+ ucd = vot_getAttr (field, "ucd");
+ if (name || id || ucd)
+ fprintf (fd, "%s", (name ? name : (id ? id : ucd)) );
+ else
+ fprintf (fd, "col%d", i);
+ if (i < (ncols-1))
+ fprintf (fd, "%c", delim);
+ i++;
+ }
+ fprintf (fd, "\n");
+ }
+
+
+ /* Now dump the data.
+ */
+ for (tr=vot_getTR (tdata); tr; tr=vot_getNext(tr)) {
+ for (td=vot_getTD(tr),i=0; td; td=vot_getNext(td),i++) {
+ s = ((s =vot_getValue (td)) ? s : "");
+ if (strchr (s, (int) delim))
+ fprintf (fd, "\"%s\"", s);
+ else
+ fprintf (fd, "%s", s);
+ if (i < (ncols-1))
+ fprintf (fd, "%c", delim);
+ }
+ fprintf (fd, "\n");
+ }
+
+ fflush (fd);
+ if (fd != stdout)
+ fclose (fd);
+}
+
+
+/**
+ * vot_writeASV -- Write the VOTable to the file descriptor as a ASV file
+ *
+ * @brief Write the VOTable to the file descriptor as a ASV file
+ * @fn vot_writeASV (handle_t node, char *fname, int header)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param header Write output header?
+ * @return nothing
+ */
+void
+vot_writeASV (handle_t node, char *fname, int header)
+{
+ vot_writeDelimited (node, fname, ' ', header);
+}
+
+
+/**
+ * vot_writeBSV -- Write the VOTable to the file descriptor as a BSV file
+ *
+ * @brief Write the VOTable to the file descriptor as a BSV file
+ * @fn vot_writeBSV (handle_t node, char *fname, int header)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param header Write output header?
+ * @return nothing
+ */
+void
+vot_writeBSV (handle_t node, char *fname, int header)
+{
+ vot_writeDelimited (node, fname, '|', header);
+}
+
+
+/**
+ * vot_writeCSV -- Write the VOTable to the file descriptor as a CSV file
+ *
+ * @brief Write the VOTable to the file descriptor as a CSV file
+ * @fn vot_writeCSV (handle_t node, char *fname, int header)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param header Write output header?
+ * @return nothing
+ */
+void
+vot_writeCSV (handle_t node, char *fname, int header)
+{
+ vot_writeDelimited (node, fname, ',', header);
+}
+
+
+/**
+ * vot_writeTSV -- Write the VOTable to the file descriptor as a TSV file
+ *
+ * @brief Write the VOTable to the file descriptor as a TSV file
+ * @fn vot_writeTSV (handle_t node, char *fname, int header)
+ *
+ * @param node A handle to an Element that you to print
+ * @param fname Output filename (or "stdout" or "-" for STDOUT)
+ * @param header Write output header?
+ * @return nothing
+ */
+void
+vot_writeTSV (handle_t node, char *fname, int header)
+{
+ vot_writeDelimited (node, fname, '\t', header);
+}
+
+
+/**
+ * vot_setWarnings -- Set the warning level.
+ *
+ * @brief Set the warning level.
+ * @fn vot_setWarnings (int value)
+ *
+ * @param value Warning level
+ * @return nothing
+ */
+void
+vot_setWarnings (int value)
+{
+ switch ((votWarn = value)) {
+ case 0: votELevel = ""; break;
+ case 1: votELevel = "Warning: "; break;
+ case 2: votELevel = "Error: "; break;
+ }
+}
+
+
+/**
+ * votEmsg -- Error message print utility.
+ */
+void
+votEmsg (char *msg)
+{
+ if (votWarn)
+ fprintf (stderr, "%s%s", votELevel, msg);
+}
+
+
+
+/****************************************************************************
+ * Private procedures.
+ ****************************************************************************/
+
+/**
+ * vot_cleanUp
+ *
+ * @brief Free all the handles and Element nodes.
+static void
+vot_cleanUp (void)
+{
+ vot_handleCleanup ();
+
+ if (vot_struct == NULL)
+ vot_struct = vot_newElem (TY_ROOT);
+
+ vot_struct->parent = NULL;
+ vot_struct->child = NULL;
+ vot_struct->last_child = NULL;
+ vot_struct->next = NULL;
+}
+ */
+
+
+/**
+ * vot_elementDup -- Duplicate the input Element.
+ *
+ * @brief Duplicate the input Element.
+ * @fn Element * vot_elementDup (handle_t element_h)
+ *
+ * @param element_h A handle_t to the ELEMENT you want to copy
+ * @return An ELEMENT type
+ */
+static Element *
+vot_elementDup (handle_t element_h)
+{
+ Element *new, *src;
+ handle_t new_h;
+ int type;
+ AttrList *attr;
+
+
+ src = vot_getElement (element_h); /* get the element to copy */
+ type = vot_typeOf (element_h); /* get the type */
+
+ if (type >= NUM_ELEMENTS)
+ return ((void *) NULL);
+
+ new_h = vot_nodeCreate (type); /* make a blank Node */
+ new = vot_getElement (new_h); /* get the pointer */
+
+
+ /* Copy the attributes.
+ */
+ for (attr=src->attr->attributes; attr; attr = attr->next)
+ vot_attrSet (new->attr, attr->name, attr->value);
+
+ /* Copy the content.
+ */
+ if (src->content) {
+ int len = strlen (src->content);
+
+ new->content = (char *) calloc ((len + 2), sizeof(char));
+ new->isCData = src->isCData;
+ strncpy (new->content, src->content, len);
+ }
+
+ return (new); /* return the copy */
+}
+
+
+/**
+ * vot_nodeCreate -- Create a new blank unlinked node.
+ *
+ * @brief Create a new blank unlinked node.
+ * @fn handle_t vot_nodeCreate (int type)
+ *
+ * @param type The type of node you wish to create
+ * @return A handle to the created node
+ */
+static handle_t
+vot_nodeCreate (int type)
+{
+ /* Make a new blank node and give it a handle. */
+ Element *elem = vot_newElem (type);
+
+ return (vot_setHandle (elem));
+}
+
+
+/**
+ * vot_attachToNode -- Adds a node as a child of parent.
+ *
+ * @brief Adds a node as a child of parent.
+ * @fn vot_attachToNode (handle_t parent, handle_t new)
+ *
+ * @param parent A handle to the Element that you want to add a node to
+ * @param new A handle to the Element that you want to add
+ * @return nothing
+ */
+static void
+vot_attachToNode (handle_t parent, handle_t new)
+{
+ Element *parent_ptr, *new_ptr;
+
+ /* Sanity check.
+ assert ( (new & vot_validChildren (parent)) );
+ */
+ if ((parent == 0) || (new == 0))
+ return;
+
+ parent_ptr = vot_getElement (parent);
+ new_ptr = vot_getElement (new);
+
+ new_ptr->ref_count++;
+
+ /* Make links. */
+ if (parent_ptr->child)
+ parent_ptr->last_child->next = new_ptr;
+ else
+ parent_ptr->child = new_ptr;
+
+ parent_ptr->last_child = new_ptr;
+
+ new_ptr->parent = parent_ptr;
+}
+
+
+/**
+ * vot_attachSibling -- Adds a node as a sibling of big_brother.
+ *
+ * @brief Adds a node as a sibling of big_brother.
+ * @fn vot_attachSibling (handle_t big_brother, handle_t new)
+ *
+ * @param big_brother Handle to the Element you want to add a node to
+ * @param new A handle to the Element that you want to add
+ * @return nothing
+ */
+static void
+vot_attachSibling (handle_t big_brother, handle_t new)
+{
+ Element *big_brother_ptr, *new_ptr;
+
+ /* Sanity check. */
+ if ((big_brother == 0) || (new == 0))
+ return;
+
+ /* Get relevant pointers.
+ */
+ big_brother_ptr = vot_getElement (big_brother);
+ new_ptr = vot_getElement (new);
+
+ new_ptr->ref_count++; /* Up reference count. DEFUNCT. */
+
+ /* Make the links.
+ */
+ if (big_brother_ptr->next)
+ big_brother_ptr->last_child->next = new_ptr;
+ else
+ big_brother_ptr->next = new_ptr;
+
+ big_brother_ptr->last_child = new_ptr;
+
+ new_ptr->parent = big_brother_ptr;
+}
+
+
+/**
+ * vot_dumpXML -- Prints the document tree as readable XML.
+ *
+ * @brief Prints the document tree as readable XML.
+ * @fn vot_dumpXML (Element *node, int level, int indent, FILE *fd)
+ *
+ * @param node A pointer to the Element that you want to print from.
+ * @param level The number of tabs to format the output.
+ * @param indent Number of spaces to indent at each level.
+ * @param fd The file descriptor to send the output to.
+ * @return nothing
+ */
+static void
+vot_dumpXML (Element *node, int level, int indent, FILE *fd)
+{
+ register int i, space = indent;
+
+
+ /* If the element is NULL, there is nothing to print.
+ */
+ if (node == NULL)
+ return;
+
+ /* Make spaces based on how deep we are and print the formatted Element.
+ */
+ for (i = 0; space && i < (space * level); i++)
+ fprintf (fd, " ");
+ fprintf (fd, "%s", vot_elemXML (node));
+
+ /* If there are children, recurse to them, print function returns.
+ */
+ if (node->child) {
+ if (indent)
+ fprintf (fd, "\n");
+ vot_dumpXML (node->child, (level + 1), indent, fd);
+
+ /* Print the content between the tags. */
+ if (node->content) {
+ if (node->isCData)
+ fprintf (fd, "<![CDATA[%s]]>", node->content);
+ else
+ fprintf (fd, "%s", vot_deWS(node->content));
+ }
+
+ /* Make space and print the closing XML tag.
+ */
+ for (i = 0; space && i < (space * level); i++)
+ fprintf (fd, " ");
+ fprintf (fd, "%s", vot_elemXMLEnd (node));
+
+ } else { /* This node has no children, beginning of base case. */
+ if (node->content) {
+ if (node->isCData)
+ fprintf (fd, "<![CDATA[%s]]>", node->content);
+ else
+ fprintf (fd, "%s", vot_deWS(node->content));
+ }
+
+ /* Print the closing XML tag.
+ */
+ fprintf (fd, "%s", vot_elemXMLEnd (node));
+ }
+
+ if (indent)
+ fprintf (fd, "\n");
+
+ /* If there are siblings, recurse through them.
+ */
+ if (node->next)
+ vot_dumpXML (node->next, level, indent, fd);
+
+ /* At this point there should be no more children or sibling on this node.
+ */
+ fflush (fd);
+}
+
+
+/**
+ * vot_deWS -- Determine whether the input string is nothing but whitespace.
+ */
+static char *
+vot_deWS (char *in)
+{
+ char *ip = in;
+
+ for (ip=in; *ip && isspace (*ip); ip++) ;
+ return ((*ip ? in : ""));
+}
+
+
+#ifdef USE_VALIDITY
+/**
+ * vot_validParents -- Return the mask of valid parents for the type.
+ */
+static int
+vot_validParents (int type)
+{
+ int i;
+
+ for (i=0; i < NUM_ELEMENTS; i++) {
+ if (elemParents[i].type >= 0 && elemParents[i].type)
+ return (elemParents[i].parents);
+ }
+
+ return (0);
+}
+
+
+/**
+ * vot_validChildren -- Return the mask of valid children for the type.
+ */
+static int
+vot_validChildren (int type)
+{
+ int i;
+
+ for (i=0; i < NUM_ELEMENTS; i++) {
+ if (elemParents[i].type >= 0 && elemParents[i].type)
+ return (elemParents[i].children);
+ }
+
+ return (0);
+}
+#endif
+
+
+
+/* Debug utility
+ */
+#ifdef USE_DEBUG
+static void votBob (void) { }
+
+
+/**
+ * vot_printData -- Print the table matrix.
+ *
+ * @brief Print the table matrix.
+ * @fn vot_printData (Element *tdata)
+ *
+ * @param tdata A pointer to the TABLEDATA Element that you want print
+ * @return nothing
+ */
+static void
+vot_printData (Element *tdata)
+{
+ Element *r = NULL, *c = NULL;
+ handle_t r_h, c_h;
+ int i, j, cols, ncells, rows;
+
+
+ if (tdata->type != TY_TABLEDATA) {
+ fprintf (stderr, "ERROR: Must be a TABLEDATA element to print.\n");
+ return;
+ }
+
+ cols = atoi (vot_attrGet (tdata->parent->parent->attr, "NCOLS"));
+ rows = atoi (vot_attrGet (tdata->parent->parent->attr, "NROWS"));
+ ncells = rows * cols;
+
+ if (ncells == 0) /* e.g. a metadata votable return */
+ return;
+
+ r_h = vot_getTR (vot_lookupHandle (tdata));
+ r = vot_getElement (r_h);
+
+ c_h = vot_getTD (vot_lookupHandle (r));
+ c = vot_getElement (c_h);
+
+ for (i = 0; r; i++) {
+ printf ("%02d: ", i);
+
+ for (j=0; c; j++) {
+ printf ("%s ", (tdata->data[(i * cols) + j]));
+ c = c->next;
+ }
+ r = r->next;
+
+ if (r)
+ c = r->child;
+
+ printf ("\n");
+ }
+}
+
+#endif