/*
 * t4xml.cpp --
 *
 *	This file contains the implementation of the "tgraph::xml" command.
 *
 *	Authors: Jacob Levy and Jean-Claude Wippler.
 *		 jyl@best.com	jcw@equi4.com
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "t4xml_c.h"
#include "e4xml.h"
#include "t4graphrep.h"

/*
 * The following code was contributed by David Gravereux. It deals with
 * making t4xml stubs compliant on Win32 systems automatically.
 */

#ifdef _MSC_VER
	/*
	 * Only do this when MSVC++ is compiling us.
	 */
#   ifdef USE_TCL_STUBS
	/*
	 * Mark this .obj as needing tcl's Stubs library.
	 */
#	pragma comment(lib, "tclstub" \
		STRINGIFY(JOIN(TCL_MAJOR_VERSION,TCL_MINOR_VERSION)) ".lib")
#	if !defined(_MT) || !defined(_DLL) || defined(_DEBUG)
	/*
	 * This fixes a bug with how the Stubs library was compiled.
	 * The requirement for msvcrt.lib from tclstubXX.lib should
	 * be removed.
	 */
#	    pragma comment(linker, "-nodefaultlib:msvcrt.lib")
#	endif
#   else
	/*
	 * Mark this .obj needing the import library
	 */
#	pragma comment(lib, "tcl" \
		STRINGIFY(JOIN(TCL_MAJOR_VERSION,TCL_MINOR_VERSION)) ".lib")
#   endif
#endif

/*
 * Script to create the "txml" namespace:
 */

static const char *txmlNspcCreate = (const char *) "namespace eval ::txml {}";

/*
 * Selectors for input type:
 */

static char *inputSelectors[] = {
    (char *) "-channel",
    (char *) "-string",
    (char *) "-variable",
    NULL
};
typedef enum XInputSelectors {
    XIChannel = 0,
    XIString,
    XIVar
} XInputSelectors;

/*
 * Selectors for output type:
 */

typedef enum XOutputSelectors {
    XOChannel = 0,
    XOVariable,
    XOAppend,
    XOString
} XOutputSelectors;

/*
 * Static functions declared in this file:
 */

static int	T4XML_OutputToInterpResult(Tcl_Interp *interp, 
					   Tcl_Obj *nodeName, 
					   Tcl_Obj *xmlName,
					   bool ex);
static int	T4XML_OutputToChannel(Tcl_Interp *interp, Tcl_Obj *nodeName,
				      Tcl_Obj *xmlName, Tcl_Obj *channelName,
				      bool ex);
static int	T4XML_OutputToVar(Tcl_Interp *interp, Tcl_Obj *nodeName,
				  Tcl_Obj *xmlName, Tcl_Obj *varName, 
				  int flags, bool ex);
static int	T4XML_ExportNode(Tcl_Interp *interp, Tcl_Obj *nodeName, 
				 e4_Node &nn);
static int	T4XML_InputFromChannel(Tcl_Interp *interp,
				       Tcl_Obj *channelName,
				       Tcl_Obj *nodeName);
static int	T4XML_InputFromString(Tcl_Interp *interp, Tcl_Obj *nodeName,
				      Tcl_Obj *inputString);
static int	T4XML_InputFromVar(Tcl_Interp *interp, Tcl_Obj *nodeName,
				   Tcl_Obj *varName);
static int	T4XML_OutputCmdProc(ClientData cd,
				    Tcl_Interp *interp, int objc,
				    Tcl_Obj *CONST objv[]);
static int	T4XML_InputCmdProc(ClientData cd, 
				   Tcl_Interp *interp, int objc, 
				   Tcl_Obj *CONST objv[]);

/*
 * T4XML_ExportNode --
 *
 *	Given a command name in a given interpreter, obtain the
 *	e4_Node structure associated with the command.
 *
 * Results:
 *	Upon success, the interpreter result is blank. The e4_Node is
 *	returned via an output parameter.
 *
 * Side effects:
 *	None.
 */

static int
T4XML_ExportNode(Tcl_Interp *interp, Tcl_Obj *nodeName, e4_Node &nn)
{
    char *nname = Tcl_GetString(nodeName);
    GO_InternalRep *ptr2;
    T4InternalRep *tip;
    T4Node *tnp;
    Tcl_CmdInfo cmdInfo;

    if (Tcl_GetCommandInfo(interp, nname, &cmdInfo) == 0) {
        Tcl_ResetResult(interp);
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "there is no node named ",
			       nname, NULL);
	return TCL_ERROR;
    }
    Tcl_ResetResult(interp);

    ptr2 = (GO_InternalRep *) cmdInfo.objClientData;
    if (ptr2 == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "internal error: partially defined node ",
			       nname, NULL);
	return TCL_ERROR;
    } 
    tip = (T4InternalRep *) ptr2->data;
    if (tip == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "internal error: partially defined node ",
			       nname, NULL);
	return TCL_ERROR;
    } 
    if (tip->KindIdentifier() != T4GRAPH_NODE) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       nname, " is not the name of a node", NULL);
	return TCL_ERROR;
    }
    tnp = (T4Node *) tip;

    tnp->ExternalizeNode(nn);

    return TCL_OK;
}

/*
 * T4XML_InputFromChannel --
 *
 *	This procedure reads in XML from a channel.
 *
 * Results:
 *	Upon success, the interpreter result is empty. The given node will
 *	contain new vertices representing the parsed XML input.
 *
 * Side effects:
 *	May read input, may allocate new storage in a T4Storage object, and
 *	may allocate new memory.
 */

static int
T4XML_InputFromChannel(Tcl_Interp *interp, 
		       Tcl_Obj *nodeName, Tcl_Obj *channelName)
{
    e4_XMLParser *parser;
    e4_Node n;
    Tcl_Channel chan;
    char *input;
    int len, mode;
    bool done = false;
    bool error = false;
    Tcl_Obj *bufObj;

    if (T4XML_ExportNode(interp, nodeName, n) != TCL_OK) {
	return TCL_ERROR;
    }
    if (channelName == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "missing channel name for xml::input", NULL);
	return TCL_ERROR;
    }
    chan = Tcl_GetChannel(interp, Tcl_GetString(channelName), &mode);
    if (chan == NULL) {
	return TCL_ERROR;
    }
    Tcl_ResetResult(interp);
    if ((mode & TCL_READABLE) != TCL_READABLE) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "channel ", Tcl_GetString(channelName),
			       " is not open for reading", NULL);
	return TCL_ERROR;
    }
    parser = new e4_XMLParser(n);
    if (parser == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "failed to construct parser", NULL);
	return TCL_ERROR;
    }
    bufObj = Tcl_NewStringObj("", 0);
    do {
	Tcl_SetStringObj(bufObj, "", 0);
	if ((Tcl_GetsObj(chan, bufObj) < 0) && (Tcl_Eof(chan))) {
	    done = true;
	    continue;
	}
	if (parser->Finished()) {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   "input following end of xml on ",
				   "channel ", Tcl_GetString(channelName),
				   NULL);
	    error = true;
	    done = true;
	    continue;
	}
	input = Tcl_GetStringFromObj(bufObj, &len);
	if (input == NULL) {
	    done = true;
	    error = true;
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   "error reading channel ",
				   Tcl_GetString(channelName),
				   NULL);
	    continue;
	}
	if (!parser->Parse(input, len)) {
	    done = true;
	    error = true;
	    if (parser->ErrorString() != NULL) {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       parser->ErrorString(), NULL);
	    } else {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       "unspecified parser error", NULL);
	    }
	    continue;
	}
    } while (!done);

    if (!parser->Finished()) {
	error = true;
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "premature termination of xml input on ",
			       "channel ", Tcl_GetString(channelName),
			       NULL);
    }
    if (parser->Finished() && parser->HasError()) {
	error = true;
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       parser->ErrorString(), NULL);
    }

    delete parser;

    if (error) {
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 * T4XML_InputFromVar --
 *
 *	This procedure reads in XML from the value of a Tcl variable.
 *
 * Results:
 *	A standard Tcl result. Upon success, the interpreter result
 *	is empty.
 *
 * Side effects:
 *	May read input, may allocate new storage in a T4Storage object, and
 *	may allocate new memory.
 */

static int
T4XML_InputFromVar(Tcl_Interp *interp, Tcl_Obj *nodeName,
		   Tcl_Obj *varName)
{
    e4_XMLParser *parser;
    e4_Node n;
    char *input;

    if (T4XML_ExportNode(interp, nodeName, n) != TCL_OK) {
	return TCL_ERROR;
    }
    if (varName == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "missing input var to xml::input", NULL);
	return TCL_ERROR;
    }
    input = (char *) Tcl_GetVar(interp, Tcl_GetString(varName), 0);

    /*
     * If the value returned from Tcl_GetVar is NULL, then an error message
     * is already left in the interpreter result.
     */

    if (input == NULL) {
	return TCL_ERROR;
    }

    parser = new e4_XMLParser(n);
    if (parser == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "failed to construct parser", NULL);
	return TCL_ERROR;
    }
    if (!parser->Parse(input, strlen(input))) {
	if (parser->HasError()) {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   parser->ErrorString(), NULL);
	} else {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   "unspecified parse error", NULL);
	}
	delete parser;
	return TCL_ERROR;
    }

    if (!parser->Finished()) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "parse error: Premature termination", NULL);
	delete parser;
	return TCL_ERROR;
    }
    if (parser->Finished() && parser->HasError()) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       parser->ErrorString(), NULL);
	delete parser;
	return TCL_ERROR;
    }

    delete parser;

    return TCL_OK;
}

/*
 * T4XML_InputFromString --
 *
 *	This procedure reads in XML from a given string.
 *
 * Results:
 *	A standard Tcl result. Upon success, the interpreter result
 *	is empty.
 *
 * Side effects:
 *	May read input, may allocate new storage in a T4Storage object, and
 *	may allocate new memory.
 */

static int
T4XML_InputFromString(Tcl_Interp *interp, 
		      Tcl_Obj *nodeName, Tcl_Obj *inputString)
{
    e4_XMLParser *parser;
    e4_Node n;
    char *input;
    int len;

    if (T4XML_ExportNode(interp, nodeName, n) != TCL_OK) {
	return TCL_ERROR;
    }
    if (inputString == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "empty input string to xml::input", NULL);
	return TCL_ERROR;
    }
    input = Tcl_GetStringFromObj(inputString, &len);
    parser = new e4_XMLParser(n);
    if (parser == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "failed to construct parser", NULL);
	return TCL_ERROR;
    }
    if (!parser->Parse(input, len)) {
	if (parser->HasError()) {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   parser->ErrorString(), NULL);
	} else {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   "unspecified parse error", NULL);
	}
	delete parser;
	return TCL_ERROR;
    }

    if (!parser->Finished()) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "parse error: Premature termination", NULL);
	delete parser;
	return TCL_ERROR;
    }
    if (parser->Finished() && parser->HasError()) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       parser->ErrorString(), NULL);
	delete parser;
	return TCL_ERROR;
    }

    delete parser;

    return TCL_OK;
}

/*
 * T4XML_OutputXML --
 *
 *	This routine leaves in the interpreter result a string object
 *	representing the XML output for the given node, wrapped with
 *	the supplied tag.
 *
 * Results:
 *	A standard Tcl result. Upon success, the interpreter result
 *	holds a string object containing the XML output.
 *
 * Side effects:
 *	None.
 */

static int
T4XML_OutputXML(Tcl_Interp *interp, e4_Node n, char *tagName, bool ex)
{
    e4_XMLGenerator *g;
    char *xmloutput;

    Tcl_ResetResult(interp);

    /*
     * Generate the XML output. The returned string is owned by the XML
     * generator, so it will be freed when the generator is deleted.
     */

    g = new e4_XMLGenerator(n, tagName, ex);
    xmloutput = g->Generate();

    /*
     * Check for errors.
     */

    if (xmloutput == NULL) {
	delete g;
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "error generating XML", NULL);
	return TCL_ERROR;
    }

    /*
     * Put the XML output into the interpreter result.
     */

    Tcl_SetStringObj(Tcl_GetObjResult(interp), xmloutput, -1);

    /*
     * Clean up.
     */

    delete g;

    return TCL_OK;
}

/*
 * T4XML_OutputToInterpResult --
 *
 *	This procedure writes out XML for a given node, wrapped in an enclosing
 *	XML tag with the given name, into the interpreter result.
 *
 * Results:
 *	On success, the interpreter result contains the XML produced from the
 *	given input.
 *
 * Side effects:
 *	None.
 */

static int
T4XML_OutputToInterpResult(Tcl_Interp *interp, Tcl_Obj *nodeName,
			   Tcl_Obj *XMLName, bool ex)
{
    e4_Node n;

    if (T4XML_ExportNode(interp, nodeName, n) != TCL_OK) {
	return TCL_ERROR;
    }
    Tcl_ResetResult(interp);
    if (!n.IsValid()) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "invalid node ", Tcl_GetString(nodeName),
			       NULL);
	return TCL_ERROR;
    }
    return T4XML_OutputXML(interp, n, Tcl_GetString(XMLName), ex);
}

/*
 * T4XML_OutputToChannel --
 *
 *	Produce XML representing the given node to a given channel.
 *
 * Results:
 *	A standard Tcl result. Upon success, the interpreter result will be
 *	empty and the given channel will contain the XML output.
 *
 * Side effects:
 *	May write to a channel.
 */

static int
T4XML_OutputToChannel(Tcl_Interp *interp, Tcl_Obj *nodeName, Tcl_Obj *XMLName,
		      Tcl_Obj *channelName, bool ex)
{
    Tcl_Channel chan;
    int mode;
    int len;
    char *xml;

    if (channelName == NULL) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "missing channel name for xml::output", NULL);
	return TCL_ERROR;
    }
    chan = Tcl_GetChannel(interp, Tcl_GetString(channelName), &mode);
    if ((mode & TCL_WRITABLE) != TCL_WRITABLE) {
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "channel ", Tcl_GetString(channelName),
			       " is not open for writing", NULL);
	return TCL_ERROR;
    }
    if (T4XML_OutputToInterpResult(interp, nodeName, XMLName, ex) != TCL_OK) {
	return TCL_ERROR;
    }
    xml = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
    (void) Tcl_WriteChars(chan, xml, len);

    Tcl_ResetResult(interp);

    return TCL_OK;
}

/*
 * T4XML_OutputToVar --
 *
 *	This procedure stores the XML produced from a node in a Tcl variable.
 *	If the "flags" argument contains the bit TCL_APPEND_VALUE set then the
 *	XML is appended to the existing value instead of replacing it.
 *
 * Results:
 *	A standard Tcl result. Upon successful completion the interpreter
 *	result is empty.
 *
 * Side effects:
 *	A Tcl variable may be created.
 */

static int
T4XML_OutputToVar(Tcl_Interp *interp, Tcl_Obj *nodeName, Tcl_Obj *XMLName,
		  Tcl_Obj *varName, int flags, bool ex)
{
    Tcl_Obj *res;
    char *xml;
    int len;

    if (T4XML_OutputToInterpResult(interp, nodeName, XMLName, ex) != TCL_OK) {
	return TCL_ERROR;
    }

    res = Tcl_GetObjResult(interp);
    Tcl_IncrRefCount(res);
    xml = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
    Tcl_ResetResult(interp);

    (void) Tcl_SetVar(interp, Tcl_GetString(varName), xml, flags);

    Tcl_DecrRefCount(res);

    Tcl_ResetResult(interp);

    return TCL_OK;
}

/*
 * T4XML_InputCmdProc --
 *
 *	This procedure reads in XML from a channel or from a given string.
 *
 * Results:
 *	Upon success, the interpreter result is empty. The given node will
 *	contain new vertices representing the parsed XML input.
 *
 * Side effects:
 *	May read input, may allocate new storage in a T4Storage object, and
 *	may allocate new memory.
 */

static int
T4XML_InputCmdProc(ClientData cd, Tcl_Interp *interp, int objc,
		   Tcl_Obj *CONST objv[])
{
    XInputSelectors index;

    /*
     * Expecting exactly three arguments:
     *
     * txml::input node -channel chan
     * --OR--
     * txml::input node -string str
     */

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 0, NULL,
			 "txml::input node sel sel-arg");
	return TCL_ERROR;
    }

    /*
     * Figure out what type of input to read:
     */

    if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **) inputSelectors, 
			    (char *) "inputSelector", 0, (int *) &index)
	!= TCL_OK) {
	return TCL_ERROR;
    }

    switch (index) {
    case XIChannel:
	return T4XML_InputFromChannel(interp, objv[1], objv[3]);
    case XIString:
	return T4XML_InputFromString(interp, objv[1], objv[3]);
    case XIVar:
	return T4XML_InputFromVar(interp, objv[1], objv[3]);
    default:
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "T4XML_InputCmdProc: unreachable code!",
			       (char *) NULL);
	return TCL_ERROR;
    }
}

/*
 * T4XML_OutputCmdProc --
 *
 *	This procedure produces XML from a given node, wrapped in a tag
 *	named according to the given XML name.
 *
 * Results:
 *	Upon success, the XML representing the given node is produced. It is
 *	written to various destinations depending on the output selector
 *	given.
 *
 * Side effects:
 *	May write output, may create a variable or append to a given variable.
 */

static int
T4XML_OutputCmdProc(ClientData cd, Tcl_Interp *interp, int objc,
		    Tcl_Obj *CONST objv[])
{
    XOutputSelectors index = XOString;
    Tcl_Obj *chan;
    Tcl_Obj *var;
    int ex = 0;
    bool exv;
    char *str;
    int i;

    /*
     * Expecting a variable number of arguments:
     *
     * txml::output node name ?-pure <bool>? ?-string?
     * txml::output node name ?-pure <bool>? ?-channel chanName?
     * txml::output node name ?-pure <bool>? ?-variable varName?
     * --OR--
     * txml::output node name ?-pure <bool>? ?-append varName?
     */

    if (objc < 3) {
	Tcl_WrongNumArgs(interp, 0, NULL,
			 "txml::output node name ?args ...?");
	return TCL_ERROR;
    }

    for (i = 3; i < objc; i++) {
	str = Tcl_GetString(objv[i]);
	if (strncmp(str, "-pure", strlen("-pure")) == 0) {
	    i++;
	    if (i == objc) {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       "txml::output: expecting argument ",
				       "to -pure", NULL);
		return TCL_ERROR;
	    }
	    if (Tcl_GetBooleanFromObj(interp, objv[i], &ex) != TCL_OK) {
		return TCL_ERROR;
	    }
	} else if (strncmp(str, "-string", strlen("-string")) == 0) {
	    index = XOString;
	} else if (strncmp(str, "-channel", strlen("-channel")) == 0) {
	    i++;
	    if (i == objc) {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       "txml::output: expecting argument ",
				       "to -channel", NULL);
		return TCL_ERROR;
	    }
	    index = XOChannel;
	    chan = objv[i];
	} else if (strncmp(str, "-variable", strlen("-variable")) == 0) {
	    i++;
	    if (i == objc) {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       "txml::output: expecting argument ",
				       "to -variable", NULL);
		return TCL_ERROR;
	    }
	    index = XOVariable;
	    var = objv[i];
	} else if (strncmp(str, "-append", strlen("-append")) == 0) {
	    i++;
	    if (i == objc) {
		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				       "txml::output: expecting argument ",
				       "to -append", NULL);
		return TCL_ERROR;
	    }
	    index = XOAppend;
	    var = objv[i];
	} else {
	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
				   "txml::output: unrecognized option \"",
				   Tcl_GetString(objv[i]), NULL);
	    return TCL_ERROR;
	}
    }

    /*
     * After all arguments have been parsed, dispatch to the right operation:
     */

    exv = (ex == 0 ? false : true);

    switch (index) {
    case XOString:
	return T4XML_OutputToInterpResult(interp, objv[1], objv[2], exv);
    case XOChannel:
	return T4XML_OutputToChannel(interp, objv[1], objv[2], chan, exv);
    case XOVariable:
	return T4XML_OutputToVar(interp, objv[1], objv[2], var, 0, exv);
    case XOAppend:
	return T4XML_OutputToVar(interp, objv[1], objv[2], var,
				 TCL_APPEND_VALUE, exv);
    default:
	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
			       "T4XML_InputCmdProc: unreachable code!",
			       (char *) NULL);
	return TCL_ERROR;
    }
}

/*
 * T4XML_RealInit --
 *
 *	This procedure initializes the given interpreter with the
 *	T4XML command.
 *
 * Results:
 *	Always succeeds and returns TCL_OK.
 *
 * Side effects:
 *	Defines a new command, "tgraph::xml", in the given interpreter.
 */

extern "C" int
T4XML_RealInit(Tcl_Interp *interp)
{
#ifdef USE_TCL_STUBS
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
	return TCL_ERROR;
    }
#endif

    /*
     * Require the T4Graph package.
     */

    if (Tcl_PkgRequire(interp, "tgraph", (char *) "1.0.0", 0) == NULL) {
	return TCL_ERROR;
    }

    /*
     * Create the "::txml" namespace.
     */

    (void) Tcl_Eval(interp, (char *) txmlNspcCreate);

    (void) Tcl_CreateObjCommand(interp, (char *) "::txml::input",
				T4XML_InputCmdProc, (ClientData) NULL,
				(Tcl_CmdDeleteProc *) NULL);
    (void) Tcl_CreateObjCommand(interp, (char *) "::txml::output",
				T4XML_OutputCmdProc, (ClientData) NULL,
				(Tcl_CmdDeleteProc *) NULL);

    /*
     * All went well, so declare that we provide the T4XML package.
     */

    return Tcl_PkgProvide(interp, (char *) "txml", (char *) "1.0.0");
}
