#include <xml/pathnode.h>
#include <string.h>
#include <stdlib.h>

/* cater for pre-C99 compilers */
#define ___ {
#define ____ }

#ifdef NO_UTF8
#define g_utf8_get_char(S)  ((gunichar)*(guchar*)(S))
#undef  g_utf8_next_char
#define g_utf8_next_char(S) ((S)+1)
#define g_unichar_isalnum(c) g_ascii_isalnum((c))
#endif

/*
   the xpath syntax returned and searched here is strictly abbreviated.
   It is always "/name" as "/child::name" and "[2]" = "[position() = 2]"
   Anything else gets rejected as illegal - a name matches [\w\:\.\_\-].
 */
static gboolean
is_name_char (gunichar c)
{
  if (g_unichar_isalnum (c) ||
      c == '.' ||
      c == '-' ||
      c == '_' ||
      c == ':')
    return TRUE;
  else
    return FALSE;
}
static const gchar*
advance_to_name_end (const gchar* str)
{
    do 
{
	if (! is_name_char (g_utf8_get_char (str)))
	    return str;
	str = g_utf8_next_char (str);
    }
while (*str); return str; }
/* ______________________________________________________________ */
xml_GNode*
xml_tree_find_node (xml_GNode* tree, gsize off)
{
    if (tree->text)
    
{
	if (tree->off > off) return 0;
	if (tree->end < off) return 0;
    }
recurse: if (! tree->children) return tree; ___ xml_GNode* node; for (node = tree->children; node ; node = node->next)
{
	if (! node->text) continue;
	if (node->off > off) return tree;
	if (node->end >= off) 
{ tree = node; goto recurse; }
/* (node->end >= off) return xml_tree_find_node (node, off); */ }
____; return tree; }
xml_GNode*
xml_tree_find_node2 (xml_GNode* tree, gsize off1, gsize off2)
{
    if (tree->text)
    
{
	if (tree->off > off1) return 0;
	if (tree->off > off2) return 0;
	if (tree->end < off1) return 0;
	if (tree->end < off2) return 0;
    }
recurse: if (! tree->children) return tree; ___ xml_GNode* node; for (node = tree->children; node ; node = node->next)
{
	if (! node->text) continue;
	if (node->off > off1) return tree;
	if (node->off > off2) return tree;
	if (node->end >= off1 && node->end >= off2) 
	
{ tree = node; goto recurse; }
}
____; return tree; }
xml_GNode*
xml_tree_find_nodes (xml_GNode* tree, gsize* offs, gsize n)
{
    int i;
    if (tree->text)
    
{
	for (i=0;i<n;i++) if (tree->off > offs[i]) return 0;
	for (i=0;i<n;i++) if (tree->end < offs[i]) return 0;
    }
recurse: if (! tree->children) return tree; ___ xml_GNode* node; for (node = tree->children; node ; node = node->next)
{
	if (! node->text) continue;
	for (i=0;i<n;i++) if (node->off > offs[i]) return tree;
	for (i=0;i<n;i++) if (node->end < offs[i]) goto continues;
	tree = node; goto recurse;
    continues:
	continue;
    }
____; return tree; }
/* ______________________________________________________________ */
gchar*
xml_path_strdup (xml_GNode* tree, xml_GNode* node, xml_path_flags_t flag)
{
    if (! tree || ! node || ! node->name) return 0;
    ___ GString* path = g_string_new (0);

    for (; node ; node = node->parent)
    
{
	if (path->len) g_string_prepend_c (path, '/');
	if (flag) /* (flag&xml_path_SERIALS) */
	
{
	    int brothers = 0;
	    xml_GNode* look = node->prev; 
	    for (; look ; look = look->prev)
	    
{
		if (! look->name) continue;
		if (look->name[0] == '<') continue;
		if (! strcmp (look->name, node->name)) brothers++;
	    }
if (brothers || xml_path_STRICT&flag)
{
		gchar* serial = g_strdup_printf ("[%i]", brothers+1);
		g_string_prepend (path, serial); g_free (serial);
	    }
}
if (node->name) g_string_prepend (path, node->name); if (path->str[0] == ':' && ! is_name_char (path->str[1])) path->str[0] = '/'; /* virtual root */ if (node == tree) return g_string_free (path, 0); }
return g_string_free (path, 1); ____; }
xml_GNode*
xml_path_node (xml_GNode* tree, const gchar* xpath)
{
    if (! tree || ! xpath) return 0;
    ___ xml_GNode* node = tree;

    if (xpath[0] == '.') xpath++;
    if (xpath[0] == '/') xpath++;
    if (xpath[0] == '/') xpath++; /* wrong!! should match any descendant */

 again:
    ___ const gchar* end = advance_to_name_end (xpath);
    if (end == xpath) return (*end ? 0 : node);

    ___ int serial = 0;
    if (end[0] == '[' && g_ascii_isdigit (end[1]))
	serial = strtol (&end[1], 0, 0);

    if (0) 
{
	if (serial)
	    g_printerr ("<%.*s[%i]>", end-xpath, xpath, serial);
	else
	    g_printerr ("<%.*s>", end-xpath, xpath);
    }
if (serial < 1) serial = 1; node = node->children; for (; node ; node = node->next)
{
	if (! node->name) continue;
	if (! strncmp (node->name, xpath, end-xpath))
	
{
	    if (! --serial) break; /* found it */
	}
}
____; if (! node) return node; if (end[0] == '[')
{
	end++; while (g_ascii_isalnum(*end)) end++;
	if (end[0] != ']') return 0; else end++;
    }
if (end[0] == 0) return node; /* final ! */ if (end[0] != '/') return 0; xpath = end+1; goto again; ____;____; }
/* 
   Local variables:
   c-file-style: "stroustrup"
   End:
 */