
/***************************************************************************
                          search.c  -  search implementation
                             -------------------
    begin                : Fri Nov 2 2001
    copyright            : (C) 2001 by Keith Isdale
    email                : k_isdale@tpg.com.au
 ***************************************************************************/


#include "xsldbg.h"
#include "debugXSL.h"
#include "breakpoint.h"
#include "search.h"
#include "options.h"
#include "files.h"

#ifdef __riscos

/* Include for filename conversions */
#include "libxml/riscos.h"
#endif

/* our private function*/
void scanForBreakPoint(void *payload, void *data,
                       xmlChar * name);

/* store all data in this document so we can write it to file*/
static xmlDocPtr searchDataBase;

/* the topmost node in document*/
static xmlNodePtr searchDataBaseRoot;

/* what was the last query that was run */
static xmlChar *lastQuery;

#define BUFFER_SIZE 500
static xmlChar searchBuffer[BUFFER_SIZE];

/* -----------------------------------------
   Private function declarations for dbgsearch.c
 -------------------------------------------*/

/**
 * findNodeByLineNoHelper:
 * @payload: valid xsltStylesheetPtr
 * @data: valid searchInfoPtr
 * @name: not used
 *
 * We are walking through stylesheets looking for a match 
 */
void
  findNodeByLineNoHelper(void *payload, void *data,
                         xmlChar * name);

/**
 * globalVarHelper:
 * @payload: valid xsltStylesheetPtr
 * @data: is valid
 * @name: not used
 *
 * Helper to find the global variables. We are given control via
 *   walkStylesheets globalWalkFunc will always be set to the
 *    walkFunc to call
 */
void
  globalVarHelper(void **payload, void *data,
                  xmlChar * name);

/**
 * localVarHelper:
 * @payload: valid xsltTemplatePtr
 * @data: is valid
 * @name: not used
 *
 * Helper to find the local variables. We are given control via walkTemplates
 *    globalWalkFunc will always be set to the walkFunc to call
 *   localWalkFunc will always be set to the walkFunc to call
 */
void
  localVarHelper(void **payload, void *data,
                 xmlChar * name);


/* ------------------------------------- 
    End private functions
    ---------------------------------------*/


/**
 * searchInit:
 *
 * Initialize the search module
 *
 * Returns 1 if search structures have been initialized properly and all
 *               memory required has been obtained,
 *         0 otherwise
*/
int
searchInit(void)
{
    searchDataBase = NULL;
    searchDataBaseRoot = NULL;
    lastQuery = NULL;
    if (!searchEmpty()) {
        xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
    }
    return (searchRootNode() != NULL);
}


/**
 * searchFree:
 *
 * Free all memory used by the search module
 */
void
searchFree(void)
{
    if (searchDataBase) {
        xmlFreeDoc(searchDataBase);
        searchDataBase = NULL;
        searchDataBaseRoot = NULL;
    }
}


/**
 * searchNewInfo:
 * @type: What type of search is required
 * 
 * Create a new search
 *
 * Returns A valid search info pointer if successful
 *         NULL otherwise
 */
searchInfoPtr
searchNewInfo(SearchEnum type)
{
    searchInfoPtr result = NULL;
    int searchType = type;

    switch (searchType) {
        case SEARCH_BREAKPOINT:
            result = (searchInfoPtr) xmlMalloc(sizeof(searchInfo));
            if (result) {
                breakPointSearchDataPtr searchData;

                result->type = SEARCH_BREAKPOINT;
                searchData = (breakPointSearchDataPtr)
                    xmlMalloc(sizeof(breakPointSearchData));
                if (searchData) {
                    searchData->id = -1;
                    searchData->templateName = NULL;
                    searchData->breakPtr = NULL;
                    result->data = searchData;
                } else {
                    xmlFree(result);
                    result = NULL;
                }
            }
            break;

        case SEARCH_NODE:
            result = (searchInfoPtr) xmlMalloc(sizeof(searchInfo));
            if (result) {
                nodeSearchDataPtr searchData;

                result->type = SEARCH_NODE;
                searchData =
                    (nodeSearchDataPtr) xmlMalloc(sizeof(nodeSearchData));
                if (searchData) {
                    searchData->node = NULL;
                    searchData->lineNo = -1;
                    searchData->url = NULL;
                    searchData->nameInput = NULL;
                    searchData->guessedNameMatch = NULL;
                    searchData->absoluteNameMatch = NULL;
                    result->data = searchData;
                } else {
                    xmlFree(result);
                    result = NULL;
                }
            }
            break;

        case SEARCH_XSL:
            break;

        case SEARCH_VARIABLE:
            result = (searchInfoPtr) xmlMalloc(sizeof(searchInfo));
            if (result) {
                variableSearchDataPtr searchData;

                result->type = SEARCH_VARIABLE;
                searchData = (variableSearchDataPtr)
                    xmlMalloc(sizeof(variableSearchData));
                if (searchData) {
                    searchData->name = NULL;
                    searchData->nameURI = NULL;
                    searchData->select = NULL;
                    result->data = searchData;
                } else {
                    xmlFree(result);
                    result = NULL;
                }
            }
            break;

    }
    if (result) {
        result->found = 0;
        result->error = 0;
    }
    return result;
}


/**
 * searchFreeInfo:
 * @info: valid search info
 *
 * Free memory used by @info
 */
void
searchFreeInfo(searchInfoPtr info)
{
    if (info) {
        if (info->data) {
            switch (info->type) {
                case SEARCH_BREAKPOINT:
                    {
                        breakPointSearchDataPtr searchData =
                            (breakPointSearchDataPtr) info->data;

                        if (searchData->templateName)
                            xmlFree(searchData->templateName);
                    }
                    break;

                case SEARCH_NODE:
                    {
                        nodeSearchDataPtr searchData =
                            (nodeSearchDataPtr) info->data;

                        if (searchData->url)
                            xmlFree(searchData->url);

                        if (searchData->nameInput)
                            xmlFree(searchData->nameInput);

                        if (searchData->guessedNameMatch)
                            xmlFree(searchData->guessedNameMatch);

                        if (searchData->absoluteNameMatch)
                            xmlFree(searchData->absoluteNameMatch);

                        /* we never free searchData->node as we did not create it! */
                    }
                    break;

                case SEARCH_XSL:
                    break;

                case SEARCH_VARIABLE:
                    {
                        variableSearchDataPtr searchData =
                            (variableSearchDataPtr) info->data;

                        if (searchData->name)
                            xmlFree(searchData->name);

                        if (searchData->nameURI)
                            xmlFree(searchData->nameURI);

                        if (searchData->select)
                            xmlFree(searchData->select);
                    }
                    break;

            }
            xmlFree(info->data);
        }
        xmlFree(info);
    }
}


/**
 * searchEmpty:
 *
 * Empty the seach dataBase of its contents
 *
 * Returns 1 on success,
 *         0 otherwise
 */
int
searchEmpty(void)
{
    if (searchDataBase) {
        xmlFreeDoc(searchDataBase);
    }
    searchDataBase = xmlNewDoc((xmlChar *) "1.0");
    searchDataBaseRoot = NULL;
    if (searchDataBase) {
        xmlCreateIntSubset(searchDataBase,
                           (xmlChar *) "search", (xmlChar *)
                           "-//xsldbg//DTD search XML V1.1//EN",
                           (xmlChar *) "search_v1_1.dtd");
        searchDataBaseRoot = xmlNewNode(NULL, (xmlChar *) "search");
        if (searchDataBaseRoot)
            xmlAddChild((xmlNodePtr) searchDataBase, searchDataBaseRoot);
    }
    if (lastQuery)
        xmlFree(lastQuery);
    lastQuery = NULL;
    if (searchRootNode() == NULL) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
        xsltGenericError(xsltGenericErrorContext,
                        "Error: Unable to clear old search results, memory error?\n");
#endif
    }

    return (searchRootNode() != NULL);
}


/**
 * searchDoc:
 *
 * Return the document used for seaching ie the search dataBase
 *
 * Returns The document used for searching
 *         Dangerous function to use! Does NOT return a copy of 
 *             search data  so don't free it
 */
xmlDocPtr
searchDoc(void)
{
    return searchDataBase;
}


/**
 * searchRootNode:
 *
 * Get the topmost node in the search dataBase
 *
 * Returns The topmost xml node in search dataBase.
 *         Dangerous function to use! Does NOT return a copy of 
 *             search root node  so don't free it
 */
xmlNodePtr
searchRootNode(void)
{
    return searchDataBaseRoot;
}


/**
 * searchAdd:
 * @node: Is valid
 *
 * Add a node to the search dataBase
 *
 * Returns 1 if able to add @node to top node in search dataBase,
 *         0 otherwise
 */
int
searchAdd(xmlNodePtr node)
{
    int result = 0;

    if (node && searchDataBaseRoot
        && xmlAddChild(searchDataBaseRoot, node)) {
        result = 1;
    }

    return result;
}


/**
 * searchSave:
 * @fileName: A valid file name, or NULL for the default
 *
 * Save the search dataBase to @fileName
 *
 * Returns 1 on success,
 *         0 otherwise
 */
int
searchSave(const xmlChar * fileName)
{

    int result = 0;
    xmlChar *searchInput = NULL;

    if (fileName == NULL)
        searchInput = filesSearchFileName(FILES_SEARCHINPUT);
    else
        searchInput = xmlStrdup(fileName);

    if (xmlSaveFormatFile((char *) searchInput, searchDataBase, 1) != -1){
        result = 1;
    }else{
        xsldbgGenericErrorFunc(i18n("Error: Unable to write search Database to file %1. Try setting the \"searchresultspath\" option to a writable path.\n").arg(xsldbgText(searchInput)));
    }

    if (searchInput)
        xmlFree(searchInput);

    return result;
}


/**
 * searchQuery:
 * @query: The query to run . If NULL then query is "//search/ *"
 * @tempFile: Where do we load the search dataBase from to execute
 *             query. If tempFile is NULL "searchresult.xml" is used
 * @outputFile : Where do we store the result. If NULL
 *             then default to  "searchresult.html"
 * 
 * Send query as parameter for execution of search.xsl using
 *    data stored in @tempFile 
 *
 * Returns 1 on success,
 *         0 otherwise   
 */
int
searchQuery(const xmlChar * tempFile, const xmlChar * outputFile,
            const xmlChar * query)
{
    int result = 0;

    /* The file name of where the input is comming from */
    xmlChar *searchInput = NULL;

    /* The XSL file name to use during transformation of searchInput */
    xmlChar *searchXSL = NULL;

    /* Where to store the result of transformation */
    xmlChar *searchOutput = NULL;


    /* if a tempFile if provided its up you to make sure that it is correct !! */
    if (tempFile == NULL)
        searchInput = filesSearchFileName(FILES_SEARCHINPUT);
    else
        searchInput = xmlStrdup(tempFile);

    searchXSL = filesSearchFileName(FILES_SEARCHXSL);

    /* if a outputFile if provided its up you to make sure that it is correct */
    if (outputFile == NULL)
        searchOutput = filesSearchFileName(FILES_SEARCHRESULT);
    else
        searchOutput = xmlStrdup(outputFile);

    if (!query || (xmlStrlen(query) == 0))
        query = (xmlChar *) "--param query //search/*";

    /* see configure.in for the definition of XSLDBG_BIN, the name of our binary */
    if (searchInput && searchXSL && searchOutput) {
        if (optionsGetIntOption(OPTIONS_CATALOGS) == 0)
            snprintf((char *) searchBuffer, sizeof(searchBuffer),
                     "%s -o %s %s %s %s", XSLDBG_BIN,
                     searchOutput, query, searchXSL, searchInput);
        else
            /* assume that we are to use catalogs as well in our query */
            snprintf((char *) searchBuffer, sizeof(searchBuffer),
                     "%s --catalogs -o %s %s %s %s", XSLDBG_BIN,
                     searchOutput, query, searchXSL, searchInput);
        result = xslDbgShellExecute(searchBuffer, 1);

        if (result && (optionsGetIntOption(OPTIONS_PREFER_HTML) == 0)) {
            /* try printing out the file */
            result = filesMoreFile(searchOutput, NULL);
        }

        xsldbgGenericErrorFunc(i18n("Information: Transformed %1 using %2 and saved to %3.\n").arg(xsldbgText(searchInput)).arg(xsldbgText(searchXSL)).arg(xsldbgText(searchOutput)));
    } else {
        xsldbgGenericErrorFunc(i18n("Error: Invalid arguments to command %1.\n").arg("search"));
    }

    if (searchInput)
        xmlFree(searchInput);

    if (searchXSL)
        xmlFree(searchXSL);

    if (searchOutput)
        xmlFree(searchOutput);

    return result;
}


/**
 * scanForBreakPoint: 
 * @payload: A valid breakPointPtr 
 * @data: The criteria to look for and a valid searchInfoPtr of
 *          type SEARCH_BREAKPOINT 
 * @name: Not used 
 *
 * Test if break point matches criteria given by @data. If so then 
 *      set @data->found to 1 and stores  reference to break point found in 
 *         @data->data->node
 *     otherwise @data is unchanged
*/
void
scanForBreakPoint(void *payload, void *data,
                  xmlChar * name)
{
    Q_UNUSED(name);
    breakPointPtr breakPtr = (breakPointPtr) payload;
    searchInfoPtr searchInf = (searchInfoPtr) data;
    breakPointSearchDataPtr searchData = NULL;
    int found = 0;

    if (!payload || !searchInf || !searchInf->data
        || (searchInf->type != SEARCH_BREAKPOINT) || searchInf->found)
        return;

    searchData = (breakPointSearchDataPtr) searchInf->data;

    if (searchData->id && (breakPtr->id == searchData->id))
        found = 1;
    else if (searchData->templateName && breakPtr->templateName &&
             (xmlStrCmp(breakPtr->templateName, searchData->templateName)
              == 0))
        found = 1;

    if (found) {
        searchInf->found = 1;
        searchData->breakPtr = breakPtr;
    }
}



/**
 * scanForNode: 
 * @payload: A valid xmlNodePtr
 * @data: The criteria to look for and a valid searchInfo of
 *          type SEARCH_NODE 
 * @name: Not used

 * Test if node matches criteria given by @data if so then set @data->found 
 *   to 1 and  stores reference to node found in @data->data->node
 *     otherwise @data is unchanged
 */
void
scanForNode(void *payload, void *data, xmlChar * name)
{
    Q_UNUSED(name);
    searchInfoPtr searchInf = (searchInfoPtr) data;
    nodeSearchDataPtr searchData = NULL;
    xmlNodePtr node = (xmlNodePtr) payload;
    xmlChar *baseUri = NULL;
    int match = 1;

    if (!node || !node->doc || !node->doc->URL ||
        !searchInf || (searchInf->type != SEARCH_NODE))
        return;

    searchData = (nodeSearchDataPtr) searchInf->data;

    if (searchData->lineNo >= 0)
        match = searchData->lineNo == xmlGetLineNo(node);

    if (searchData->url)
        baseUri = filesGetBaseUri(node);
    if (baseUri) {
        match = match && (xmlStrCmp(searchData->url, baseUri) == 0);
        xmlFree(baseUri);
    } else {
        match = match && (xmlStrcmp(searchData->url, node->doc->URL) == 0);
    }

    if (match) {
        searchData->node = node;
        searchInf->found = 1;
    }

}


/**
 * findNodeByLineNoHelper:
 * @payload: A valid xsltStylesheetPtr
 * @data: A valid searchInfoPtr
 * @name: Not used
 *
 * We are walking through stylesheets looking for a match 
 */
void
findNodeByLineNoHelper(void *payload, void *data,
                       xmlChar * name)
{
    Q_UNUSED(name);
    xsltStylesheetPtr style = (xsltStylesheetPtr) payload;
    searchInfoPtr searchInf = (searchInfoPtr) data;

    if (!payload || !searchInf || !style->doc)
        return;

    walkChildNodes((xmlHashScanner) scanForNode, searchInf,
                   (xmlNodePtr) style->doc);

    /* try the included stylesheets */
    if (!searchInf->found)
        walkIncludes((xmlHashScanner) scanForNode, searchInf, style);
}


/**
 * findNodeByLineNo:
 * @ctxt: Valid ctxt to look into
 * @url: Non-null, non-empty file name that has been loaded by debugger
 * @lineNumber: @lineNumber >= 0 and is available in @url
 *
 * Finds the closest line number in file specified that can be a point 
 *
 * Returns The node at line number number specified if successfull,
 *          NULL otherwise
 */
xmlNodePtr
findNodeByLineNo(xsltTransformContextPtr ctxt,
                 const xmlChar * url, long lineNumber)
{
    xmlNodePtr result = NULL;
    searchInfoPtr searchInf = searchNewInfo(SEARCH_NODE);
    nodeSearchDataPtr searchData = NULL;

    if (!searchInf) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
        xsltGenericError(xsltGenericErrorContext,
                         "Error: Unable to create searchInfo in findNodeByLineNo\n");
#endif
        return result;
    }

    if (!ctxt || !url || (lineNumber == -1)) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
        xsltGenericError(xsltGenericErrorContext,
                         "Error: Invalid ctxt, url or line number to findNodeByLineNo\n");
#endif
        return result;
    }

    searchData = (nodeSearchDataPtr) searchInf->data;
    searchData->url = (xmlChar *) xmlMemStrdup((char *) url);
    searchData->lineNo = lineNumber;
    walkStylesheets((xmlHashScanner) findNodeByLineNoHelper, searchInf,
                    ctxt->style);
    if (!searchInf->found) {
        /* try searching the document set */
        xsltDocumentPtr document = ctxt->document;

        while (document && !searchInf->found) {
            walkChildNodes((xmlHashScanner) scanForNode, searchInf,
                           (xmlNodePtr) document->doc);
            document = document->next;
        }
    }
    result = searchData->node;
    searchFreeInfo(searchInf);

    return result;
}


/**
 * findTemplateNode: 
 * @style: A valid stylesheet collection to look into 
 * @name: A valid template name to look for
 *
 * Find a template node
 *
 * Returns The template node found if successful,
 *         NULL otherwise 
 */
xmlNodePtr
findTemplateNode(xsltStylesheetPtr style, const xmlChar * name)
{
    xmlNodePtr result = NULL;
    xmlChar *templName;
    xsltTemplatePtr templ;

    if (!style || !name) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
        xsltGenericError(xsltGenericErrorContext,
                         "Error: Invalid stylesheet or template name : findTemplateNode\n");
#endif
        return result;
    }

    while (style) {
        templ = style->templates;

        while (templ) {
            if (templ->match)
                templName = (xmlChar *) templ->match;
            else
                templName = (xmlChar *) templ->name;

            if (templName) {
                if (!xmlStrCmp((char *) templName, (char *) name)) {
                    return templ->elem;
                }
            }
            templ = templ->next;
        }
        if (style->next)
            style = style->next;
        else
            style = style->imports;
    }

    if (!result)
        xsldbgGenericErrorFunc(i18n("Error: XSLT template named \"%1\" was not found.\n").arg(xsldbgText(name)));
    return result;
}


/**
 * findBreakPointByName:
 * @templateName: The template name to look for
 *
 * Find the breakpoint at template with "match" or "name" equal 
 *    to templateName
 *
 * Returns The break point that matches @templateName
 *         NULL otherwise
*/
breakPointPtr
findBreakPointByName(const xmlChar * templateName)
{
    breakPointPtr result = NULL;
    searchInfoPtr searchInf = searchNewInfo(SEARCH_BREAKPOINT);
    breakPointSearchDataPtr searchData;

    if (!searchInf || (searchInf->type != SEARCH_BREAKPOINT))
        return result;

    searchData = (breakPointSearchDataPtr) searchInf->data;
    searchData->templateName = (xmlChar *) xmlStrdup(templateName);
    if (templateName) {
        walkBreakPoints((xmlHashScanner) scanForBreakPoint, searchInf);
        if (!searchInf->found) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
            xsltGenericError(xsltGenericErrorContext,
                             "Error: Breakpoint with template name of \"%s\" not found\n",
                             templateName);
#endif
        } else
            result = searchData->breakPtr;
    }

    searchFreeInfo(searchInf);

    return result;
}


/**
 * findBreakPointById:
 * @id: The break point id to look for
 *
 * Find a break point by its id
 *
 * Returns The break point with given the break point id if found,
 *         NULL otherwise 
 */
breakPointPtr
findBreakPointById(int id)
{
    breakPointPtr result = NULL;
    searchInfoPtr searchInf = searchNewInfo(SEARCH_BREAKPOINT);
    breakPointSearchDataPtr searchData;

    if (!searchInf || !searchInf->data)
        return result;

    searchData = (breakPointSearchDataPtr) searchInf->data;
    if (id >= 0) {
        searchData->id = id;
        walkBreakPoints((xmlHashScanner) scanForBreakPoint, searchInf);
        if (!searchInf->found) {
#ifdef WITH_XSLDBG_DEBUG_PROCESS
            xsltGenericError(xsltGenericErrorContext,
                             "Error: Breakpoint id %d not found\n", id);
#endif
        } else
            result = searchData->breakPtr;
    }

    searchFreeInfo(searchInf);
    return result;
}


/**
 * findNodesByQuery:
 * @query: The xpath query to run, see docs/en/search.dtd for more details
 *  
 * Find nodes in search dataBase using an xpath query
 *
 * Returns The nodes that match the given query on success,
 *         NULL otherwise 
 */
xmlXPathObjectPtr
findNodesByQuery(const xmlChar * query)
{
    Q_UNUSED(query);
    xmlXPathObjectPtr list = NULL;

    return list;
}



/**
 * walkBreakPoints:
 * @walkFunc: The function to callback for each break point found
 * @data: The extra data to pass onto walkFunc
 *
 * Walks through all break points calling walkFunc for each. The payload
 *  sent to walkFunc is of type breakPointPtr 
 */
void
walkBreakPoints(xmlHashScanner walkFunc, void *data)
{
    int lineNo;
    xmlHashTablePtr hashTable;

    if (!walkFunc)
        return;

    for (lineNo = 0; lineNo < breakPointLinesCount(); lineNo++) {
        hashTable = breakPointGetLineNoHash(lineNo);
        if (hashTable) {
            xmlHashScan(hashTable, walkFunc, data);
        }
    }
}


/**
 * walkTemplates:
 * @walkFunc: The function to callback for each template found
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Walks through all templates calling walkFunc for each. The payload
 *   of walkFunc is of type xsltTemplatePtr
 */
void
walkTemplates(xmlHashScanner walkFunc, void *data, xsltStylesheetPtr style)
{
    xsltTemplatePtr templ;

    if (!walkFunc || !style)
        return;

    while (style) {
        templ = style->templates;
        while (templ) {
            (*walkFunc) (templ, data, NULL);
            templ = templ->next;
        }
        if (style->next)
            style = style->next;
        else
            style = style->imports;
    }
}


/**
 * walkStylesheets:
 * @walkFunc: The function to callback for each stylesheet found
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Walks through all templates calling walkFunc for each. The payload
 *   sent to walkFunc is of type xsltStylesheetPtr
 */
void
walkStylesheets(xmlHashScanner walkFunc, void *data,
                xsltStylesheetPtr style)
{
    xsltStylesheetPtr next;

    if (!walkFunc || !style)
        return;

    next = style->next;
    while (style) {
        (*walkFunc) (style, data, NULL);
        if (style->imports)
            style = style->imports;
        else
            style = next;
    }
}



xmlHashScanner globalWalkFunc = NULL;

/**
 * globalVarHelper:
 * @payload: valid xsltStylesheetPtr
 * @data: is valid
 * @name: not used
 *
 * Helper to find the global variables. We are given control via
 *   walkStylesheets globalWalkFunc will always be set to the
 *    walkFunc to call
 */
void
globalVarHelper(void **payload, void *data,
                xmlChar * name)
{
    Q_UNUSED(data);
    Q_UNUSED(name);
    xsltStylesheetPtr style = (xsltStylesheetPtr) payload;
    xsltStackElemPtr global;

    if (style) {
        global = style->variables;

        while (global &&global->comp) {
            (*globalWalkFunc) (global->comp->inst, data, NULL);
            global = global->next;
        }
    }
}


/**
 * walkGlobals:
 * @walkFunc: The function to callback for each gobal variable found
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Call walkFunc for each global variable. The payload
 *   sent to walkFunc is of type  xmlNodePtr
 */
void
walkGlobals(xmlHashScanner walkFunc, void *data,
            xsltStylesheetPtr style)
{
    Q_UNUSED(data);
    if (!walkFunc || !style)
        return;

    globalWalkFunc = walkFunc;

    walkStylesheets((xmlHashScanner) globalVarHelper, data, style);
}



xmlHashScanner localWalkFunc = NULL;

/**
 * localVarHelper:
 * @payload: valid xsltTemplatePtr
 * @data: is valid
 * @name: not used
 *
 * Helper to find the local variables. We are given control via walkTemplates
 *    globalWalkFunc will always be set to the walkFunc to call
 *   localWalkFunc will always be set to the walkFunc to call
 */
void
localVarHelper(void **payload, void *data,
               xmlChar * name)
{
    Q_UNUSED(data);
    Q_UNUSED(name);
    xsltTemplatePtr templ = (xsltTemplatePtr) payload;
    xmlNodePtr node;

    if (templ && templ->elem) {
        node = templ->elem->children;

        while (node) {
            if (IS_XSLT_NAME(node, "param")
                || IS_XSLT_NAME(node, "variable")) {
                (*localWalkFunc) (node, data, NULL);
                node = node->next;
            } else
                break;
        }
    }
}


/**
 * walkLocals:
 * @walkFunc: The function to callback for each local variable found
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Walks through all local variables calling walkFunc for each. The payload
 *   of walkFunc is of type xmlNodePtr
 */
void
walkLocals(xmlHashScanner walkFunc, void *data, xsltStylesheetPtr style)
{
    if (!walkFunc || !style)
        return;

    localWalkFunc = walkFunc;

    walkTemplates((xmlHashScanner) localVarHelper, data, style);

}


/**
 * walkIncludes:
 * @walkFunc: The function to callback for each included stylesheet
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Walks through all included stylesheets calling walkFunc for each. 
 * The payload of walkFunc is of type xmlNodePtr
 */

void
walkIncludes(xmlHashScanner walkFunc, void *data, xsltStylesheetPtr style)
{
    xsltDocumentPtr document;   /* included xslt documents */

    if (!walkFunc || !style)
        return;

    while (style) {
        document = style->docList;
        /* look at included documents */
        while (document) {
            (*walkFunc) ((xmlNodePtr) document->doc, data, NULL);
            document = document->next;
        }
        /* try next stylesheet */
        if (style->next)
            style = style->next;
        else
            style = style->imports;
    }
}


/**
 * walkIncludeInst:
 * @walkFunc: The function to callback for each xsl:include instruction found
 * @data: The extra data to pass onto walkFunc
 * @style: The stylesheet to start from
 *
 * Walks through all xsl:include calling walkFunc for each. The payload
 *   of walkFunc is of type xmlNodePtr
 */
void
walkIncludeInst(xmlHashScanner walkFunc, void *data,
                xsltStylesheetPtr style)
{
    xmlNodePtr node = NULL, styleChild = NULL;

    if (!walkFunc || !style)
        return;

    while (style) {
        /*look for stylesheet node */
        if (style->doc) {
            node = (xmlNodePtr) style->doc->children;
            while (node) {
                /* not need but just in case :) */
                if (IS_XSLT_NAME(node, "stylesheet")
                    || IS_XSLT_NAME(node, "transform")) {
                    styleChild = node->children;        /* get the topmost elements */
                    break;
                } else
                    node = node->next;
            }

            /* look for includes */
            while (styleChild) {
                if (IS_XSLT_NAME(styleChild, "include"))
                    (*walkFunc) (styleChild, data, NULL);
                styleChild = styleChild->next;
            }
        }
        /* try next stylesheet */
        if (style->next)
            style = style->next;
        else
            style = style->imports;
    }
}


/**
 * walkChildNodes:
 * @walkFunc: The function to callback for each child/sibling found
 * @data: The extra data to pass onto walkFunc
 * @node: Is valid
 *
 * Call walkFunc for each child of @node the payload sent to walkFunc is
 *   a xmlNodePtr
 */
void
walkChildNodes(xmlHashScanner walkFunc, void *data, xmlNodePtr node)
{
    xmlNodePtr child = NULL;
    searchInfoPtr searchInf = (searchInfoPtr) data;

    if (!walkFunc || !searchInf || !searchInf->data)
        return;

    while (node && !searchInf->found) {
        (walkFunc) (node, data, NULL);
        child = node->children;
        if (child && !searchInf->found) {
            walkChildNodes(walkFunc, data, child);
        }
        node = node->next;
    }
}


/**
 * searchBreakPointNode:
 * @breakPtr: Is valid
 *
 * Convert @breakPtr into search dataBase format
 *
 * Returns @breakPtr as a new xmlNode in search dataBase format 
 *               if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchBreakPointNode(breakPointPtr breakPtr)
{

    xmlNodePtr node = NULL;
    int result = 1;

    if (breakPtr) {
        node = xmlNewNode(NULL, (xmlChar *) "breakpoint");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            result = result
                && (xmlNewProp(node, (xmlChar *) "url", breakPtr->url) !=
                    NULL);
            sprintf((char *) searchBuffer, "%ld", breakPtr->lineNo);
            result = result
                &&
                (xmlNewProp(node, (xmlChar *) "line", (xmlChar *) searchBuffer)
                 != NULL);
            if (breakPtr->templateName) {
                result = result
                    &&
                    (xmlNewProp
                     (node, (xmlChar *) "template",
                      breakPtr->templateName) != NULL);
            }
            sprintf((char *) searchBuffer, "%d", breakPtr->flags & BREAKPOINT_ENABLED);
            result = result
                &&
                (xmlNewProp
                 (node, (xmlChar *) "enabled", (xmlChar *) searchBuffer)
                 != NULL);
            sprintf((char *) searchBuffer, "%d", breakPtr->type);
            result = result
                &&
                (xmlNewProp(node, (xmlChar *) "type", (xmlChar *) searchBuffer)
                 != NULL);
            sprintf((char *) searchBuffer, "%d", breakPtr->id);
            result = result
                && (xmlNewProp(node, (xmlChar *) "id", (xmlChar *) searchBuffer)
                    != NULL);
        } else
            result = 0;
        if (!result) {
            xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
        }
    }
    return node;
}


/**
 * searchTemplateNode:
 * @templNode: Is valid
 * 
 * Convert @templateNode into search dataBase format
 *
 * Returns @templNode as a new xmlNode in search dataBase format 
 *               if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchTemplateNode(xmlNodePtr templNode)
{
    xmlNodePtr node = NULL;
    xmlChar *value;
    int result = 1;

    if (templNode) {
        node = xmlNewNode(NULL, (xmlChar *) "template");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            value = xmlGetProp(templNode, (xmlChar *) "match");
            if (value) {
                result = result
                    && (xmlNewProp(node, (xmlChar *) "match", value) !=
                        NULL);
                xmlFree(value);
            }
            value = xmlGetProp(templNode, (xmlChar *) "name");
            if (value) {
                result = result
                    && (xmlNewProp(node, (xmlChar *) "name", value) !=
                        NULL);
                xmlFree(value);
            }
            if (templNode->doc) {
                result = result
                    &&
                    (xmlNewProp
                     (node, (xmlChar *) "url",
                      templNode->doc->URL) != NULL);
            }
            sprintf((char *) searchBuffer, "%ld", xmlGetLineNo(templNode));
            result = result
                &&
                (xmlNewProp(node, (xmlChar *) "line", (xmlChar *) searchBuffer)
                 != NULL);
            if (result) {
                xmlNodePtr textNode = searchCommentNode(templNode);

                if (textNode && !xmlAddChild(node, textNode))
                    result = 0;
            }
        } else
            result = 0;
	if (!result) {
	    xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
        }
    }
    return node;
}


/** 
 * searchGlobalNode:
 * @globalVariable: Is valid
 *
 * Convert @globalVariable into search dataBase format
 *
 * Returns @globalVariable as a new xmlNode in search dataBase format 
 *             if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchGlobalNode(xmlNodePtr variable)
{
    xmlNodePtr node = NULL;
    int result = 1;
    xmlChar *value;

    if (variable) {
        node = xmlNewNode(NULL, (xmlChar *) "variable");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            if (variable->doc) {
                result = result &&
                    (xmlNewProp(node, (xmlChar *) "url",
                                variable->doc->URL) != NULL);
                sprintf((char *) searchBuffer, "%ld", xmlGetLineNo(variable));
                result = result
                    && (xmlNewProp(node, (xmlChar *) "line",
                                   (xmlChar *) searchBuffer) != NULL);
            }
            value = xmlGetProp(variable, (xmlChar *) "name");
            if (value) {
                result = result
                    && (xmlNewProp(node, (xmlChar *) "name", value) !=
                        NULL);
                xmlFree(value);
            }
            value = xmlGetProp(variable, (xmlChar *) "select");
            if (value) {
                result = result
                    && (xmlNewProp(node, (xmlChar *) "select", value) !=
                        NULL);
                xmlFree(value);
            }
            if (result) {
                xmlNodePtr textNode = searchCommentNode(variable);

                if (textNode && !xmlAddChild(node, textNode))
                    result = 0;
            }
        } else
            result = 0;
    }
    if (!result) {
        xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
    }
    return node;
}


/** 
 * searchLocalNode:
 * @localvariable: Is valid
 *
 * Convert @localVariable into search dataBase format
 *
 * Returns @localVariable as a new xmlNode in search dataBase format 
 *             if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchLocalNode(xmlNodePtr variable)
{
    xmlNodePtr node = NULL;
    int result = 1;
    xmlChar *value;
    xmlNodePtr parent;

    if (variable) {
        node = searchGlobalNode(variable);
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            parent = variable->parent;
            /* try to find out what template this variable belongs to */
            if (parent && IS_XSLT_NAME(parent, "template")) {
                value = xmlGetProp(parent, (xmlChar *) "name");
                if (value) {
                    result = result
                        &&
                        (xmlNewProp(node, (xmlChar *) "templname", value)
                         != NULL);
                    xmlFree(value);
                }
                value = xmlGetProp(parent, (xmlChar *) "match");
                if (value) {
                    result = result
                        &&
                        (xmlNewProp(node, (xmlChar *) "templmatch", value)
                         != NULL);
                    xmlFree(value);
                }
            }
        } else
            result = 0;
    }
    if (!result) {
        xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
    }
    return node;
}


/**
 * searchSourceNode:
 * @style: Is valid
 * 
 * Convert @style into search dataBase format
 *
 * Returns @style as a new xmlNode in search dataBase format if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchSourceNode(xsltStylesheetPtr style)
{
    xmlNodePtr node = NULL;
    int result = 1;

    if (style) {
        if (style->parent == NULL)
            node = xmlNewNode(NULL, (xmlChar *) "source");
        else
            node = xmlNewNode(NULL, (xmlChar *) "import");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            if (style->doc) {
                result = result &&
                    (xmlNewProp(node, (xmlChar *) "href", style->doc->URL)
                     != NULL);
                if (style->parent && style->parent->doc) {
                    result = result &&
                        (xmlNewProp(node, (xmlChar *) "parent",
                                    style->parent->doc->URL) != NULL);
                }
                if (result) {
                    xmlNodePtr textNode =
                        searchCommentNode((xmlNodePtr) style->doc);
                    if (textNode && !xmlAddChild(node, textNode))
                        result = 0;
                }
            }
        } else
            result = 0;
    }
    if (!result) {
        xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
    }
    return node;
}


/**
 * searchIncludeNode:
 * @include: Is a valid xsl:include instruction
 *   
 * Convert @include into search dataBase format
 *
 * Returns @include as a new xmlNode in search dataBase format if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchIncludeNode(xmlNodePtr include)
{
    xmlNodePtr node = NULL;
    int result = 1;
    xmlChar *value;

    if (include) {
        node = xmlNewNode(NULL, (xmlChar *) "include");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            if (include->doc) {
                value = xmlGetProp(include, (xmlChar *) "href");
                if (value) {
                    result = result
                        && (xmlNewProp(node, (xmlChar *) "href", value) !=
                            NULL);
                    xmlFree(value);
                }
                if (include->parent && include->parent->doc) {
                    result = result &&
                        (xmlNewProp(node, (xmlChar *) "url",
                                    include->parent->doc->URL) != NULL);
                    sprintf((char *) searchBuffer, "%ld", xmlGetLineNo(include));
                    result = result
                        && (xmlNewProp(node, (xmlChar *) "line",
                                       (xmlChar *) searchBuffer) != NULL);
                }
                if (result) {
                    xmlNodePtr textNode = searchCommentNode(include);

                    if (textNode && !xmlAddChild(node, textNode))
                        result = 0;
                }
            }
        } else
            result = 0;
    }
    if (!result) {
        xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
    }
    return node;
}


/**
 * searchCallStackNode:
 * @callStackItem: Is valid
 * 
 * Convert @callStackItem into search dataBase format
 *
 * Returns @callStackItem as a new xmlNode in search dataBase format 
 *            if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchCallStackNode(callPointPtr callStackItem)
{
    xmlNodePtr node = NULL;
    int result = 1;

    if (callStackItem) {
        node = xmlNewNode(NULL, (xmlChar *) "callstack");
        if (node) {
            /* if unable to create any property failed then result will be equal to 0 */
            if (callStackItem->info && callStackItem->info->url)
                result = result
                    &&
                    (xmlNewProp
                     (node, (xmlChar *) "url", callStackItem->info->url)
                     != NULL);
            sprintf((char *) searchBuffer, "%ld", callStackItem->lineNo);
            result = result
                &&
                (xmlNewProp(node, (xmlChar *) "line", (xmlChar *) searchBuffer)
                 != NULL);
            if (callStackItem->info && callStackItem->info->templateName) {
                result = result &&
                    (xmlNewProp
                     (node, (xmlChar *) "template",
                      callStackItem->info->templateName) != NULL);
            }
        } else
            result = 0;
        if (!result) {
            xsldbgGenericErrorFunc(i18n("Error: Out of memory.\n"));
        }
    }
    return node;
}


static xmlChar *commentText(xmlNodePtr node);

/*
 * Returns A copy of comment text that applies to node,
 *         NULL otherwise
 */
xmlChar *
commentText(xmlNodePtr node)
{
    xmlChar *result = NULL;

    if (node) {
        if (node->type == XML_COMMENT_NODE)
            result = xmlNodeGetContent(node);
    }

    return result;
}

/**
 * searchCommentNode:
 * @sourceNode: Is valid
 * 
 * Find documentation comment that applies to @node. If found convert comment 
 *         into search dataBase format required
 *
 * Returns Documentation comment for @node as a new xmlNode in search dataBase format 
 *            if successful,
 *         NULL otherwise
 */
xmlNodePtr
searchCommentNode(xmlNodePtr sourceNode)
{
    xmlNodePtr node = NULL, textChild = NULL;
    xmlChar *text = NULL;
    int result = 0;

    if (sourceNode) {
        text = commentText(sourceNode->prev);
        if (!text) {
            text = commentText(sourceNode->children);
        }

        if (text) {
            node = xmlNewNode(NULL, (xmlChar *) "comment");
            textChild = xmlNewText(text);
            if (node && textChild && xmlAddChild(node, textChild)) {
                result = 1;
            }
            if (!result) {
                if (node) {
                    xmlFreeNode(node);
                    node = NULL;
                }
                if (textChild)
                    xmlFreeNode(textChild);
            }

            xmlFree(text);
        }
    }
    return node;
}
