.
Contributions from Yon Derek:
Version 1.4, released on 07/09/2001
* fixed the bug where ns_xml node ... wouldn't work for
nodes from documents parsed with -persist flag
Version 1.3 released on 07/05/2001
* bugfix for ns_xml apply_xslt (incorrect parsing of arguments)
Version 1.2 released on 07/04/2001
* XSLT support has been added in the form of
ns_xml apply_xslt $xslt_doc_id $xml_doc_id and
ns_xml parse_xslt ?-persist? command.
You need to install libxslt for it to work.
* looks like ns_xml has not been updated to libxml2 which would
result in the ns_xml doc root not returning a root but
a node below the root. Most visibly, if there
was a inside XML doc, you would only get
a couple of nodes and not the whole doc. Fixed. May change
behaviour of scripts.
* ns_xml parse -persistent was broken (would crash) because a hash table
for persistent docs has not been initialized. Fixed by initializing hash table.
* ns_xml stats was broken, free() was used to free the memory allocated by
ckalloc(). Fixed by calling ckfree().
* many commands didn't check for the number of arguments so they would
crash if called incorrectly. Fixed by adding checks for a proper number
of arguments.
* fixed Windows compilation issues.
* regression tests were written for the OpenNSD test framework.
Tests cover all the issues above and then some.
Jeremy Collins added XPath support.
nsxml-1.5/design.htm 0000644 0001750 0001750 00000023742 07500537427 015616 0 ustar giskard giskard 0000000 0000000
ns_xml 2.0 Requirements and Design
ns_xml
2.0 Requirements and Design
by John Mileham
Motivation
ns_xml
as it exists currently is an integral part of OpenACS 4.x, but its implementation
fails to expose several important aspects of libxml
's tree manipulation API.
As a result, it is impossible to create some legal XML documents using
ns_xml
. The shortcoming lies in ns_xml
's inability
to create node content through the use of text
nodes. In
ns_xml
, the only way to manipulate the content of a node is to
either initialize the node's content at creation time (e.g. through a
node new_child
call) or by setting it explicitly through
node setcontent
. That overlooks the case of this simple XML
snippet:
<sentence>Here is <keyword>my</keyword> sentence</sentence>
That is only part of the challenge, however. ns_xml
is also
limited to the linear creation of a document. This is great if your goal is
to programmatically serialize data in a database, which is the obvious use
of ns_xml
within OpenACS. If you hope to mutate the structure of
an existing document or create a document in a random-access fashion (e.g. through a
user interface), however, you're in trouble. The following shortcomings exist:
- Nodes can not be inserted before other existing nodes at the same level.
- Nodes can not be deleted.
- Nodes can not be moved.
- Nodes can not be copied.
- You can't find the parent of a node, so tree traversal is one-way (top-down).
- An attribute can not be unset. It can be set to null, but not made to disappear.
The Requirements
- Support for all legacy (
ns_xml
1.x) Tcl code.
- Support for
text
node creation.
- Support for "insert as previous" node creation for both text and element nodes.
- Support for node deletion (recursive).
- Support for node relocation, including cross-document relocation. Nodes moved between documents of different persistence states
will inherit the persistence of the new document (i.e. a node moved from a persistent document to a transient document will vanish at the
end of the Tcl interpreter's session).
- Support for in-place node duplication (recursive). The duplicate will be instantiated as the sibling following the cloned node.
- Support for bottom-up tree traversal, from child to parent.
- Support for unsetting attributes
And from the "While we're at it" department:
- Support rendering of any node as a stand-alone XML document (recursive)
- Get rid of the
ns_xml doc free
naming convention. This is Tcl. We don't want to think about freeing memory. We're just deleting persistent documents here. :)
The Design
Creating this functionality means adding several commands to the
ns_xml
API. The naming scheme of ns_xml
's existing
calls is very intuitive to the Tcl developer. However, it quickly became
apparent that the naming scheme wasn't general enough to encompass the new
functionality cleanly. If an attempt were made to add the functionality
described above within the existing naming scheme, all transparency would be
lost. Tcl developers would find themselves referring to reference material
constantly. Luckily, the naming scheme proposed below does not conflict with
the existing scheme (except in cases where the commands are identical between
the two schemes).
Thus, all the deprecated calls can be mapped to their 2.0
equivalents, and a user can run legacy Tcl code unmodified.
In the new scheme, commands are divided into four functional buckets which
largely coincide with their first word. All calls follow the
grammatical convention:
subject verb [object]
with the sole exception of ns_xml create xml
, which is unique in
that its job is to conjure up an object with no relationship to anything that
exists.
Note that the Node Interaction bucket is subdivided into several categories due to its
complexity.
The 2.0 API
- Document Instantiation
set xml_doc_id [ns_xml string parse xml ?-persist? ?-validate? string]
set xsl_doc_id [ns_xml string parse xsl ?-persist? ?-validate? string]
set xml_doc_id [ns_xml create xml ?-persist? ?doc-version?]
- Document Transformation
set new_xml_doc_id [ns_xml transform ?-persist? xml_doc_id xsl_doc_id]
- Document Interaction
set node_id [ns_xml doc get root doc_id]
set node_id [ns_xml doc create root doc_id node_name node_content]
set node_id [ns_xml doc render doc_id]
set node_id [ns_xml doc delete doc_id]
set node_id [ns_xml doc cleanup doc_id]
(tolerant of already deleted documents)
- Node Interaction
- Tree Traversal
set node_id_list [ns_xml node get children node_id]
set node_id [ns_xml node get parent node_id]
- Node Instantiation
set node_id [ns_xml node create child_node node_id name content]
set node_id [ns_xml node create child_text node_idcontent]
set node_id [ns_xml node create prev_sibling_node node_id name content]
set node_id [ns_xml node create prev_sibling_text node_id content]
set node_id [ns_xml node create next_sibling_node node_id name content]
set node_id [ns_xml node create next_sibling_text node_idcontent]
- Node Duplication
new_node_id [ns_xml node clone node_id]
- Node Relocation - note that relinked nodes receive new node_ids. The old ids become defunct.
set new_node_id [ns_xml node relink_as child node_id new_parent_node_id]
set new_node_id [ns_xml node relink_as prev_sibling node_id new_sibling_node_id]
set new_node_id [ns_xml node relink_as next_sibling node_id new_prev_sibling_node_id]
- Node Removal
ns_xml node delete node_id
- Value Retrieval
set string [ns_xml node get attr node_id prop_name]
set string [ns_xml node get name node_id prop_name]
set string [ns_xml node get type node_id prop_name]
set string [ns_xml node get content node_id prop_name]
- Value Manipulation
ns_xml node set attr node_id prop_name string
ns_xml node unset attr node_id prop_name
ns_xml node set content node_id string
- Node Serialization
set string [ns_xml node render node_id]
Why'd you do that? (a.k.a. Design Considerations)
- Why
ns_xml string parse
and not just ns_xml parse
?
Because future support may be added for parsing filehandles in addition to tcl in-memory strings.
- Why
ns_xml create xml
and not just ns_xml create
?
This leaves the door open for dynamic creation of other documents like XSL or DTDs in the future. Note that XSL can currently only
be parsed and applied to XML documents as-is.
- Why implement both
ns_xml node create child
and ns_xml node create next_sibling
when you can
just create a new child of a given node's parent? (or similar questions)
This API walks a fine line between developer usability and cleanliness. The number of
calls could have been drastically reduced as well if we offered a lower-level API requiring developers to
first instantiate nodes and then link them as they chose. But that makes the developer's life
harder (not to mention decreasing performance by putting code that would be in C in Tcl), which is what we're trying to avoid.
We're also trying to get a fair amount of interoperability with the 1.x API, whose lack of a get parent
command mandated the use of the create next_sibling
command in cases where the parent was inconvenient or
impossible to derive. So deprecating the whole create sibling
concept seemed a bit harsh,
especially since it's speedier than the Tcl workaround.
- Why does the XSL transformation command have the opposite arg order to the old one?
Because the old ordering goes against every other command in the API
- Why is there an
xml node render
command? You could clone the node, relink the copy to a new document and render that!
Partly because it creates parallelism with the document itself in that a node can be created, futzed with, serialized and deleted. Partly because it's more efficient than the alternative. Partly because
it's useful in creating a nice UI for editing complicated XML documents. Partly because I say so. ;-)
John Mileham
nsxml-1.5/nsxml.c 0000644 0001750 0001750 00000172377 10054733614 015143 0 ustar giskard giskard 0000000 0000000 /*
* This is open-source software, copyright 2000 ArsDigita Corporation
* and licensed under the GNU General Public License as described in
* LICENSE file.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Copyright (C) 2002 Jeremy Collins
* Copyright (C) 2002 Scott S. Goodwin
* Copyright (C) 2002 Dave Bauer
* Copyright (C) 2001 John Mileham
* Copyright (C) 2001 Yon Derek
* Copyright (C) 2000-2001 ArsDigita Corporation and Curtis Galloway
*/
/*
* nsxml.c --
*
* This module implements an XML parser using the GNOME libxml2 library.
*/
static const char *RCSID =
"@(#) $Header: /cvsroot/aolserver/nsxml/nsxml.c,v 1.12 2002/07/23 19:19:46 scottg Exp $, compiled: "
__DATE__ " " __TIME__;
/*
* The 2.0 API (see design.htm)
*
* Document Instantiation
* set xml_doc_id [ns_xml string parse xml ?-persist? ?-validate? string]
* set xsl_doc_id [ns_xml string parse xsl ?-persist? ?-validate? string]
* set xml_doc_id [ns_xml create xml ?-persist? ?doc-version?]
* Document Transformation
* set new_xml_doc_id [ns_xml transform ?-persist? ?-param key value? xml_doc_id xsl_doc_id]
* (value is an xpath expression or use 'value' for strings)
* Document Interaction
* set node_id [ns_xml doc get root doc_id]
* set node_id [ns_xml doc create root doc_id node_name node_content]
* set node_id [ns_xml doc render doc_id]
* set node_id [ns_xml doc delete doc_id]
* set node_id [ns_xml doc cleanup doc_id] (tolerant of already deleted documents)
* Tree Traversal
* set node_id_list [ns_xml node get children node_id]
* set node_id [ns_xml node get parent node_id]
* Node Instantiation
* set node_id [ns_xml node create child_node node_id name content]
* set node_id [ns_xml node create child_text node_idcontent]
* set node_id [ns_xml node create prev_sibling_node node_id name content]
* set node_id [ns_xml node create prev_sibling_text node_id content]
* set node_id [ns_xml node create next_sibling_node node_id name content]
* set node_id [ns_xml node create next_sibling_text node_idcontent]
* Node Duplication
* new_node_id [ns_xml node clone node_id]
* Node Relocation - note that relinked nodes receive new node_ids. The old ids become defunct.
* set new_node_id [ns_xml node relink_as child node_id new_parent_node_id]
* set new_node_id [ns_xml node relink_as prev_sibling node_id new_sibling_node_id]
* set new_node_id [ns_xml node relink_as next_sibling node_id new_prev_sibling_node_id]
* Node Removal
* ns_xml node delete node_id
* Value Retrieval
* set string [ns_xml node get attr_names node_id]
* set string [ns_xml node get attr node_id prop_name]
* set string [ns_xml node get name node_id]
* set string [ns_xml node get type node_id]
* set string [ns_xml node get content node_id]
* Value Manipulation
* ns_xml node set attr node_id prop_name string
* ns_xml node unset attr node_id prop_name
* ns_xml node set content node_id string
* Node Serialization
* set string [ns_xml node render node_id]
* LEGACY ns_xml 1.3 INTERFACE. Largely deprecated for naming convention
* reasons. Please see design.html for new calls (there is a 1 to 1 mapping).
* These calls continue to work, but may be removed in future versions.
*
* ns_xml parse ?-persist? (DEPRECATED)
* ns_xml parse_xslt ?-persist? (DEPRECATED)
* ns_xml apply_xslt ?-persist? (DEPRECATED)
*
* ns_xml doc create ?-persist? ?-validate? ?doc-version? (DEPRECATED)
* ns_xml doc free (DEPRECATED)
* ns_xml doc root (DEPRECATED)
* ns_xml doc new_root (DEPRECATED)
* ns_xml doc render
*
* ns_xml node children
* ns_xml node getattr (DEPRECATED)
* ns_xml node setattr (DEPRECATED)
* ns_xml node new_child (DEPRECATED)
* ns_xml node new_sibling (DEPRECATED)
* ns_xml node getcontent (DEPRECATED)
* ns_xml node setcontent (DEPRECATED)
*
* TODO:
* - add SAX callback code
* - use AOLserver 3 API when possible
*/
#include
#include
#include
#include
#include
#include
#ifdef DO_XSLT
#include
#include
#include
#include
#endif
#ifdef DMALLOC
#include
#undef Ns_Malloc
#define Ns_Malloc malloc
#undef Ns_Free
#define Ns_Free free
#undef Ns_StrDup
#define Ns_StrDup strdup
#endif
extern
xmlParserInputPtr
xmlNewStringInputStream(xmlParserCtxtPtr ctxt, const xmlChar *buffer);
typedef struct _id_info {
unsigned int next_doc_id;
unsigned int next_node_id;
/* Holds a doc_info structure, with doc ID as key */
Tcl_HashTable doc_hash_byid;
/* Holds a doc_info structure, with doc pointer as key */
Tcl_HashTable doc_hash_bydoc;
/* Holds the node pointer, with node ID as key */
Tcl_HashTable node_hash_byid;
/* Holds the node ID, with node pointer as key */
Tcl_HashTable node_hash_bynode;
} id_info;
/* Used to create a linked list of nodes which have Tcl IDs
* for this document.
*/
typedef struct _node_info {
xmlNode *node;
struct _node_info *next;
} node_info;
/* This holds a pointer to the document
* and to a list of nodes that we have allocated
* node IDs for this document.
*/
typedef struct _doc_info {
xmlDoc *doc;
#ifdef DO_XSLT
xsltStylesheetPtr xslt;
#endif
struct _node_info *nodes;
} doc_info;
/*
* For both persistent and temporary documents,
* we need to keep track of the mapping of ids to
* documents and nodes.
* Node IDs are always temporary.
*/
static Ns_Mutex lock;
static id_info *perm_info=NULL;
static Tcl_HashTable interp_hash; /* For per-interp hash tables */
/* default values for the configuration parameters */
#define DEFAULT_DEBUG NS_FALSE
/* configurable parameters */
static int debug_p;
#ifdef WIN32
/* MSVC does not have __FUNCTION__ */
#define lexpos() \
__FILE__, __LINE__, __FILE__
#else
#define lexpos() \
__FILE__, __LINE__, __FUNCTION__
#endif
/* how big of a stack buffer to use for printing error messages
*/
#define STACK_BUFFER_SIZE 20000
/* how many params to allow (times 2)
*/
#define MAX_PARAMS 16
/* for optional logging of all kinds of random stuff, turn on
debug in the [ns/server//module/] section
of your nsd.ini
*/
static void
my_log (char *file, int line, char *fn, char *fmt, ...)
{
char *buf1;
char *buf;
va_list ap;
if (!debug_p)
return;
buf1 = Ns_Malloc(STACK_BUFFER_SIZE);
buf = Ns_Malloc(STACK_BUFFER_SIZE);
va_start (ap, fmt);
vsprintf (buf1, fmt, ap);
va_end (ap);
#ifdef WIN32
_snprintf (buf, STACK_BUFFER_SIZE, "%s:%d:%s: %s", file, line, fn, buf1);
#else
snprintf (buf, STACK_BUFFER_SIZE, "%s:%d:%s: %s", file, line, fn, buf1);
#endif
Ns_Log (Notice, "%s", buf);
Ns_Free(buf);
Ns_Free(buf1);
} /* my_log */
/*
* Create a node_info structure.
*/
static node_info *
node_info_create(xmlNode *node, node_info *next)
{
node_info *n_info;
n_info = (node_info *)Ns_Malloc(sizeof(node_info));
n_info->node = node;
n_info->next = next;
return n_info;
}
/*
* Free a node_info structure
* created by node_info_create().
*/
static void
node_info_free(node_info *n_info)
{
Ns_Free(n_info);
}
static doc_info *
doc_info_create(xmlDoc *doc)
{
doc_info *d_info;
d_info = (doc_info *)Ns_Malloc(sizeof(doc_info));
d_info->doc = doc;
d_info->nodes = NULL;
#ifdef DO_XSLT
d_info->xslt = NULL;
#endif
my_log(lexpos(), "doc_info_create returns %x", d_info);
return d_info;
}
#ifdef DO_XSLT
static void xsltNsGenericErrorFunc( void *ctx, const char *msg, ...)
{
va_list args;
if (!debug_p)
return;
va_start(args, msg);
my_log(lexpos(), (char *)msg, args);
va_end(args);
}
#endif
#ifdef DO_XSLT
static doc_info *
doc_info_create_xslt(xsltStylesheetPtr xslt)
{
doc_info *d_info;
d_info = (doc_info *)Ns_Malloc(sizeof(doc_info));
d_info->doc = NULL;
d_info->nodes = NULL;
d_info->xslt = xslt;
my_log(lexpos(), "doc_info_create returns %x", d_info);
return d_info;
}
#endif
/*
* Frees the xmlDoc pointed to by the doc_info structure,
* the associated node_info data,
* and the doc_info structure itself.
*/
static void
doc_info_free(id_info *info, doc_info *d_info)
{
node_info *n_info, *next_n_info;
Tcl_HashEntry *entry;
char *node_id;
n_info = d_info->nodes;
my_log(lexpos(), "doc_info_free %x", d_info);
while (n_info != NULL) {
next_n_info = n_info->next;
/* Get the node ID for this node */
entry = Tcl_FindHashEntry(&info->node_hash_bynode, (ClientData)n_info->node);
/* The hash entry was already deleted, meaning we've got a straggler
n_info struct. free it and skip trying to free the hash entries
a second time. */
if (entry == NULL) {
node_info_free(n_info);
n_info = next_n_info;
continue;
}
node_id = (char *)Tcl_GetHashValue(entry);
Tcl_DeleteHashEntry(entry);
entry = Tcl_FindHashEntry(&info->node_hash_byid, node_id);
Tcl_DeleteHashEntry(entry);
Ns_Free(node_id);
node_info_free(n_info);
n_info = next_n_info;
}
/* Free the actual document and all its nodes */
my_log(lexpos(), "xmlFreeDoc %x", d_info->doc);
#ifdef DO_XSLT
if (d_info->doc)
xmlFreeDoc(d_info->doc);
if (d_info->xslt)
xsltFreeStylesheet(d_info->xslt);
#else
xmlFreeDoc(d_info->doc);
#endif
Ns_Free(d_info);
}
/*
* Create an id_info structure.
*/
static id_info *
id_info_create()
{
id_info *info;
info = (id_info *)Ns_Malloc(sizeof(id_info));
info->next_doc_id = 0;
info->next_node_id = 0;
Tcl_InitHashTable(&info->doc_hash_byid, TCL_STRING_KEYS);
Tcl_InitHashTable(&info->doc_hash_bydoc, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&info->node_hash_byid, TCL_STRING_KEYS);
Tcl_InitHashTable(&info->node_hash_bynode, TCL_ONE_WORD_KEYS);
return info;
}
/*
* Free an id_info structure
* created with id_info_create().
*/
static void
id_info_free(id_info *info)
{
Tcl_HashEntry *entry;
Tcl_HashSearch search;
/* Clean up the docs */
entry = Tcl_FirstHashEntry(&info->doc_hash_byid, &search);
while (entry) {
doc_info *d_info;
d_info = (doc_info *)Tcl_GetHashValue(entry);
doc_info_free(info, d_info);
entry = Tcl_NextHashEntry(&search);
}
Tcl_DeleteHashTable(&info->doc_hash_byid);
/* The bydoc hash table simply has pointers to the doc_info
* structures we just freed, so don't free them again.
*/
Tcl_DeleteHashTable(&info->doc_hash_bydoc);
/* There shouldn't be any nodes left in the node hash tables,
* but if there are, free the ID strings.
*/
entry = Tcl_FirstHashEntry(&info->node_hash_bynode, &search);
while (entry) {
Ns_Free(Tcl_GetHashValue(entry));
entry = Tcl_NextHashEntry(&search);
}
Tcl_DeleteHashTable(&info->node_hash_bynode);
/* The byid table has pointers to the nodes,
* which should bave been freed when the documents were freed.
*/
Tcl_DeleteHashTable(&info->node_hash_byid);
Ns_Free(info);
}
/*
* This function is called when an interpreter is
* about to be freed, and it cleans up all the data
* we are keeping that's related to that interpreter,
* such as the document id hash.
*/
static void
cleanup_interp_data(ClientData clientData, Tcl_Interp *interp)
{
id_info *info;
Tcl_HashEntry *entry;
my_log(lexpos(), "cleaning up interp %x", interp);
entry = Tcl_FindHashEntry(&interp_hash, (char *)interp);
if (entry != NULL) {
info = (id_info *)Tcl_GetHashValue(entry);
id_info_free(info);
Tcl_DeleteHashEntry(entry);
}
}
/*
* The connection is over.
* Clean up all non-persistent handles.
*/
static void
cleanup_after_connection(Tcl_Interp *interp, void *arg)
{
cleanup_interp_data(NULL, interp);
}
/*
* Get the info structure associated with an interpreter.
*/
static id_info *
get_interp_info(Tcl_Interp *interp)
{
Tcl_HashEntry *entry;
id_info *info;
int new;
Ns_LockMutex(&lock);
entry = Tcl_CreateHashEntry(&interp_hash, (ClientData)interp, &new);
if (new) {
my_log(lexpos(), "creating new interp info for %x", interp);
info = id_info_create();
Tcl_SetHashValue(entry, info);
Tcl_CallWhenDeleted(interp, cleanup_interp_data, entry);
} else {
my_log(lexpos(), "reusing interp info for %x", interp);
info = (id_info *)Tcl_GetHashValue(entry);
}
Ns_UnlockMutex(&lock);
return info;
}
/*
* Given an interpreter and a document id (e.g. "t0"),
* find the xmlDoc pointer for it.
* Returns NULL if not found.
*/
static doc_info *
xml_find_doc_info(Tcl_Interp *interp, char *docId)
{
id_info *info=NULL;
Tcl_HashEntry *entry;
doc_info *d_info;
if (docId[0] == 'p') {
info = perm_info;
} else if (docId[0] == 't') {
info = get_interp_info(interp);
}
if ( NULL == info )
return NULL;
entry = Tcl_FindHashEntry(&info->doc_hash_byid, docId);
if (entry == NULL)
return NULL;
d_info = (doc_info *)Tcl_GetHashValue(entry);
return d_info;
}
static xmlDocPtr
xml_find_doc(Tcl_Interp *interp, char *docId)
{
doc_info *info;
info = xml_find_doc_info(interp, docId);
if (NULL == info )
return NULL;
return info->doc;
}
#ifdef DO_XSLT
static xsltStylesheetPtr
xml_find_xslt(Tcl_Interp *interp, char *docId)
{
doc_info *info;
info = xml_find_doc_info(interp, docId);
if ( NULL == info)
return NULL;
return info->xslt;
}
#endif
/*
* Given an interpreter and a node id,
* find the xmlNode pointer for it.
* Returns NULL if not found.
*/
static xmlNodePtr
xml_find_node_byid(Tcl_Interp *interp, char *nodeId)
{
id_info *info;
Tcl_HashEntry *entry;
xmlNodePtr value;
if ( 'N' == nodeId[0] ) {
info = perm_info;
} else if ( 'n' == nodeId[0] ) {
info = get_interp_info(interp);
} else {
return NULL;
}
entry = Tcl_FindHashEntry(&info->node_hash_byid, nodeId);
if (entry == NULL)
return NULL;
value = (xmlNodePtr) Tcl_GetHashValue(entry);
if (value == NULL)
return NULL;
return value;
}
#ifdef NEVER /* function not used anywhere */
/*
* Given an interpreter and an xmlNode pointer,
* returns the node ID (e.g. "n0") for that node
* if it has already been entered for this interpreter.
* Returns NULL if not found.
*/
static char *
xml_find_id_bynode(Tcl_Interp *interp, xmlNodePtr node)
{
id_info *info;
Tcl_HashEntry *entry;
info = get_interp_info(interp);
entry = Tcl_FindHashEntry(&info->node_hash_bynode, (ClientData)node);
if (entry == NULL)
return NULL;
return (char *) Tcl_GetHashValue(entry);
}
#endif
#define ENTER_TRANSIENT 0
#define ENTER_PERSISTENT 1
/*
* Create a document ID string for a document
* and add it to the result for an interpreter.
* The argument determines whether it is marked as
* a transient document (ENTER_TRANSIENT), which will be cleaned up when
* the current transaction ends, or a persistent document
* (ENTER_PERSISTENT), which will persist across transactions
* and must be freed explicitly.
*/
#ifdef DO_XSLT
static void
xml_enter_doc_or_xslt(Tcl_Interp *interp, xmlDocPtr xmlDoc, xsltStylesheetPtr xslt, int flags, int append_result_p)
#else
static void
xml_enter_doc(Tcl_Interp *interp, xmlDocPtr xmlDoc, int flags, int append_result_p)
#endif
{
id_info *info;
Tcl_HashEntry *entry;
char prefix;
int new;
Ns_DString result;
doc_info *d_info;
Ns_DStringInit(&result);
if (flags & ENTER_PERSISTENT) {
/*
* Lock the global mutex and use the global hashtable.
*/
info = perm_info;
prefix = 'p';
} else {
info = get_interp_info(interp);
prefix = 't';
}
if (flags & ENTER_PERSISTENT) {
Ns_LockMutex(&lock);
}
do {
Ns_DStringTrunc(&result, 0);
Ns_DStringPrintf(&result, "%c%u", prefix, info->next_doc_id);
info->next_doc_id += 1;
entry = Tcl_CreateHashEntry(&info->doc_hash_byid, Ns_DStringValue(&result),
&new);
} while (!new);
#ifdef DO_XSLT
if (xslt)
d_info = doc_info_create_xslt(xslt);
else
d_info = doc_info_create(xmlDoc);
#else
d_info = doc_info_create(xmlDoc);
#endif
my_log(lexpos(), "set doc_hash_byid to %x", d_info);
Tcl_SetHashValue(entry, d_info);
#ifdef DO_XSLT
if (xmlDoc)
entry = Tcl_CreateHashEntry(&info->doc_hash_bydoc, (ClientData)xmlDoc, &new);
if (xslt)
entry = Tcl_CreateHashEntry(&info->doc_hash_bydoc, (ClientData)xslt, &new);
#else
entry = Tcl_CreateHashEntry(&info->doc_hash_bydoc, (ClientData)xmlDoc, &new);
#endif
if (new) {
my_log(lexpos(), "set doc_hash_bydoc to %x", d_info);
Tcl_SetHashValue(entry, (ClientData)d_info);
} else {
d_info = (doc_info *)Tcl_GetHashValue(entry);
}
if (flags & ENTER_PERSISTENT) {
Ns_UnlockMutex(&lock);
} else {
#ifdef NS_AOLSERVER_3_PLUS
Ns_TclRegisterDeferred(interp, cleanup_after_connection, NULL);
#else
Ns_DString cmd;
Ns_DStringInit(&cmd);
Ns_DStringPrintf(&cmd, "ns_atclose {ns_xml doc cleanup %s}", Ns_DStringValue(&result));
Tcl_Eval(interp, Ns_DStringValue(&cmd));
Ns_DStringFree(&cmd);
#endif
my_log( lexpos(), "entering doc value %s interp %x", Ns_DStringValue(&result),
interp);
}
if ( append_result_p ) {
Tcl_AppendResult(interp, Ns_DStringValue(&result), NULL);
Ns_DStringFree(&result);
}
}
#ifdef DO_XSLT
static void
xml_enter_doc(Tcl_Interp *interp, xmlDocPtr xmlDoc, int flags, int append_result_p)
{
xml_enter_doc_or_xslt(interp, xmlDoc, NULL, flags, append_result_p );
}
static void
xml_enter_xslt(Tcl_Interp *interp, xsltStylesheetPtr xslt, int flags, int append_result_p)
{
xml_enter_doc_or_xslt(interp, NULL, xslt, flags, append_result_p );
}
#endif
/*
* Constructs a node ID string (e.g. "n0") for the given
* node pointer and appends it to the result for this interpreter.
* The determination as to whether it is a temporary node
* or a persistent one is taken from the status of the document
* the node is attached to.
*/
static void
xml_enter_node(Tcl_Interp *interp, xmlNodePtr node)
{
id_info *info;
Tcl_HashEntry *node_entry, *id_entry, *doc_entry;
int new;
xmlDoc *doc;
doc_info *d_info;
node_info *n_info;
char prefix;
if (node == NULL)
return;
/* Determine if node is attached to a temporary
* or persistent doc
*/
doc = node->doc;
my_log(lexpos(), "node attached to doc %x", doc);
/* Look for temporary first */
info = get_interp_info(interp);
doc_entry = Tcl_FindHashEntry(&info->doc_hash_bydoc, (ClientData) doc);
my_log(lexpos(), "doc entry %x", doc_entry);
if (doc_entry != NULL) {
my_log(lexpos(), "doc is a temporary one");
prefix = 'n';
} else {
info = perm_info;
Ns_LockMutex(&lock);
doc_entry = Tcl_FindHashEntry(&info->doc_hash_bydoc, (ClientData) doc);
prefix = 'N';
my_log(lexpos(), "doc is persistent");
if (doc_entry == NULL) {
my_log(lexpos(), "doc not found");
Tcl_AppendResult(interp, "This should never happen", NULL);
return;
}
}
id_entry = Tcl_CreateHashEntry(&info->node_hash_bynode,
(ClientData) node, &new);
if (new) {
Ns_DString result;
Ns_DStringInit(&result);
do {
Ns_DStringTrunc(&result, 0);
Ns_DStringPrintf(&result, "%c%u", prefix, info->next_node_id);
info->next_node_id += 1;
node_entry = Tcl_CreateHashEntry(&info->node_hash_byid,
Ns_DStringValue(&result), &new);
} while (!new);
my_log( lexpos(), "Creating new node id %s", Ns_DStringValue(&result));
Tcl_SetHashValue(node_entry, node);
Tcl_AppendResult(interp, Ns_DStringValue(&result), NULL);
Tcl_SetHashValue(id_entry, Ns_StrDup(Ns_DStringValue(&result)));
Ns_DStringFree(&result);
d_info = Tcl_GetHashValue(doc_entry);
n_info = node_info_create(node, d_info->nodes);
d_info->nodes = n_info;
} else {
char *value;
value = (char *)Tcl_GetHashValue(id_entry);
Tcl_AppendResult(interp, value, NULL);
}
if (prefix == 'N') {
Ns_UnlockMutex(&lock);
}
}
/*
* Appends information about the location of a parse error
* to the Tcl result.
*/
static void
xml_append_file_info(Ns_DString *str, xmlParserInputPtr input) {
if (input != NULL) {
if (input->filename)
Ns_DStringPrintf(str, "%s:%d: ", input->filename,
input->line);
else
Ns_DStringPrintf(str, "Entity: line %d: ", input->line);
}
}
/*
* Appends information about the context of a parse error
* to the Tcl result.
*/
static void
xml_append_file_context(Ns_DString *str, xmlParserInputPtr input) {
const xmlChar *cur, *base;
int n;
char buf[2];
if (input == NULL) return;
buf[1] = '\0';
cur = input->cur;
base = input->base;
while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
cur--;
}
n = 0;
while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
cur--;
if ((*cur == '\n') || (*cur == '\r')) cur++;
base = cur;
n = 0;
while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
buf[0] = (unsigned char )*cur++;
Ns_DStringAppend(str, buf);
n++;
}
Ns_DStringAppend(str, "\n");
cur = input->cur;
while ((*cur == '\n') || (*cur == '\r'))
cur--;
n = 0;
while ((cur != base) && (n++ < 80)) {
Ns_DStringAppend(str, " ");
base++;
}
Ns_DStringAppend(str,"^\n");
}
/*
* Custom warning message function that is called from the
* XML parsing routine; it appends the message to the
* Tcl result.
*/
static void
xml_parse_warning(void *ctx, const char *msg, ...)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
xmlParserInputPtr input;
xmlParserInputPtr cur = NULL;
va_list args;
Tcl_Interp *interp = (Tcl_Interp *)ctxt->_private;
Ns_DString str;
char buf[4096];
Ns_DStringInit(&str);
input = ctxt->input;
if ((input != NULL) && (input->filename == NULL) && (ctxt->inputNr > 1)) {
cur = input;
input = ctxt->inputTab[ctxt->inputNr - 2];
}
xml_append_file_info(&str, input);
Ns_DStringAppend(&str, "warning: ");
va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
Ns_DStringAppend(&str, buf);
xml_append_file_context(&str, input);
if (cur != NULL) {
xml_append_file_info(&str, cur);
Ns_DStringAppend(&str, "\n");
xml_append_file_context(&str, cur);
}
Tcl_AppendResult(interp, Ns_DStringValue(&str), NULL);
Ns_DStringFree(&str);
}
/*
* Custom error message function that is called from the
* XML parsing routine; it appends the message to the
* Tcl result.
*/
static void
xml_parse_error(void *ctx, const char *msg, ...)
{
xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
xmlParserInputPtr input;
xmlParserInputPtr cur = NULL;
va_list args;
Tcl_Interp *interp = (Tcl_Interp *)ctxt->_private;
Ns_DString str;
char buf[4096];
my_log(lexpos(), "parse error");
Ns_DStringInit(&str);
input = ctxt->input;
if ((input != NULL) && (input->filename == NULL) && (ctxt->inputNr > 1)) {
cur = input;
input = ctxt->inputTab[ctxt->inputNr - 2];
}
xml_append_file_info(&str, input);
Ns_DStringAppend(&str, "error: ");
va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
Ns_DStringAppend(&str, buf);
xml_append_file_context(&str, input);
if (cur != NULL) {
xml_append_file_info(&str, cur);
Ns_DStringAppend(&str, "\n");
xml_append_file_context(&str, cur);
}
Tcl_AppendResult(interp, Ns_DStringValue(&str), NULL);
Ns_DStringFree(&str);
}
/*
* Retrieve an external entity (probably a DTD).
*
*/
static xmlParserInputPtr
xml_load_entity(const char *url, const char *id, xmlParserCtxtPtr ctxt)
{
xmlParserInputPtr input;
Ns_DString str;
int cc;
char *buf;
char *protocol, *host, *port, *path, *tail;
buf = Ns_StrDup((char *)url);
Ns_ParseUrl(buf, &protocol, &host, &port, &path, &tail);
Ns_DStringInit(&str);
if (protocol && strcmp(protocol, "http") == 0) {
cc = Ns_FetchURL(&str, (char *)url, NULL);
} else if (protocol == NULL || *protocol == '\0'
|| strcmp(protocol, "file") == 0) {
Ns_DString pathStr;
char *newPath;
if (path) {
Ns_DStringInit(&pathStr);
Ns_MakePath(&pathStr, path, tail, NULL);
newPath = Ns_DStringValue(&pathStr);
} else {
newPath = tail;
}
cc = Ns_FetchPage(&str, newPath, NULL);
if (path) {
Ns_DStringFree(&pathStr);
}
} else {
/* We must use Ns_Log here instead of appending a Tcl error
* because we don't have access to the interp pointer;
* we are in a sub-context of the parser context that has a valid
* userData2 pointer.
*
* There is a problem here: we can't really signal an error
* to the caller, since returning NULL will just result in this
* input being skipped.
*/
Ns_Log(Error,
"ns_xml: error fetching external entity '%s': unsupported protocol '%s'",
url,
protocol ? protocol : "(null)");
Ns_Free(buf);
return NULL;
}
Ns_Free(buf);
if (cc != NS_OK) {
/* Must use Ns_Log as noted above. */
Ns_Log(Error,
"ns_xml: error fetching external entity URL '%s'",
url);
return NULL;
}
input = xmlNewStringInputStream(ctxt, Ns_DStringExport(&str));
Ns_DStringFree(&str);
return input;
}
/*
* Parse an XML string and return a pointer
* to an xmlDoc.
* The validate flag determines whether the parser will
* validate against a DTD;
* if it is 0, it won't, if it's 1, it will,
* and if it is -1, it will use the parser's default behavior.
* Returns NULL if there was an error and appends
* the error message to the Tcl result.
*/
static xmlDocPtr
xml_parse(Tcl_Interp *interp, char *buffer, int validate) {
xmlDocPtr ret;
xmlParserCtxtPtr ctxt;
my_log(lexpos(), "xml_parse");
if (buffer == NULL) return(NULL);
ctxt = xmlCreateDocParserCtxt(buffer);
if (ctxt == NULL) return(NULL);
ctxt->_private = interp;
/* Use our own custom error functions */
ctxt->sax->warning = xml_parse_warning;
ctxt->sax->error = xml_parse_error;
ctxt->sax->fatalError = xml_parse_error;
ctxt->vctxt.warning = xml_parse_warning;
ctxt->vctxt.error = xml_parse_error;
if (validate >= 0) {
ctxt->validate = (validate > 0);
}
xmlParseDocument(ctxt);
if (ctxt->wellFormed && ctxt->valid)
ret = ctxt->myDoc;
else {
ret = NULL;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = NULL;
if (!ctxt->wellFormed) {
Tcl_AppendResult(interp, "document was not well formed\n", NULL);
} else if (!ctxt->valid) {
Tcl_AppendResult(interp, "document failed validation\n", NULL);
}
}
xmlFreeParserCtxt(ctxt);
return(ret);
}
/*
NOTE: This doesn't actually free the n_info structs for this or any
children that may exist. For performance/simplicity reasons, we just freed
the hash entries and got rid of the underlying libxml data. When
doc_info_free is called, the n_info structs for these defunct nodes
are removed correctly, skipping removal of the already gone hash entries.
The tradeoff here is a potential bunch of garbage n_info's piling
up if you repeatedly create huge trees and free their nodes. The number
of n_info's will only increase until the document itself is freed. This
could be bad for a persistent document that undergoes a lot of modification
*/
static void
node_unlink (Tcl_Interp *interp, xmlNodePtr node) {
Tcl_HashEntry *entry;
id_info *info;
int new;
char *node_id;
/* first try the interp */
info = get_interp_info(interp);
entry = Tcl_FindHashEntry(&info->node_hash_bynode, (ClientData) node);
if (entry == NULL) {
/* doc must be permanent */
info = perm_info;
entry = Tcl_FindHashEntry(&info->node_hash_bynode, (ClientData) node);
}
node_id = (char *)Tcl_GetHashValue(entry);
/* unlink the node from the tree */
xmlUnlinkNode(node);
/* Lose the hash entries */
Tcl_DeleteHashEntry(entry);
entry = Tcl_FindHashEntry(&info->node_hash_byid, (ClientData) node_id);
Tcl_DeleteHashEntry(entry);
Ns_Free(node_id);
}
/*
* The "ns_xml string" command, which performs all manipulations
* on input strings (for now, just parsing).
*/
static int
xml_string_command (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[]) {
if (argc < 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "parse") == 0) {
int flag;
int validate = 0;
char *xml_string = "";
char **argp;
xmlDocPtr doc;
flag = ENTER_TRANSIENT;
argp = &argv[3];
while (argc >= 3) {
argc--;
if (strcmp(*argp, "-persist") == 0) {
flag = ENTER_PERSISTENT;
} else if (strcmp(*argp, "-validate") == 0) {
validate = 1;
} else {
xml_string = *argp;
break;
}
argp++;
}
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "xml") == 0) {
doc = xml_parse(interp, xml_string, validate);
if (doc == NULL) {
Tcl_AppendResult(interp, "error while parsing XML", NULL);
return TCL_ERROR;
}
xml_enter_doc(interp, doc, flag, NS_TRUE);
} else if (strcmp(argv[2], "html") == 0) {
doc = (xmlDocPtr)htmlParseDoc(xml_string, NULL);
if (doc == NULL) {
Tcl_AppendResult(interp, "error while parsing XML", NULL);
return TCL_ERROR;
}
xml_enter_doc(interp, doc, flag, NS_TRUE);
#ifdef DO_XSLT
} else if (strcmp(argv[2], "xsl") == 0) {
xsltStylesheetPtr xslt=NULL;
doc = xml_parse(interp, xml_string, validate);
if (doc == NULL) {
Tcl_AppendResult(interp, "error while parsing XML", NULL);
return TCL_ERROR;
}
xslt = xsltParseStylesheetDoc(doc);
if ( xslt == NULL ) {
Tcl_AppendResult(interp, "error while parsing style sheet",NULL);
}
xml_enter_xslt(interp, xslt, flag, NS_TRUE);
if (xslt->indent == 1)
xmlIndentTreeOutput = 1;
else
xmlIndentTreeOutput = 0;
if (strcmp (xslt->method, "html") == 0)
xmlSaveNoEmptyTags = 1;
#endif
} else {
Tcl_AppendResult(interp, "unknown document type ", argv[2], NULL);
return TCL_ERROR;
}
} else {
Tcl_AppendResult(interp, "unknown command", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* The "ns_xml create" command. The only command with no subject.
* Used to conjure up an empty document from nothing. Currently
* only supports XML (as opposed to XSL or DTDs or XSchemae or what have you).
*/
static int
xml_create_command (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[]) {
int flag;
char *version = "1.0";
char **argp;
xmlDocPtr doc;
if (argc < 2) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
flag = ENTER_TRANSIENT;
argp = &argv[2];
while (argc >= 3) {
argc--;
if (strcmp(*argp, "-persist") == 0) {
flag = ENTER_PERSISTENT;
} else {
version = *argp;
break;
}
argp++;
}
if (argc != 2) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "xml") == 0) {
doc = xmlNewDoc(version);
if (doc == NULL) {
Tcl_AppendResult(interp, "error allocating new doc", NULL);
return TCL_ERROR;
}
xml_enter_doc(interp, doc, flag, NS_TRUE);
} else {
Tcl_AppendResult(interp, "unknown document type ", argv[1], NULL);
return TCL_ERROR;
}
return TCL_OK;
}
#ifdef DO_XSLT
/*
* The "ns_xml transform" command
* applies and XSL stylesheet to an XML document
*/
static int
xml_transform_command( ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[] ) {
const char *params[MAX_PARAMS+1];
int nbparams = 0;
xmlDocPtr res, doc;
xsltStylesheetPtr xslt;
int flags = ENTER_TRANSIENT;
char **arg_ptr = &argv[1];
my_log(lexpos(), "arg num: %d", argc);
while (argc > 3) {
if (strcmp(*arg_ptr, "-persist") == 0) {
flags = ENTER_PERSISTENT;
arg_ptr++;
argc--;
} else if (strcmp(*arg_ptr, "-param") == 0) {
if (nbparams + 2 > MAX_PARAMS) {
Tcl_AppendResult(interp, "too many params" , NULL);
return TCL_ERROR;
}
*arg_ptr++;
params[nbparams++] = *arg_ptr++;
params[nbparams++] = *arg_ptr++;
argc -= 3;
} else {
Tcl_AppendResult(interp, "unknown option ", *arg_ptr, NULL);
return TCL_ERROR;
}
}
if ( argc < 3 ) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
my_log(lexpos(), "xslt %s xml %s", arg_ptr[1], arg_ptr[0]);
xslt = xml_find_xslt(interp, arg_ptr[1] );
if ( NULL == xslt ) {
Tcl_AppendResult(interp, "invalid xslt doc ID ", arg_ptr[1], NULL );
return TCL_ERROR;
}
doc = xml_find_doc(interp, arg_ptr[0] );
if ( NULL == doc ) {
Tcl_AppendResult(interp, "invalid xml doc ID ", arg_ptr[0], NULL );
return TCL_ERROR;
}
params[nbparams] = NULL;
res = xsltApplyStylesheet( xslt, doc, params);
if ( NULL == res ) {
Tcl_AppendResult(interp, "error applying stylesheet", NULL );
return TCL_ERROR;
}
xml_enter_doc(interp, res, flags, NS_TRUE );
return TCL_OK;
}
#endif
/*
* The "ns_xml doc" tcl command.
*/
static int
xml_doc_command (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[])
{
if (argc < 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "get") == 0) {
if (argc < 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "root") == 0) {
xmlDoc *doc;
my_log(lexpos(), "find root of %s", argv[3]);
doc = xml_find_doc(interp, argv[3]);
if (doc == NULL) {
Tcl_AppendResult(interp, "invalid doc ID ", argv[3], NULL);
return TCL_ERROR;
}
my_log(lexpos(), "enter node");
xml_enter_node(interp, (xmlNode *)doc);
} else {
Tcl_AppendResult(interp, "unable to get ", argv[2], NULL);
}
} else if (strcmp(argv[1], "create") == 0) {
if (argc < 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "root") == 0) {
xmlDocPtr xmlDoc;
xmlNodePtr root;
if (argc != 6) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
xmlDoc = xml_find_doc(interp, argv[3]);
if (xmlDoc == NULL) {
Tcl_AppendResult(interp, "invalid doc ID ", argv[3], NULL);
return TCL_ERROR;
}
root = xmlNewDocNode(xmlDoc, NULL, argv[4], argv[5]);
xmlDocSetRootElement(xmlDoc, root);
xml_enter_node(interp, root);
} else {
int i;
char *args[argc-1];
/* This must have been a 1.x call (ns_xml doc create) mapping to "ns_xml create xml" */
args[0] = "create";
args[1] = "xml";
for (i = 2; i < (argc-1); i++) {
args[i] = argv[i+1];
}
return xml_create_command (dummy, interp, argc-1, args);
}
/* "ns_xml doc free" is deprecated. use "ns_xml doc delete" instead. */
} else if (strcmp(argv[1], "root") == 0) {
int i;
char *args[argc+1];
/* This was the 1.x call (ns_xml doc root) mapping to "ns_xml doc get root" */
args[0] = "doc";
args[1] = "get";
for (i = 2; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_doc_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "free") == 0 ||
strcmp(argv[1], "delete") == 0 ||
strcmp(argv[1], "cleanup") == 0) {
char *docId;
id_info *info;
Tcl_HashEntry *entry;
doc_info *d_info;
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
docId = argv[2];
my_log(lexpos(), "freeing doc %s interp %x", docId, interp);
if (docId[0] == 'p') {
info = perm_info;
} else if (docId[0] == 't') {
info = get_interp_info(interp);
} else {
Tcl_AppendResult(interp, "invalid xml doc ID ", docId, NULL);
return TCL_ERROR;
}
my_log(lexpos(), "free or cleanup %s %s", docId, interp);
entry = Tcl_FindHashEntry(&info->doc_hash_byid, docId);
if (entry == NULL) {
if (argv[1][0] == 'c') {
/* "cleanup" command is tolerant of errors */
return TCL_OK;
}
Tcl_AppendResult(interp, "invalid xml doc ID ", docId, NULL);
return TCL_ERROR;
}
d_info = (doc_info *)Tcl_GetHashValue(entry);
if (d_info == NULL) {
Tcl_AppendResult(interp, "hash table screwup", NULL);
return TCL_ERROR;
}
my_log(lexpos(), "got d_info %x", d_info);
Tcl_DeleteHashEntry(entry);
entry = Tcl_FindHashEntry(&info->doc_hash_bydoc, (ClientData)d_info->doc);
Tcl_DeleteHashEntry(entry);
doc_info_free(info, d_info);
} else if (strcmp(argv[1], "new_root") == 0) {
int i;
char *args[argc+1];
/* Remap this to the correct call: "ns_xml doc create root" */
args[0] = "doc";
args[1] = "create";
args[2] = "root";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_doc_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "render") == 0) {
xmlDocPtr xmlDoc;
xmlChar *buf;
int size;
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
xmlDoc = xml_find_doc(interp, argv[2]);
if (xmlDoc == NULL) {
Tcl_AppendResult(interp, "invalid doc ID ", argv[2], NULL);
return TCL_ERROR;
}
xmlDocDumpFormatMemoryEnc(xmlDoc, &buf, &size, NULL, 1);
Tcl_SetResult(interp, buf, (Tcl_FreeProc *)Ns_Free);
} else {
Tcl_AppendResult(interp, "unknown command", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* Returns a static pointer to
* the XML type name for a type number.
*/
static char *
xml_type_name(xmlElementType type)
{
static char *type_names[] = {
"element",
"attribute",
"text",
"cdata_section",
"entity_ref",
"entity",
"pi",
"comment",
"document",
"document_type",
"document_frag",
"notation",
"html_document",
"dtd",
"element_decl",
"attribute_decl",
"entity_decl",
"namespace_decl",
"xinclude_start",
"xinclude_end",
"docb_document_node"
};
if ((int)type < 0 || (int)type >= (sizeof(type_names) / sizeof(char *))) {
return "unknown";
}
return type_names[(int)type];
}
/*
* The "ns_xml node" Tcl command.
*/
static int
xml_node_command (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[])
{
if (strcmp(argv[1], "selectnodes") == 0) {
xmlNode *node = NULL;
xmlXPathContext *xptr = NULL;
xmlXPathObject *xptrobject = NULL;
int i;
if (argc < 4 || strlen(argv[3]) == 0) {
Tcl_AppendResult(interp, "bad # args.", NULL);
return TCL_OK;
}
node = xml_find_node_byid(interp, argv[2]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[2], NULL);
return TCL_ERROR;
}
xptr = xmlXPathNewContext(node->doc);
xptr->node = node;
xptrobject = xmlXPathEval(argv[3], xptr);
if (xptrobject == NULL) {
Tcl_AppendResult(interp, "invalid XPath Expression => ", argv[3], NULL);
xmlXPathFreeContext(xptr);
xmlXPathFreeObject(xptrobject);
return TCL_ERROR;
}
switch (xptrobject->type) {
case XPATH_UNDEFINED: Tcl_AppendResult(interp, "", NULL); break;
case XPATH_NODESET:
if (xptrobject->nodesetval != NULL) {
for (i = 0; i < xptrobject->nodesetval->nodeNr; i++) {
if (i > 0) { Tcl_AppendResult(interp, " ", NULL); }
xml_enter_node(interp, xptrobject->nodesetval->nodeTab[i]);
}
} else {
Tcl_AppendResult(interp, "", NULL);
}
break;
case XPATH_BOOLEAN: Tcl_AppendResult(interp, xmlXPathCastBooleanToString(xptrobject->boolval), NULL); break;
case XPATH_NUMBER: Tcl_AppendResult(interp, xmlXPathCastNumberToString(xptrobject->floatval), NULL); break;
case XPATH_STRING: Tcl_AppendResult(interp, xmlXPathCastToString(xptrobject), NULL); break;
default: Tcl_AppendResult(interp, "", NULL);
}
xmlXPathFreeContext(xptr);
xmlXPathFreeObject(xptrobject);
} else if (strcmp(argv[1], "get") == 0) {
if (argc < 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "children") == 0) {
xmlNodePtr node;
int i;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
for (i = 0, node=node->children; node != NULL; i++, node = node->next) {
if (i > 0) {
Tcl_AppendResult(interp, " ", NULL);
}
xml_enter_node(interp, node);
}
} else if (strcmp(argv[2], "attr_names") == 0) {
xmlNodePtr node;
xmlAttrPtr attr;
int i;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
if (node->type == XML_ELEMENT_NODE) {
for (i = 0, attr=node->properties; attr != NULL; i++, attr = attr->next) {
if (i > 0) {
Tcl_AppendResult(interp, " ", NULL);
}
Tcl_AppendResult(interp, attr->name, NULL);
}
}
} else if (strcmp(argv[2], "name") == 0) {
xmlNode *node;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
Tcl_AppendResult(interp, node->name, NULL);
} else if (strcmp(argv[2], "type") == 0) {
xmlNodePtr node;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
Tcl_SetResult(interp, xml_type_name(node->type), TCL_STATIC);
} else if (strcmp(argv[2], "parent") == 0) {
xmlNodePtr node;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
xml_enter_node(interp, node->parent);
} else if (strcmp(argv[2], "attr") == 0) {
xmlNode *node;
char *value;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
value = xmlGetProp(node, argv[4]);
Tcl_SetResult(interp, value, (Tcl_FreeProc *)Ns_Free);
} else if (strcmp(argv[2], "content") == 0) {
xmlNode *node;
char *value;
if (argc != 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
value = xmlNodeGetContent(node);
Tcl_SetResult(interp, value, (Tcl_FreeProc *)Ns_Free);
} else {
Tcl_AppendResult(interp, "unknown command",NULL);
}
} else if (strcmp(argv[1], "create") == 0) {
if (argc < 4) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "child_node") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 6) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewChild(node, NULL, argv[4], argv[5]);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[2], "child_text") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocText(node->doc,argv[4]);
xmlAddChild(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[2], "prev_sibling_node") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 6) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocNode(node->doc, NULL, argv[4], argv[5]);
xmlAddPrevSibling(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[2], "prev_sibling_text") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocText(node->doc, argv[4]);
xmlAddPrevSibling(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[2], "next_sibling_node") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 6) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocNode(node->doc, NULL, argv[4], argv[5]);
xmlAddNextSibling(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[2], "next_sibling_text") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocText(node->doc, argv[4]);
xmlAddNextSibling(node, newNode);
xml_enter_node(interp, newNode);
} else {
Tcl_AppendResult(interp, "invalid node creation method ", argv[2], NULL);
return TCL_ERROR;
}
} else if (strcmp(argv[1], "clone") == 0) {
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[2]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[2], NULL);
}
newNode = xmlCopyNode(node,1);
xmlAddNextSibling(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[1], "relink_as") == 0) {
xmlNodePtr node;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "child") == 0) {
xmlNodePtr parent;
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
parent = xml_find_node_byid(interp, argv[4]);
if (parent == NULL) {
Tcl_AppendResult(interp, "invalid new parent node ID ", argv[4], NULL);
return TCL_ERROR;
}
node_unlink(interp, node);
xmlAddChild(parent, node);
xml_enter_node(interp, node);
} else if (strcmp(argv[2], "prev_sibling") == 0) {
xmlNodePtr sibling;
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
sibling = xml_find_node_byid(interp, argv[4]);
if (sibling == NULL) {
Tcl_AppendResult(interp, "invalid new sibling node ID ", argv[4], NULL);
return TCL_ERROR;
}
node_unlink(interp, node);
xmlAddPrevSibling(sibling, node);
xml_enter_node(interp, node);
} else if (strcmp(argv[2], "next_sibling") == 0) {
xmlNodePtr prev_sibling;
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
prev_sibling = xml_find_node_byid(interp, argv[4]);
if (prev_sibling == NULL) {
Tcl_AppendResult(interp, "invalid new sibling node ID ", argv[4], NULL);
return TCL_ERROR;
}
node_unlink(interp, node);
xmlAddNextSibling(prev_sibling, node);
xml_enter_node(interp, node);
} else {
Tcl_AppendResult(interp, "unknown relink location ",argv[2],NULL);
return TCL_ERROR;
}
} else if (strcmp(argv[1], "set") == 0) {
if (argc < 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "attr") == 0) {
xmlNode *node;
if (argc != 6) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
(void) xmlSetProp(node, argv[4], argv[5]);
} else if (strcmp(argv[2], "content") == 0) {
xmlNode *node;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[3], NULL);
return TCL_ERROR;
}
(void) xmlNodeSetContent(node, argv[4]);
} else {
Tcl_AppendResult(interp, "cannot set ", argv[2],NULL);
return TCL_ERROR;
}
} else if (strcmp(argv[1], "unset") == 0) {
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[2], "attr") == 0) {
xmlNode *node;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[3]);
xmlUnsetProp(node, argv[4]);
} else {
Tcl_AppendResult(interp, "cannot unset ", argv[2],NULL);
return TCL_ERROR;
}
} else if (strcmp(argv[1], "children") == 0) {
int i;
char *args[argc+1];
/* the 1.x call mapping to "ns_xml node get children" (deprecated) */
args[0] = "node";
args[1] = "get";
for (i = 2; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "name") == 0) {
int i;
char *args[argc+1];
/* 1.x call (ns_xml node name) maps to "ns_xml node get name" */
args[0] = "node";
args[1] = "get";
for (i = 2; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command(dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "type") == 0) {
int i;
char *args[argc+1];
/* 1.x call (ns_xml node type) maps to "ns_xml node get type" */
args[0] = "node";
args[1] = "get";
for (i = 2; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command(dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "getcontent") == 0) {
int i;
char *args[argc+1];
/* 1.x call (ns_xml node getcontent) maps to "ns_xml node get content" */
args[0] = "node";
args[1] = "get";
args[2] = "content";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command(dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "setcontent") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml node setcontent) maps to "ns_xml node set content" */
args[0] = "node";
args[1] = "set";
args[2] = "content";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "getattr") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml node getattr) maps to "ns_xml node get attr" */
args[0] = "node";
args[1] = "get";
args[2] = "attr";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "setattr") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml node setattr) maps to "ns_xml node set attr" */
args[0] = "node";
args[1] = "set";
args[2] = "attr";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "new_child") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml node new_child) maps to "ns_xml node create child_node" */
args[0] = "node";
args[1] = "create";
args[2] = "child_node";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_node_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "new_sibling") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml node new_sibling) HAS NO MAPPING. It is well and truely deprecated, so we need to implement it specially */
/* From now on, either add next_siblings to the last sibling or children to the parent to achieve the same effect */
xmlNodePtr node;
xmlNodePtr newNode;
if (argc != 5) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[2]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[2], NULL);
return TCL_ERROR;
}
newNode = xmlNewDocNode(node->doc, NULL, argv[3], argv[4]);
xmlAddSibling(node, newNode);
xml_enter_node(interp, newNode);
} else if (strcmp(argv[1], "delete") == 0) {
xmlNode *node;
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[2]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[2], NULL);
return TCL_ERROR;
}
node_unlink(interp, node);
xmlFreeNode(node);
} else if (strcmp(argv[1], "render") == 0) {
xmlNodePtr node;
xmlBufferPtr buf;
buf = xmlBufferCreate();
if (argc != 3) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
node = xml_find_node_byid(interp, argv[2]);
if (node == NULL) {
Tcl_AppendResult(interp, "invalid node ID ", argv[2], NULL);
return TCL_ERROR;
}
xmlNodeDump(buf, node->doc, node, 0, 0);
Tcl_SetResult(interp, buf->content, TCL_VOLATILE);
xmlBufferFree(buf);
} else {
Tcl_AppendResult(interp, "unknown command", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* Debugging function to print out statistics
* on the various hash tables used internally
* by the xml-module.
*/
static int
xml_info_stats(id_info *info)
{
char *message;
message = Tcl_HashStats(&info->doc_hash_byid);
Ns_Log(Notice, "doc_hash_byid--> %s", message);
ckfree(message);
message = Tcl_HashStats(&info->doc_hash_bydoc);
Ns_Log(Notice, "doc_hash_bydoc--> %s", message);
ckfree(message);
message = Tcl_HashStats(&info->node_hash_bynode);
Ns_Log(Notice, "node_hash_byid--> %s", message);
ckfree(message);
message = Tcl_HashStats(&info->node_hash_bynode);
Ns_Log(Notice, "node_hash_bynode--> %s", message);
ckfree(message);
return TCL_OK;
}
/*
* The Tcl command for printing internal hash table
* statistics to the AOLserver log.
*/
static int
xml_stats (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[])
{
Tcl_HashEntry *entry;
Tcl_HashSearch search;
char *message;
Ns_LockMutex(&lock);
message = Tcl_HashStats(&interp_hash);
Ns_Log(Notice, "** interp hash: %s", message);
ckfree(message);
Ns_Log(Notice, "** perm_info **");
xml_info_stats(perm_info);
entry = Tcl_FirstHashEntry(&interp_hash, &search);
while (entry) {
Ns_Log(Notice, "** interp %x **", Tcl_GetHashKey(&interp_hash, entry));
/* xml_info_stats(Tcl_GetHashValue(entry));*/
entry = Tcl_NextHashEntry(&search);
}
Ns_UnlockMutex(&lock);
return TCL_OK;
}
/*
* The ns_xml Tcl command.
*/
static int
xml_tcl_command (ClientData dummy, Tcl_Interp *interp,
int argc, char *argv[])
{
xmlDocPtr xmlDoc;
my_log( lexpos(), "xml_tcl_command: %d %s", argc, argv[1]);
if (argc < 2) {
Tcl_AppendResult(interp, "bad # of args", NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "create") == 0) {
return xml_create_command(dummy, interp, argc-1, argv+1);
} else if (strcmp(argv[1], "string") == 0) {
return xml_string_command(dummy, interp, argc-1, argv+1);
} else if (strcmp(argv[1], "parse") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml parse) maps to "ns_xml string parse xml" */
args[0] = "string";
args[1] = "parse";
args[2] = "xml";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_string_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "doc") == 0) {
return xml_doc_command(dummy, interp, argc-1, argv+1);
} else if (strcmp(argv[1], "node") == 0) {
return xml_node_command(dummy, interp, argc-1, argv+1);
} else if (strcmp(argv[1], "stats") == 0) {
return xml_stats(dummy, interp, argc-1, argv+1);
#ifdef DO_XSLT
} else if (strcmp(argv[1], "transform") == 0) {
return xml_transform_command(dummy, interp, argc-1, argv+1);
} else if (strcmp(argv[1], "parse_xslt") == 0) {
int i;
char *args[argc+1];
/* the 1.x call (ns_xml parse_xslt) maps to "ns_xml string parse xsl" */
args[0] = "string";
args[1] = "parse";
args[2] = "xsl";
for (i = 3; i < (argc+1); i++) {
args[i] = argv[i-1];
}
return xml_string_command (dummy, interp, argc+1, args);
} else if (strcmp(argv[1], "apply_xslt") == 0) {
/* the 1.x call (ns_xml apply_xslt) maps to "ns_xml transform" */
if (argc == 5) {
char *args[] = {argv[1],argv[2],argv[4],argv[3]};
return xml_transform_command (dummy, interp, 4, args);
} else if (argc == 4) {
char *args[] = {argv[1],argv[3],argv[2]};
return xml_transform_command (dummy, interp, 3, args);
} else {
Tcl_AppendResult(interp, "bad # of args");
return TCL_ERROR;
}
#endif
} else {
Tcl_AppendResult(interp, "unknown command", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* Called during the initialization of a new interpreter;
* adds our "ns_xml" Tcl command to the interpreter.
*/
static int
xml_interp_init (Tcl_Interp *interp, void *dummy)
{
Tcl_CreateCommand (interp, "ns_xml", xml_tcl_command, NULL, NULL);
return NS_OK;
}
/*
* Called when the module is loaded.
*/
extern int xmlSaveNoEmptyTags;
NS_EXPORT int
Ns_ModuleInit(char *hServer, char *hModule)
{
char *configPath;
Ns_Log( Notice, "%s module starting", hModule);
configPath = Ns_ConfigGetPath(hServer, hModule, NULL);
if (!Ns_ConfigGetBool (configPath, "Debug", &debug_p))
debug_p = DEFAULT_DEBUG;
if (!Ns_ConfigGetBool (configPath, "SaveEmptyTags", &xmlSaveNoEmptyTags))
xmlSaveNoEmptyTags = 0;
perm_info = id_info_create();
if ( NULL == perm_info ) {
Ns_Log( Notice, "couldn't create perm_info in nsxml" );
return NS_ERROR;
}
Tcl_InitHashTable(&interp_hash, TCL_ONE_WORD_KEYS);
Ns_InitializeMutex(&lock);
xmlMemSetup ((xmlFreeFunc) Ns_Free,
(xmlMallocFunc) Ns_Malloc,
(xmlReallocFunc) Ns_Realloc,
(xmlStrdupFunc) Ns_StrDup);
xmlInitMemory();
xmlSetExternalEntityLoader(xml_load_entity);
xmlInitParser();
#ifdef DO_XSLT
xmlSubstituteEntitiesDefault(1);
xsltSetGenericErrorFunc(NULL, xsltNsGenericErrorFunc);
#endif
Ns_TclInitInterps (hServer, xml_interp_init, NULL);
return NS_OK;
}
/* Required to identify the module version to AOLserver. */
NS_EXPORT int Ns_ModuleVersion = 1;
nsxml-1.5/configure-stamp 0000644 0001750 0001750 00000000000 10054735654 016637 0 ustar giskard giskard 0000000 0000000 nsxml-1.5/build-stamp 0000644 0001750 0001750 00000000000 10054735655 015756 0 ustar giskard giskard 0000000 0000000