#include <xml/pdoc-cpp.h>
#include <xml/nextpcre.h>
#include <xml/nextnode.h>
#include <xml/addspcre.h>
#include <xml/addsnode.h>
#include <xml/listnode.h>
#include <xml/savetree.h>
#include <xml/attrnode.h>
#include <string.h>
#include <stdlib.h>

#define ___ {
#define ____ }


static void xml_pdoc_c_doc_ (xml_GNode* node, gpointer data)
{           xml_pdoc_c_doc (node); }
xml_GNode*  xml_pdoc_c_docs (xml_GNode* tree)
{  
    xml_path_node_foreach (tree, "//*comment", xml_pdoc_c_doc_, 0); 
    return tree; 
}
/* _________________________________________________________________ */
#if 0
static xml_GNode* add1 (xml_GNode* node, gpointer data, gsize off, gsize end)
{
    if (off == end) return 0;
    if(0) g_printerr ("[%.*s]\n", (int)(end-off), node->text->str + off + 1); 

    /* there seems to be a bug in pcre ?? */
    if (off+3 < end &&
      node->text->str[end-1] == '/' && node->text->str[end-2] == '*')
        end -= 2;
      
    return xml_tree_add (node, data, ++off, end);
}
#endif
static gboolean ispara (gchar* txt, gsize off, gsize end)
{ 
# if 0
    return xml_pcre_match1 (txt, off, end, "?" "\\s{1,3}[*]\\s.*");
# else
    if (end-off > 3) end = off+3;
    for (; off < end ; off++)
    
{
	if (txt[off] == '*' && g_ascii_isspace (txt[off+1])) return TRUE;
	if (! g_ascii_isspace (txt[off])) break;
    }
return FALSE; # endif }
xml_GNode*
xml_pdoc_c_doc_old (xml_GNode* node)
{
    if (! node) return node;
    g_assert (node->name && strstr (node->name, "comment"));
    g_assert (node->text && node->text->str[node->off] == '/');
    if (node->off + 4 > node->end) return node;

    ___ gchar* txt = node->text->str+node->off; 
    ___ gssize len = node->end - node->off;
    ___ gchar* end = memchr (txt, '\n', len);
    if (! end) 
{ if (txt[1]=='/') end = txt+len; else end = txt+len-2; }
if (txt+3 > end) goto nofirstline; /* we have the first line... */ if (g_ascii_isspace(txt[3]) && txt[1] == txt[2])
{
        xml_node_attribute_add (node, "doc", "");
        xml_tree_add3 (node, "doc", txt+3, end);
    }
else if (g_ascii_isspace(txt[3]) && strchr ("!$#%=:", txt[3]))
{
        xml_node_attribute_add_value (node, "docs", g_strndup (&txt[3], 1));
        xml_tree_add3 (node, "docs", txt+3, end);
    }
else
{
        xml_node_attribute_add (node, "info", "");
        xml_tree_add3 (node, "info", txt+2, end);
    }
nofirstline: if (end+2 >= txt+len) return node; /* ..................................................... */ # if 0 /* first try */ xml_path_pcre_add (node, 0, "?" "(\\n\\s*[*]\\s)[^\\n]*(\\1[^\\n])*", "para", 0); # elif 0 /* next try */ xml_path_pcre_add_with (node,0, "\\n(\\s*[*])\\s[^\\n]*(?:\\n\\1(?=\\s)[^\\n]*)*", add1, (gpointer) "para", 0); # else ___ xml_GNode *paranode = 0, *codenode = 0; ___ xml_GNode *brkA, *brkE; brkA = node->children; if (! brkA) goto nopara; if (! xml_node_hasname_eq_(brkA, "br")) brkA = xml_node_next_with_name (node->children, "br"); if (! brkA) goto nopara; while ((brkE = xml_node_next_with_name (brkA, "br")))
{	/* we have a body line - now check if it is screen or para */
	g_assert (brkA != brkE); 
	if (0) g_printerr ("[%s:%i]", brkE->name, brkE->off);
	if (ispara (brkA->text->str, brkA->end, brkE->off))
	
{
	    xml_node_save_text_to_attribute (
		xml_tree_add3 (node, "J", brkA->text->str + brkA->end,
			       strchr (brkA->text->str + brkA->end, '*')+1),
		"text", 0);
	    if (! paranode) paranode = brkA;
	    if (codenode) 
	    
{  xml_node_group_inner_new (codenode, brkA, "code"); codenode=0; }
brkA = brkE; continue; }
else
{
	    g_assert (paranode != brkA);
	    if (! codenode) codenode = brkA;
	    if (paranode) 
	    
{  xml_node_group_inner_new (paranode, brkA, "para"); paranode=0; }
brkA = brkE; continue; }
}
____; if (codenode && codenode->next) xml_node_group_inner_new ( codenode, xml_node_next_or_last (codenode, "br"), "code"); if (paranode && paranode->next) xml_node_group_inner_new ( paranode, xml_node_next_or_last (paranode, "br"), "para"); ____; nopara: /* ... */ # endif return node; ____;____;____; }
xml_GNode*
xml_pdoc_c_doc (xml_GNode* node)
{
    if (! node) return node;
    g_assert (node->name && strstr (node->name, "comment"));
    g_assert (node->text && node->text->str[node->off] == '/');
    if (node->off + 4 > node->end) return node;

    ___ gchar* txt = node->text->str+node->off; 
    ___ gchar* end = node->text->str+node->end;
    if (txt[1] == '*') end -= 2;
    ___ gchar* brk = memchr (txt, '\n', end-txt);
    if (! brk) brk = end;
    if (txt+3 > brk) goto nofirstline;
    /* we have the first line... */

    if (g_ascii_isspace(txt[3]) && txt[1] == txt[2])
    
{
        xml_node_attribute_add (node, "doc", "");
        xml_tree_add3 (node, "doc", txt+3, brk);
    }
else if (g_ascii_isspace(txt[3]) && strchr ("!$#%=:", txt[3]))
{
        xml_node_attribute_add_value (node, "docs", g_strndup (&txt[3], 1));
        xml_tree_add3 (node, "docs", txt+3, brk);
    }
else
{
        xml_node_attribute_add (node, "info", "");
        xml_tree_add3 (node, "info", txt+2, brk);
    }
nofirstline: ___ gchar* sec = brk+1; static const gchar para[] = "para"; static const gchar screen[] = "screen"; const gchar* type = para; while (1)
{
	txt = brk+1;
	if (txt >= end) break;
	brk = memchr (txt, '\n', end-txt);
	if (! brk) brk = end;
	/* we have a line, classify it - para, screen, emptypara */
	if (end-txt > 2 && ! g_ascii_isspace (txt[1]) &&
	    g_ascii_isspace (txt[0]) && g_ascii_isspace (txt[2]))
	
{
	    const gchar* seen;
	    switch (txt[1])
	    
{
	    case '*': seen = para; break; /* <para> */
	    default: seen = 0; break;     /* <screen> */
	    }
if (seen)
{
		if (type != seen && sec != txt) 
{
		    g_assert (txt < end);
		    xml_tree_add3 (node, type, sec, txt); }
if (type != seen)
{ sec = txt; type = seen; }
xml_node_save_text_to_attribute ( xml_tree_add3 (node, "J", txt+1, txt+2), "text", 0); ___ gchar* non = txt+2; while (g_ascii_isspace (*non))
{
		    if (++non >= brk) /* an empty line = section break */
		    
{
			if (sec != txt) 
{
			    g_assert (txt < end);
			    xml_tree_add3 (node, seen, sec, txt); }
sec = brk+1; /* type = seen; */ break; }
}
; ____; continue; }
/* what? ... fall through into <screen> section */ }
if (type != screen && sec != txt)
{ 
	    g_assert (txt < end);
	    xml_tree_add3 (node, type, sec, txt); }
if (type != screen)
{ sec = txt; type = screen; }
/* a screen section does not know section breaks */ }
; if (sec < end)
{ /* finalize last section */
	if (sec+1 != end || ! g_ascii_isspace (*sec))
	    xml_tree_add3 (node, type, sec, end); 
    }
return node; ____;____;____;____; }
/* _________________________________________________________________ */
xml_GNode*  xml_pdoc_c_blocks (xml_GNode* tree)
{  
    g_return_val_if_fail (tree && tree->text, tree);
    
    xml_path_pcre_text_to_attr (tree, 
      "//+(comment|literal|externc|cpreproc)", "-cblks", ".");

    ___ gsize cnt;
    
    do 
{
        gchar* off = tree->text->str + tree->off;
        gchar* end = tree->text->str + tree->end;
        gchar* from = 0; gchar* ends = 0;
        for (cnt = 0; off < end; off++)
        
{
            ends = memchr (off, '}', end-off);
	    if (! ends) break;
	    from = memchr (off, '{', ends-off);
	    if (! from) break;
	    while (1) 
{
		gchar* p = memchr (from+1, '{', ends-(from+1));
		if (! p) break; else from = p;
	    }
if (0) g_printerr ("<%i..%i>", from - tree->text->str, ends+1 - tree->text->str); ___ xml_GNode* node = xml_tree_add (tree, "cblock", from - tree->text->str, ends+1 - tree->text->str); if (! node)
{ 
		g_warning ("could not add node %i..%i\n", 
		from - tree->text->str, ends+1 - tree->text->str);
		break;
	    }
if (node->children) xml_path_pcre_text_restore (node, "//*@-cblks", "-cblks"); xml_node_save_text_to_attribute (node, "-cblks", "."); ____; cnt++; off = ends; }
/* g_printerr ("________\n"); */ }
while (cnt); ____; xml_path_pcre_text_restore (tree, "//*@-cblks", "-cblks"); // "cblock" xml_path_pcre_text_restore (tree, "//*@-cblks", "-cblks"); // literals return tree; }
/* _________________________________________________________________ */
xml_GNode* xml_pdoc_c_blocc (xml_GNode* tree)
{
    g_return_val_if_fail (tree && tree->text, tree);

    ___ xml_GNode* node = tree->children;
    gchar* str = tree->text->str;
    gchar* end = 0;
    gchar* off = str + tree->off;
    while (node)
    
{
        end = str + node->off;
        while (off < end)
        
{
            gchar* pnt = memchr (off, ';', end-off);
            if (! pnt) break;
            xml_tree_add2 (tree, "cblocc", pnt-str, pnt+1-str);
            off = pnt+1;
        }
off = str + node->end; node = node->next; }
end = tree->text->str + tree->end;
{
        while (off < end)
        
{
            gchar* pnt = memchr (off, ';', end-off);
            if (! pnt) break;
            xml_tree_add2 (tree, "cblocc", pnt-str, pnt+1-str);
            off = pnt+1;
        }
}
____; return tree; }
xml_GNode* xml_pdoc_item_cdef (xml_GNode* tree)
{
    g_return_val_if_fail (tree && tree->text, tree);

    ___ xml_GNode* node = tree->children; xml_GNode *next;
    gsize  off = tree->off;

    for (; node ; node = next)
    
{
        next = node->next;
        if (! node->name) continue;

        if (xml_node_hasname_as_(node, "*literal"))
            continue;
        if (xml_node_hasname_as_(node, "*comment"))
        
{
            if (next && xml_node_hasname_eq_(next, "br") &&
                xml_node_next_text_empty (node) &&
                xml_node_next_text_empty (next) &&
                (!next->next || xml_node_hasname_eq_(next->next, "br")))
            
{
		node = next; next = next->next;
                xml_tree_add2 (tree, "item_cdoc", off, node->end);
		off = next ? next->end : node->end; 
                continue;
            }
continue; }
if (xml_node_hasname_eq_(node, "br"))
{
            /* an empty line separates everything */
            if (next && xml_node_hasname_eq_(next, "br") &&
                xml_node_next_text_empty (node))
            
{
                off = next->end;
            }
continue; }
if (xml_node_hasname_as_(node, "*bloc"))
{
            xml_tree_add2 (tree, "item_cbloc", off, node->end);
            /* off = node->end; */
            /* continue; */
        }
/* else */ off = node->end; if (next && xml_node_hasname_eq_(next, "br") && xml_node_prev_text_empty (next))
{
            off = next->end;
        }
}
____; return tree; }
/*
   walk through all bloc*s and attach anything before that could be
   part of a declaration or a comment. It breaks parsing on the
   next empty line or non-br/non-comment node. This should group all
   declaration-items in the source. Another call is used to group
   "the rest" into item-elements, so that further processing can be
   done in terms of <item*>s.
 */
xml_GNode* xml_pdoc_item_bloc (xml_GNode* tree)
{
    g_return_val_if_fail (tree && tree->text, tree);

    ___ xml_GNode* node = tree->children;
    ___ xml_GNode* from = node; /* file start is a good one */
    ___ xml_GNode* next; /* needed since grouping might modify node.next */

    for (; node ; node = next)
    
{
        next = node->next;

        if (xml_node_hasname_as_(node, "*literal")) continue;    /* accept */
	if (xml_node_hasname_as_(node, "*comment")) goto node_comment_;
	if (xml_node_hasname_as_(node, "*bloc")) goto node_bloc_;
	if (xml_node_hasname_as_(node, "*br")) goto node_br_;
	/* neither <br> nor <*bloc> */
	from = next; continue; 

    node_br_: 
	if (! next || ! xml_node_hasname_eq_(next, "br") ||
	    ! xml_strstr_empty (node->text->str, node->end, next->off))
	    continue;
	/* detected empty line */
	from = next; continue;
    node_comment_:
	if (next && xml_node_hasname_eq_(next, "br") &&
	    xml_node_next_text_empty (node) &&
	    xml_node_next_text_empty (next) &&
	    (!next->next || xml_node_hasname_eq_(next->next, "br")))
	
{
	    gchar* line = xml_node_attribute_lookup (next, "line");
	    from = next->next;
	    node = xml_tree_add2 (tree, "item_cdoc", node->off, next->end);
	    /* reuse "next" to ask for earlier line.. we set it later...*/
	    for (next = node->children; next ; next = next->next)
	    
{
		if (xml_node_hasname_eq_ (next, "br"))
		
{
		    line = xml_node_attribute_lookup (next, "line");
		    break;
		}
}
xml_node_attribute_add (node, "line", line); next = from; /* start next cycle after item_cdoc */ }
continue; node_bloc_: if (from == node)
{ from = next; continue; }
/* ignore it!! */ /* it would have happened if an empty line is just before some * "*bloc" statement - a rare thing usually intentional by the * programmer to defeat the normal style of doc extracting */ ___ gchar* item = g_strconcat ("item_", node->name, 0); if (! item)
{ from = next; continue; }
if (! xml_node_hasname_eq_(from, "br"))
{
	    node = xml_node_group_outer_new (from, node, item);
	}
else
{
	    node = xml_tree_add2 (tree, item, from->end, node->end);
	    /* if (! node) { from = next; continue; } */
	}
g_free (item); for (from = node->children; from ; from = from->next)
{
	    if (( item = xml_node_attribute_lookup (from, "line") ))
	    
{ xml_node_attribute_add (node, "line", item); break; }
}
____; from = next; continue; }
____;____;____; return tree; }
/* 
   Local variables:
   c-file-style: "stroustrup"
   End:
 */