/*  This file is part of the Trinity Desktop Environment project
 *  Copyright © 2026, Alexander Golubev (Fat-Zer) <fatzer2@gmail.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License version 2 as published by the Free Software Foundation.
 *
 *  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; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 *  This file incorporates work extracted from libxml2-2.13.8, which is covered
 *  by the following copyright and permission notice:
 *
 *    Copyright (C) 1998-2012 Daniel Veillard.  All Rights Reserved.
 *
 *    Permission is hereby granted, free of charge, to any person obtaining a
 *    copy of this software and associated documentation files (the
 *    "Software"), to deal in the Software without restriction, including
 *    without limitation the rights to use, copy, modify, merge, publish,
 *    distribute, sublicense, and/or sell copies of the Software, and to permit
 *    persons to whom the Software is fur- nished to do so, subject to the
 *    following conditions:
 *
 *    The above copyright notice and this permission notice shall be included
 *    in all copies or substantial portions of the Software.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *    MERCHANTABILITY, FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 *    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
 *    OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 *    THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <libxml/xmlversion.h>

#if (LIBXML_VERSION >= 21400)

/*
 * This file provides compatibility wigh Shell libxml function which were
 * removed from newer libxml.
 */

#ifdef LIBXML_DEBUG_ENABLED

#include <string.h>
#include <stdlib.h>

#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/debugXML.h>
#include <libxml/HTMLtree.h>
#include <libxml/HTMLparser.h>
#include <libxml/xmlerror.h>
#include <libxml/xpathInternals.h>
#include <libxml/uri.h>
#ifdef LIBXML_SCHEMAS_ENABLED
#include <libxml/relaxng.h>
#endif

#include "debugXML_compat.h"

#define DUMP_TEXT_TYPE 1

/*
 * Attributes
 */
#ifndef ATTRIBUTE_UNUSED
  #if __GNUC__ * 100 + __GNUC_MINOR__ >= 207 || defined(__clang__)
    #define ATTRIBUTE_UNUSED __attribute__((unused))
  #else
    #define ATTRIBUTE_UNUSED
  #endif
#endif

/**
 * xmlLsCountNode:
 * @node:  the node to count
 *
 * Count the children of @node.
 *
 * Returns the number of children of @node.
 */
int
xmlLsCountNode(xmlNodePtr node) {
    int ret = 0;
    xmlNodePtr list = NULL;

    if (node == NULL)
	return(0);

    switch (node->type) {
	case XML_ELEMENT_NODE:
	    list = node->children;
	    break;
	case XML_DOCUMENT_NODE:
	case XML_HTML_DOCUMENT_NODE:
	    list = ((xmlDocPtr) node)->children;
	    break;
	case XML_ATTRIBUTE_NODE:
	    list = ((xmlAttrPtr) node)->children;
	    break;
	case XML_TEXT_NODE:
	case XML_CDATA_SECTION_NODE:
	case XML_PI_NODE:
	case XML_COMMENT_NODE:
	    if (node->content != NULL) {
		ret = xmlStrlen(node->content);
            }
	    break;
	case XML_ENTITY_REF_NODE:
	case XML_DOCUMENT_TYPE_NODE:
	case XML_ENTITY_NODE:
	case XML_DOCUMENT_FRAG_NODE:
	case XML_NOTATION_NODE:
	case XML_DTD_NODE:
        case XML_ELEMENT_DECL:
        case XML_ATTRIBUTE_DECL:
        case XML_ENTITY_DECL:
	case XML_NAMESPACE_DECL:
	case XML_XINCLUDE_START:
	case XML_XINCLUDE_END:
	    ret = 1;
	    break;
    }
    for (;list != NULL;ret++)
        list = list->next;
    return(ret);
}

/**
 * xmlLsOneNode:
 * @output:  the FILE * for the output
 * @node:  the node to dump
 *
 * Dump to @output the type and name of @node.
 */
void
xmlLsOneNode(FILE *output, xmlNodePtr node) {
    if (output == NULL) return;
    if (node == NULL) {
	fprintf(output, "NULL\n");
	return;
    }
    switch (node->type) {
	case XML_ELEMENT_NODE:
	    fprintf(output, "-");
	    break;
	case XML_ATTRIBUTE_NODE:
	    fprintf(output, "a");
	    break;
	case XML_TEXT_NODE:
	    fprintf(output, "t");
	    break;
	case XML_CDATA_SECTION_NODE:
	    fprintf(output, "C");
	    break;
	case XML_ENTITY_REF_NODE:
	    fprintf(output, "e");
	    break;
	case XML_ENTITY_NODE:
	    fprintf(output, "E");
	    break;
	case XML_PI_NODE:
	    fprintf(output, "p");
	    break;
	case XML_COMMENT_NODE:
	    fprintf(output, "c");
	    break;
	case XML_DOCUMENT_NODE:
	    fprintf(output, "d");
	    break;
	case XML_HTML_DOCUMENT_NODE:
	    fprintf(output, "h");
	    break;
	case XML_DOCUMENT_TYPE_NODE:
	    fprintf(output, "T");
	    break;
	case XML_DOCUMENT_FRAG_NODE:
	    fprintf(output, "F");
	    break;
	case XML_NOTATION_NODE:
	    fprintf(output, "N");
	    break;
	case XML_NAMESPACE_DECL:
	    fprintf(output, "n");
	    break;
	default:
	    fprintf(output, "?");
    }
    if (node->type != XML_NAMESPACE_DECL) {
	if (node->properties != NULL)
	    fprintf(output, "a");
	else
	    fprintf(output, "-");
	if (node->nsDef != NULL)
	    fprintf(output, "n");
	else
	    fprintf(output, "-");
    }

    fprintf(output, " %8d ", xmlLsCountNode(node));

    switch (node->type) {
	case XML_ELEMENT_NODE:
	    if (node->name != NULL) {
                if ((node->ns != NULL) && (node->ns->prefix != NULL))
                    fprintf(output, "%s:", node->ns->prefix);
		fprintf(output, "%s", (const char *) node->name);
            }
	    break;
	case XML_ATTRIBUTE_NODE:
	    if (node->name != NULL)
		fprintf(output, "%s", (const char *) node->name);
	    break;
	case XML_TEXT_NODE:
	    if (node->content != NULL) {
		xmlDebugDumpString(output, node->content);
            }
	    break;
	case XML_CDATA_SECTION_NODE:
	    break;
	case XML_ENTITY_REF_NODE:
	    if (node->name != NULL)
		fprintf(output, "%s", (const char *) node->name);
	    break;
	case XML_ENTITY_NODE:
	    if (node->name != NULL)
		fprintf(output, "%s", (const char *) node->name);
	    break;
	case XML_PI_NODE:
	    if (node->name != NULL)
		fprintf(output, "%s", (const char *) node->name);
	    break;
	case XML_COMMENT_NODE:
	    break;
	case XML_DOCUMENT_NODE:
	    break;
	case XML_HTML_DOCUMENT_NODE:
	    break;
	case XML_DOCUMENT_TYPE_NODE:
	    break;
	case XML_DOCUMENT_FRAG_NODE:
	    break;
	case XML_NOTATION_NODE:
	    break;
	case XML_NAMESPACE_DECL: {
	    xmlNsPtr ns = (xmlNsPtr) node;

	    if (ns->prefix == NULL)
		fprintf(output, "default -> %s", (char *)ns->href);
	    else
		fprintf(output, "%s -> %s", (char *)ns->prefix,
			(char *)ns->href);
	    break;
	}
	default:
	    if (node->name != NULL)
		fprintf(output, "%s", (const char *) node->name);
    }
    fprintf(output, "\n");
}

/**
 * xmlBoolToText:
 * @boolval: a bool to turn into text
 *
 * Convenient way to turn bool into text
 *
 * Returns a pointer to either "True" or "False"
 */
const char *
xmlBoolToText(int boolval)
{
    if (boolval)
        return("True");
    else
        return("False");
}

#ifdef LIBXML_XPATH_ENABLED
/****************************************************************
 *								*
 *		The XML shell related functions			*
 *								*
 ****************************************************************/

/*
 * TODO: Improvement/cleanups for the XML shell
 *     - allow to shell out an editor on a subpart
 *     - cleanup function registrations (with help) and calling
 *     - provide registration routines
 */

/**
 * xmlShellPrintXPathError:
 * @errorType: valid xpath error id
 * @arg: the argument that cause xpath to fail
 *
 * Print the xpath error to libxml default error channel
 */
void
xmlShellPrintXPathError(int errorType, const char *arg)
{
    const char *default_arg = "Result";

    if (!arg)
        arg = default_arg;

    switch (errorType) {
        case XPATH_UNDEFINED:
            fprintf(stderr,
                            "%s: no such node\n", arg);
            break;

        case XPATH_BOOLEAN:
            fprintf(stderr,
                            "%s is a Boolean\n", arg);
            break;
        case XPATH_NUMBER:
            fprintf(stderr,
                            "%s is a number\n", arg);
            break;
        case XPATH_STRING:
            fprintf(stderr,
                            "%s is a string\n", arg);
            break;
#ifdef LIBXML_XPTR_LOCS_ENABLED
        case XPATH_POINT:
            fprintf(stderr,
                            "%s is a point\n", arg);
            break;
        case XPATH_RANGE:
            fprintf(stderr,
                            "%s is a range\n", arg);
            break;
        case XPATH_LOCATIONSET:
            fprintf(stderr,
                            "%s is a range\n", arg);
            break;
#endif /* LIBXML_XPTR_LOCS_ENABLED */
        case XPATH_USERS:
            fprintf(stderr,
                            "%s is user-defined\n", arg);
            break;
        case XPATH_XSLT_TREE:
            fprintf(stderr,
                            "%s is an XSLT value tree\n", arg);
            break;
    }
#if 0
    fprintf(stderr,
                    "Try casting the result string function (xpath builtin)\n",
                    arg);
#endif
}

/**
 * xmlShellList:
 * @ctxt:  the shell context
 * @arg:  unused
 * @node:  a node
 * @node2:  unused
 *
 * Implements the XML shell function "ls"
 * Does an Unix like listing of the given node (like a directory)
 *
 * Returns 0
 */
int
xmlShellList(xmlShellCtxtPtr ctxt,
             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
             xmlNodePtr node2 ATTRIBUTE_UNUSED)
{
    xmlNodePtr cur;
    if (!ctxt)
        return (0);
    if (node == NULL) {
	fprintf(ctxt->output, "NULL\n");
	return (0);
    }
    if ((node->type == XML_DOCUMENT_NODE) ||
        (node->type == XML_HTML_DOCUMENT_NODE)) {
        cur = ((xmlDocPtr) node)->children;
    } else if (node->type == XML_NAMESPACE_DECL) {
        xmlLsOneNode(ctxt->output, node);
        return (0);
    } else if (node->children != NULL) {
        cur = node->children;
    } else {
        xmlLsOneNode(ctxt->output, node);
        return (0);
    }
    while (cur != NULL) {
        xmlLsOneNode(ctxt->output, cur);
        cur = cur->next;
    }
    return (0);
}

/**
 * xmlShellBase:
 * @ctxt:  the shell context
 * @arg:  unused
 * @node:  a node
 * @node2:  unused
 *
 * Implements the XML shell function "base"
 * dumps the current XML base of the node
 *
 * Returns 0
 */
int
xmlShellBase(xmlShellCtxtPtr ctxt,
             char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
             xmlNodePtr node2 ATTRIBUTE_UNUSED)
{
    xmlChar *base;
    if (!ctxt)
        return 0;
    if (node == NULL) {
	fprintf(ctxt->output, "NULL\n");
	return (0);
    }

    base = xmlNodeGetBase(node->doc, node);

    if (base == NULL) {
        fprintf(ctxt->output, " No base found !!!\n");
    } else {
        fprintf(ctxt->output, "%s\n", base);
        xmlFree(base);
    }
    return (0);
}

/**
 * xmlShellDir:
 * @ctxt:  the shell context
 * @arg:  unused
 * @node:  a node
 * @node2:  unused
 *
 * Implements the XML shell function "dir"
 * dumps information about the node (namespace, attributes, content).
 *
 * Returns 0
 */
int
xmlShellDir(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED,
            char *arg ATTRIBUTE_UNUSED, xmlNodePtr node,
            xmlNodePtr node2 ATTRIBUTE_UNUSED)
{
    if (!ctxt)
        return (0);
    if (node == NULL) {
	fprintf(ctxt->output, "NULL\n");
	return (0);
    }
    if ((node->type == XML_DOCUMENT_NODE) ||
        (node->type == XML_HTML_DOCUMENT_NODE)) {
        xmlDebugDumpDocumentHead(ctxt->output, (xmlDocPtr) node);
    } else if (node->type == XML_ATTRIBUTE_NODE) {
        xmlDebugDumpAttr(ctxt->output, (xmlAttrPtr) node, 0);
    } else {
        xmlDebugDumpOneNode(ctxt->output, node, 0);
    }
    return (0);
}

/**
 * xmlShellDu:
 * @ctxt:  the shell context
 * @arg:  unused
 * @tree:  a node defining a subtree
 * @node2:  unused
 *
 * Implements the XML shell function "du"
 * show the structure of the subtree under node @tree
 * If @tree is null, the command works on the current node.
 *
 * Returns 0 or -1 in case of error
 */
int
xmlShellDu(xmlShellCtxtPtr ctxt,
           char *arg ATTRIBUTE_UNUSED, xmlNodePtr tree,
           xmlNodePtr node2 ATTRIBUTE_UNUSED)
{
    xmlNodePtr node;
    int indent = 0, i;

    if (!ctxt)
	return (-1);

    if (tree == NULL)
        return (-1);
    node = tree;
    while (node != NULL) {
        if ((node->type == XML_DOCUMENT_NODE) ||
            (node->type == XML_HTML_DOCUMENT_NODE)) {
            fprintf(ctxt->output, "/\n");
        } else if (node->type == XML_ELEMENT_NODE) {
            for (i = 0; i < indent; i++)
                fprintf(ctxt->output, "  ");
            if ((node->ns) && (node->ns->prefix))
                fprintf(ctxt->output, "%s:", node->ns->prefix);
            fprintf(ctxt->output, "%s\n", node->name);
        } else {
        }

        /*
         * Browse the full subtree, deep first
         */

        if ((node->type == XML_DOCUMENT_NODE) ||
            (node->type == XML_HTML_DOCUMENT_NODE)) {
            node = ((xmlDocPtr) node)->children;
        } else if ((node->children != NULL)
                   && (node->type != XML_ENTITY_REF_NODE)) {
            /* deep first */
            node = node->children;
            indent++;
        } else if ((node != tree) && (node->next != NULL)) {
            /* then siblings */
            node = node->next;
        } else if (node != tree) {
            /* go up to parents->next if needed */
            while (node != tree) {
                if (node->parent != NULL) {
                    node = node->parent;
                    indent--;
                }
                if ((node != tree) && (node->next != NULL)) {
                    node = node->next;
                    break;
                }
                if (node->parent == NULL) {
                    node = NULL;
                    break;
                }
                if (node == tree) {
                    node = NULL;
                    break;
                }
            }
            /* exit condition */
            if (node == tree)
                node = NULL;
        } else
            node = NULL;
    }
    return (0);
}

/**
 * xmlShellPwd:
 * @ctxt:  the shell context
 * @buffer:  the output buffer
 * @node:  a node
 * @node2:  unused
 *
 * Implements the XML shell function "pwd"
 * Show the full path from the root to the node, if needed building
 * thumblers when similar elements exists at a given ancestor level.
 * The output is compatible with XPath commands.
 *
 * Returns 0 or -1 in case of error
 */
int
xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer,
            xmlNodePtr node, xmlNodePtr node2 ATTRIBUTE_UNUSED)
{
    xmlChar *path;

    if ((node == NULL) || (buffer == NULL))
        return (-1);

    path = xmlGetNodePath(node);
    if (path == NULL)
	return (-1);

    /*
     * This test prevents buffer overflow, because this routine
     * is only called by xmlShell, in which the second argument is
     * 500 chars long.
     * It is a dirty hack before a cleaner solution is found.
     * Documentation should mention that the second argument must
     * be at least 500 chars long, and could be stripped if too long.
     */
    snprintf(buffer, 499, "%s", path);
    buffer[499] = '0';
    xmlFree(path);

    return (0);
}

#endif /* LIBXML_XPATH_ENABLED */

#endif /* LIBXML_DEBUG_ENABLED */

#endif /* LIBXML_VERSION >= 21400 */
