// gcc -o xmlpath xmlpath.c $(xml2-config --cflags --libs) -lxslt -lxml2 -lpthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/HTMLparser.h>
#include <libxml/uri.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>

#define MAX_DOCS 256
#define MAX_NODES 65536
#define MULTITHREAD_THRESHOLD 4   // 多线程阈值

typedef struct {
    int doc_id;
    xmlDocPtr doc;
    int valid;
} DocEntry;

typedef struct {
    int node_id;
    xmlNodePtr node;
    int doc_id;      // 所属文档ID，用于快速清理
    int valid;
} NodeEntry;

DocEntry docs[MAX_DOCS];
NodeEntry nodes[MAX_NODES];

pthread_mutex_t node_mutex = PTHREAD_MUTEX_INITIALIZER;  // 保护节点表

// 初始化
void init() {
    LIBXML_TEST_VERSION
    memset(docs, 0, sizeof(docs));
    memset(nodes, 0, sizeof(nodes));
}

// 清理所有资源
void cleanup() {
    for (int i = 1; i < MAX_DOCS; i++) {
        if (docs[i].valid) {
            xmlFreeDoc(docs[i].doc);
        }
    }
    xmlCleanupParser();
}

// 分配文档句柄
int alloc_doc(xmlDocPtr doc) {
    for (int i = 1; i < MAX_DOCS; i++) {
        if (!docs[i].valid) {
            docs[i].doc = doc;
            docs[i].doc_id = i;
            docs[i].valid = 1;
            return i;
        }
    }
    return -1;
}

// 释放文档句柄及相关节点句柄
void free_doc(int doc_id) {
    if (doc_id < 1 || doc_id >= MAX_DOCS || !docs[doc_id].valid) return;
    xmlFreeDoc(docs[doc_id].doc);
    docs[doc_id].valid = 0;
    // 释放所有属于该文档的节点句柄
    for (int i = 1; i < MAX_NODES; i++) {
        if (nodes[i].valid && nodes[i].doc_id == doc_id) {
            nodes[i].valid = 0;
        }
    }
}

// 分配节点句柄（线程安全）
int alloc_node(xmlNodePtr node, int doc_id) {
    pthread_mutex_lock(&node_mutex);
    // 先查找是否已存在相同节点指针
    for (int i = 1; i < MAX_NODES; i++) {
        if (nodes[i].valid && nodes[i].node == node && nodes[i].doc_id == doc_id) {
            pthread_mutex_unlock(&node_mutex);
            return i;
        }
    }
    // 不存在，找空闲位置分配
    for (int i = 1; i < MAX_NODES; i++) {
        if (!nodes[i].valid) {
            nodes[i].node = node;
            nodes[i].doc_id = doc_id;
            nodes[i].valid = 1;
            nodes[i].node_id = i;
            pthread_mutex_unlock(&node_mutex);
            return i;
        }
    }
    pthread_mutex_unlock(&node_mutex);
    return -1;
}

// 使指向特定节点的所有句柄无效
void invalidate_node_handles(xmlNodePtr node) {
    pthread_mutex_lock(&node_mutex);
    for (int i = 1; i < MAX_NODES; i++) {
        if (nodes[i].valid && nodes[i].node == node) {
            nodes[i].valid = 0;
        }
    }
    pthread_mutex_unlock(&node_mutex);
}

// 释放节点句柄（仅标记无效，不释放节点内存）
void free_node(int node_id) {
    if (node_id < 1 || node_id >= MAX_NODES || !nodes[node_id].valid) return;
    pthread_mutex_lock(&node_mutex);
    nodes[node_id].valid = 0;
    pthread_mutex_unlock(&node_mutex);
}

// 根据节点句柄获取节点指针
xmlNodePtr get_node(int node_id) {
    if (node_id < 1 || node_id >= MAX_NODES || !nodes[node_id].valid) return NULL;
    return nodes[node_id].node;
}

// 根据文档句柄获取文档指针
xmlDocPtr get_doc(int doc_id) {
    if (doc_id < 1 || doc_id >= MAX_DOCS || !docs[doc_id].valid) return NULL;
    return docs[doc_id].doc;
}

// 解析带前缀的句柄字符串，返回ID，并通过type输出类型（'d'或'n'）
int parse_handle(const char *str, char *type) {
    if (!str || strlen(str) < 2) return -1;
    if (str[0] == 'd' || str[0] == 'D') {
        *type = 'd';
    } else if (str[0] == 'n' || str[0] == 'N') {
        *type = 'n';
    } else {
        return -1;
    }
    char *endptr;
    long id = strtol(str + 1, &endptr, 16);
    if (*endptr != '\0' || id <= 0) return -1;
    if (*type == 'd' && id >= MAX_DOCS) return -1;
    if (*type == 'n' && id >= MAX_NODES) return -1;
    return (int)id;
}

// 将 XPath 结果格式化为字符串（动态分配，调用者需 free）
char* format_xpath_result(xmlXPathObjectPtr result, int doc_id) {
    if (!result) return strdup("NULL");
    char *str = NULL;
    switch (result->type) {
        case XPATH_NODESET: {
            xmlNodeSetPtr nodeset = result->nodesetval;
            if (!nodeset || nodeset->nodeNr == 0) {
                str = strdup("");
            } else {
                // 使用 open_memstream 构建字符串
                FILE *mem = open_memstream(&str, &(size_t){0});
                if (!mem) return strdup("error");
                int first = 1;
                for (int i = 0; i < nodeset->nodeNr; i++) {
                    xmlNodePtr node = nodeset->nodeTab[i];
                    int node_id = alloc_node(node, doc_id);
                    if (node_id > 0) {
                        if (!first) fprintf(mem, " ");
                        fprintf(mem, "n%x", node_id);
                        first = 0;
                    }
                }
                fclose(mem);
                if (!str) str = strdup("");
            }
            break;
        }
        case XPATH_BOOLEAN:
            asprintf(&str, "%s", result->boolval ? "true" : "false");
            break;
        case XPATH_NUMBER:
            asprintf(&str, "%g", result->floatval);
            break;
        case XPATH_STRING:
            asprintf(&str, "%s", result->stringval ? (char*)result->stringval : "");
            break;
        default:
            asprintf(&str, "Unsupported XPath result type: %d", result->type);
    }
    return str;
}

// 处理 XPath 查询结果（直接打印）
void handle_xpath_result(xmlXPathObjectPtr result, int doc_id) {
    char *out = format_xpath_result(result, doc_id);
    if (out) {
        printf("%s\n", out);
        free(out);
    }
}

// 命令：open <type> <uri>
void cmd_open(char *type_str, char *uri) {
    xmlDocPtr doc = NULL;
    if (strcmp(type_str, "xml") == 0) {
        doc = xmlReadFile(uri, NULL, XML_PARSE_RECOVER);
    } else if (strcmp(type_str, "html") == 0) {
        doc = htmlReadFile(uri, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
    } else {
        fprintf(stderr, "Invalid type: %s (use xml or html)\n", type_str);
        return;
    }
    if (!doc) {
        fprintf(stderr, "Failed to open %s\n", uri);
        return;
    }
    int doc_id = alloc_doc(doc);
    if (doc_id < 0) {
        fprintf(stderr, "No free document slot\n");
        xmlFreeDoc(doc);
        return;
    }
    printf("d%x\n", doc_id);
}

// 单句柄 xpath 执行（直接打印结果）
void cmd_xpath(char *handle_str, char *expr) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0) {
        fprintf(stderr, "Invalid handle\n");
        return;
    }

    xmlDocPtr doc = NULL;
    xmlNodePtr node = NULL;
    int doc_id = -1;

    if (type == 'd') {
        doc = get_doc(id);
        if (!doc) {
            fprintf(stderr, "Document not found\n");
            return;
        }
        doc_id = id;
    } else if (type == 'n') {
        node = get_node(id);
        if (!node) {
            fprintf(stderr, "Node not found\n");
            return;
        }
        doc = node->doc;
        doc_id = nodes[id].doc_id;
        if (!doc) {
            fprintf(stderr, "Node's document not found\n");
            return;
        }
    } else {
        fprintf(stderr, "Unknown handle type\n");
        return;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx) {
        fprintf(stderr, "Failed to create XPath context\n");
        return;
    }

    if (type == 'n' && node) {
        if (xmlXPathSetContextNode(node, ctx) != 0) {
            fprintf(stderr, "Failed to set context node\n");
            xmlXPathFreeContext(ctx);
            return;
        }
    }

    xmlXPathObjectPtr result = xmlXPathEvalExpression(BAD_CAST expr, ctx);
    if (!result) {
        fprintf(stderr, "XPath evaluation failed\n");
        xmlXPathFreeContext(ctx);
        return;
    }

    handle_xpath_result(result, doc_id);
    xmlXPathFreeObject(result);
    xmlXPathFreeContext(ctx);
}

// 线程参数结构体
typedef struct {
    const char *expr;        // 仅 xpath 使用
    char *handle_str;
    int index;
    char **result;           // 结果数组
} ThreadArg;

// xpath 线程函数
void* xpath_thread_func(void *arg) {
    ThreadArg *ta = (ThreadArg*)arg;
    char *handle_str = ta->handle_str;
    const char *expr = ta->expr;
    int index = ta->index;
    char **result = ta->result;

    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0) {
        asprintf(&result[index], "Invalid handle: %s", handle_str);
        return NULL;
    }

    xmlDocPtr doc = NULL;
    xmlNodePtr node = NULL;
    int doc_id = -1;

    if (type == 'd') {
        doc = get_doc(id);
        if (!doc) {
            asprintf(&result[index], "Document not found: %s", handle_str);
            return NULL;
        }
        doc_id = id;
    } else if (type == 'n') {
        node = get_node(id);
        if (!node) {
            asprintf(&result[index], "Node not found: %s", handle_str);
            return NULL;
        }
        doc = node->doc;
        doc_id = nodes[id].doc_id;
        if (!doc) {
            asprintf(&result[index], "Node's document not found");
            return NULL;
        }
    } else {
        asprintf(&result[index], "Unknown handle type");
        return NULL;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx) {
        asprintf(&result[index], "Failed to create XPath context");
        return NULL;
    }

    if (type == 'n' && node) {
        if (xmlXPathSetContextNode(node, ctx) != 0) {
            asprintf(&result[index], "Failed to set context node");
            xmlXPathFreeContext(ctx);
            return NULL;
        }
    }

    xmlXPathObjectPtr res = xmlXPathEvalExpression(BAD_CAST expr, ctx);
    if (!res) {
        asprintf(&result[index], "XPath evaluation failed");
        xmlXPathFreeContext(ctx);
        return NULL;
    }

    char *out = format_xpath_result(res, doc_id);
    xmlXPathFreeObject(res);
    xmlXPathFreeContext(ctx);

    result[index] = out ? out : strdup("");
    return NULL;
}

// text 线程函数
void* text_thread_func(void *arg) {
    ThreadArg *ta = (ThreadArg*)arg;
    char *handle_str = ta->handle_str;
    int index = ta->index;
    char **result = ta->result;

    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        asprintf(&result[index], "Invalid node handle: %s\n", handle_str);
        return NULL;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        asprintf(&result[index], "Node not found: %s\n", handle_str);
        return NULL;
    }
    xmlChar *content = xmlNodeGetContent(node);
    if (content) {
        asprintf(&result[index], "%s\n", (char*)content);
        xmlFree(content);
    } else {
        result[index] = strdup("");
    }
    return NULL;
}

// 多句柄 xpath 命令处理
void cmd_xpath_multiple(char *rest) {
    char *copy = strdup(rest);
    if (!copy) {
        fprintf(stderr, "Out of memory\n");
        return;
    }
    char *tokens[256];
    int token_count = 0;
    char *p = strtok(copy, " \t");
    while (p && token_count < 256) {
        tokens[token_count++] = p;
        p = strtok(NULL, " \t");
    }
    if (token_count == 0) {
        fprintf(stderr, "Usage: xpath <expr> <id1> <id2> ...\n");
        free(copy);
        return;
    }

    // 从后往前识别句柄
    int first_handle = token_count;
    for (int i = token_count - 1; i >= 0; i--) {
        char type;
        int id = parse_handle(tokens[i], &type);
        if (id > 0 && (type == 'd' || type == 'n')) {
            first_handle = i;
        } else {
            break;
        }
    }
    if (first_handle == 0) {
        fprintf(stderr, "No expression provided\n");
        free(copy);
        return;
    }

    // 构建表达式字符串
    char *expr = NULL;
    size_t len = 0;
    for (int i = 0; i < first_handle; i++) {
        len += strlen(tokens[i]) + 1;
    }
    expr = malloc(len + 1);
    if (!expr) {
        fprintf(stderr, "Out of memory\n");
        free(copy);
        return;
    }
    expr[0] = '\0';
    for (int i = 0; i < first_handle; i++) {
        if (i > 0) strcat(expr, " ");
        strcat(expr, tokens[i]);
    }

    int num_handles = token_count - first_handle;
    char **handles = &tokens[first_handle];

    if (num_handles <= MULTITHREAD_THRESHOLD) {
        // 顺序处理
        for (int i = 0; i < num_handles; i++) {
            cmd_xpath(handles[i], expr);
        }
    } else {
        // 多线程处理
        pthread_t threads[num_handles];
        ThreadArg args[num_handles];
        char *results[num_handles];
        for (int i = 0; i < num_handles; i++) {
            args[i].expr = expr;
            args[i].handle_str = handles[i];
            args[i].index = i;
            args[i].result = results;
        }
        for (int i = 0; i < num_handles; i++) {
            pthread_create(&threads[i], NULL, xpath_thread_func, &args[i]);
        }
        for (int i = 0; i < num_handles; i++) {
            pthread_join(threads[i], NULL);
        }
        for (int i = 0; i < num_handles; i++) {
            if (results[i]) {
                printf("%s\n", results[i]);
                free(results[i]);
            } else {
                printf("\n");
            }
        }
    }
    free(expr);
    free(copy);
}

// 单句柄 text 命令
void cmd_text(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    xmlChar *content = xmlNodeGetContent(node);
    if (content) {
        printf("%s\n", (char*)content);
        xmlFree(content);
    } else {
        printf("\n");
    }
}

// 多句柄 text 命令处理
void cmd_text_multiple(char *rest) {
    char *copy = strdup(rest);
    if (!copy) {
        fprintf(stderr, "Out of memory\n");
        return;
    }
    char *tokens[256];
    int token_count = 0;
    char *p = strtok(copy, " \t");
    while (p && token_count < 256) {
        tokens[token_count++] = p;
        p = strtok(NULL, " \t");
    }
    if (token_count == 0) {
        fprintf(stderr, "Usage: text <id1> <id2> ...\n");
        free(copy);
        return;
    }
    int num_handles = token_count;
    char **handles = tokens;

    if (num_handles <= MULTITHREAD_THRESHOLD) {
        for (int i = 0; i < num_handles; i++) {
            cmd_text(handles[i]);
        }
    } else {
        pthread_t threads[num_handles];
        ThreadArg args[num_handles];
        char *results[num_handles];
        for (int i = 0; i < num_handles; i++) {
            args[i].handle_str = handles[i];
            args[i].index = i;
            args[i].result = results;
            args[i].expr = NULL;
        }
        for (int i = 0; i < num_handles; i++) {
            pthread_create(&threads[i], NULL, text_thread_func, &args[i]);
        }
        for (int i = 0; i < num_handles; i++) {
            pthread_join(threads[i], NULL);
        }
        for (int i = 0; i < num_handles; i++) {
            if (results[i]) {
                printf("%s\n", results[i]);
                free(results[i]);
            } else {
                printf("\n");
            }
        }
    }
    free(copy);
}

// 命令：get <node-handle>
void cmd_get(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    xmlBufferPtr buf = xmlBufferCreate();
    xmlNodeDump(buf, node->doc, node, 0, 1);
    printf("%s\n", (char*)buf->content);
    xmlBufferFree(buf);
}

// 命令：close <handle>
void cmd_close(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0) {
        fprintf(stderr, "Invalid handle\n");
        return;
    }
    if (type == 'd') {
        free_doc(id);
        printf("OK\n");
    } else {
        free_node(id);
        printf("OK\n");
    }
}

// 以下为节点导航命令（保持不变）
void cmd_parent(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    xmlNodePtr parent = node->parent;
    if (!parent) {
        printf("\n");
        return;
    }
    int parent_id = alloc_node(parent, nodes[id].doc_id);
    if (parent_id > 0) {
        printf("n%x\n", parent_id);
    } else {
        fprintf(stderr, "Failed to allocate handle for parent\n");
    }
}

void output_node_handle(xmlNodePtr node, int doc_id) {
    if (!node) {
        printf("\n");
        return;
    }
    int node_id = alloc_node(node, doc_id);
    if (node_id > 0) {
        printf("n%x\n", node_id);
    } else {
        fprintf(stderr, "Failed to allocate node handle\n");
    }
}

void cmd_first_child(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    output_node_handle(node->children, nodes[id].doc_id);
}

void cmd_last_child(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    output_node_handle(node->last, nodes[id].doc_id);
}

void cmd_next_sibling(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    output_node_handle(node->next, nodes[id].doc_id);
}

void cmd_prev_sibling(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    output_node_handle(node->prev, nodes[id].doc_id);
}

void cmd_children(char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        fprintf(stderr, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(id);
    if (!node) {
        fprintf(stderr, "Node not found\n");
        return;
    }
    xmlNodePtr child = node->children;
    int first = 1;
    while (child) {
        int child_id = alloc_node(child, nodes[id].doc_id);
        if (child_id > 0) {
            if (!first) printf(" ");
            printf("n%x", child_id);
            first = 0;
        }
        child = child->next;
    }
    printf("\n");
}

// 主循环：从stdin读取命令
void repl() {
    char line[4096];
    while (fgets(line, sizeof(line), stdin)) {
        line[strcspn(line, "\n")] = '\0';
        if (strlen(line) == 0) continue;

        char *cmd = strtok(line, " \t");
        if (!cmd) continue;

        if (strcmp(cmd, "shutdown") == 0) {
            printf("Shutting down server...\n");
            fflush(stdout);
            killpg(getpgrp(), SIGTERM);
            cleanup();
            exit(0);
        } else if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "exit") == 0) {
            fflush(stdout);
            cleanup();
            break;
        } else if (strcmp(cmd, "open") == 0) {
            char *type = strtok(NULL, " \t");
            char *uri = strtok(NULL, "");
            if (!type || !uri) {
                fprintf(stderr, "Usage: open <xml|html> <uri>\n");
                continue;
            }
            while (*uri == ' ' || *uri == '\t') uri++;
            cmd_open(type, uri);
        } else if (strcmp(cmd, "xpath") == 0) {
            char *rest = strtok(NULL, "\n");  // 取剩余部分
            if (!rest) {
                fprintf(stderr, "Usage: xpath <expr> <id1> <id2> ...\n");
                continue;
            }
            while (*rest == ' ' || *rest == '\t') rest++;
            cmd_xpath_multiple(rest);
        } else if (strcmp(cmd, "get") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: get <node-handle>\n");
                continue;
            }
            cmd_get(handle);
        } else if (strcmp(cmd, "text") == 0) {
            char *rest = strtok(NULL, "\n");
            if (!rest) {
                fprintf(stderr, "Usage: text <id1> <id2> ...\n");
                continue;
            }
            while (*rest == ' ' || *rest == '\t') rest++;
            cmd_text_multiple(rest);
        } else if (strcmp(cmd, "parent") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: parent <node-handle>\n");
                continue;
            }
            cmd_parent(handle);
        } else if (strcmp(cmd, "first-child") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: first-child <node-handle>\n");
                continue;
            }
            cmd_first_child(handle);
        } else if (strcmp(cmd, "last-child") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: last-child <node-handle>\n");
                continue;
            }
            cmd_last_child(handle);
        } else if (strcmp(cmd, "next-sibling") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: next-sibling <node-handle>\n");
                continue;
            }
            cmd_next_sibling(handle);
        } else if (strcmp(cmd, "prev-sibling") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: prev-sibling <node-handle>\n");
                continue;
            }
            cmd_prev_sibling(handle);
        } else if (strcmp(cmd, "children") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: children <node-handle>\n");
                continue;
            }
            cmd_children(handle);
        } else if (strcmp(cmd, "close") == 0) {
            char *handle = strtok(NULL, " \t");
            if (!handle) {
                fprintf(stderr, "Usage: close <handle>\n");
                continue;
            }
            cmd_close(handle);
        } else {
            fprintf(stderr, "Unknown command: %s\n", cmd);
        }
        fflush(stdout);
    }
    if (feof(stdin)) {
        fprintf(stderr, "Connection closed.\n");
    }
}

void signalHandler(int sig) {
    fflush(stdout);
    cleanup();
    _exit(sig);
}

int main(int argc, char **argv) {
    init();
    signal(SIGINT, signalHandler);
    signal(SIGTERM, signalHandler);
    repl();
    cleanup();
    return 0;
}


//3. CSS选择器（暂未实现）
//可以在 cmd_css 中实现一个简单的转换器，例如：
//tag → //tag
//.class → //*[contains(concat(' ', @class, ' '), ' class ')]
//#id → //*[@id='id']
//tag.class → //tag[contains(concat(' ', @class, ' '), ' class ')]
//组合器如 >、+ 等需要更复杂的解析，建议初期只支持单步选择器，或直接提示用户改用XPath。
