Subversion Repositories f9daq

Rev

Rev 195 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /********************************************************************\
  2.  
  3.    Name:         mxml.c
  4.    Created by:   Stefan Ritt
  5.    Copyright 2000 + Stefan Ritt
  6.  
  7.    Contents:     Midas XML Library
  8.  
  9.    This is a simple implementation of XML functions for writing and
  10.    reading XML files. For writing an XML file from scratch, following
  11.    functions can be used:
  12.  
  13.    writer = mxml_open_file(file_name);
  14.      mxml_start_element(writer, name);
  15.      mxml_write_attribute(writer, name, value);
  16.      mxml_write_value(writer, value);
  17.      mxml_end_element(writer);
  18.      ...
  19.    mxml_close_file(writer);
  20.  
  21.    To read an XML file, the function
  22.  
  23.    tree = mxml_parse_file(file_name, error, sizeof(error));
  24.  
  25.    is used. It parses the complete XML file and stores it in a
  26.    hierarchical tree in memory. Nodes in that tree can be searched
  27.    for with
  28.  
  29.    mxml_find_node(tree, xml_path);
  30.  
  31.    or
  32.  
  33.    mxml_find_nodes(tree, xml_path, &nodelist);
  34.  
  35.    which support a subset of the XPath specification. Another set of
  36.    functions is availabe to retrieve attributes and values from nodes
  37.    in the tree and for manipulating nodes, like replacing, adding and
  38.    deleting nodes.
  39.    
  40.    
  41.    This file is part of MIDAS XML Library.
  42.  
  43.    MIDAS XML Library is free software: you can redistribute it and/or modify
  44.    it under the terms of the GNU General Public License as published by
  45.    the Free Software Foundation, either version 3 of the License, or
  46.    (at your option) any later version.
  47.  
  48.    MIDAS XML Library is distributed in the hope that it will be useful,
  49.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  50.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  51.    GNU General Public License for more details.
  52.  
  53.    You should have received a copy of the GNU General Public License
  54.    along with MIDAS XML Library.  If not, see <http://www.gnu.org/licenses/>.
  55.  
  56. \********************************************************************/
  57.  
  58. #include <stdio.h>
  59. #include <fcntl.h>
  60. #include <string.h>
  61. #include <assert.h>
  62.  
  63. #ifdef _MSC_VER
  64.  
  65. #include <windows.h>
  66. #include <io.h>
  67. #include <time.h>
  68.  
  69. #pragma warning( disable: 4996) /* disable "deprecated" warning */
  70.  
  71. #else
  72.  
  73. #define TRUE 1
  74. #define FALSE 0
  75.  
  76. #ifndef O_TEXT
  77. #define O_TEXT 0
  78. #define O_BINARY 0
  79. #endif
  80.  
  81. #include <stdlib.h>
  82. #include <unistd.h>
  83. #include <ctype.h>
  84. #include <stdarg.h>
  85. #include <errno.h>
  86. #ifndef OS_VXWORKS
  87. #include <sys/time.h>
  88. #endif
  89. #include <time.h>
  90.  
  91. #endif
  92.  
  93. #include "mxml.h"
  94. #ifndef HAVE_STRLCPY
  95. #include "strlcpy.h"
  96. #endif
  97.  
  98. #define XML_INDENT "  "
  99.  
  100. #if defined(__GNUC__) && !defined(__MAKECINT__)
  101. #   define MXML_GNUC_PRINTF( format_idx, arg_idx )          \
  102.    __attribute__((format (printf, format_idx, arg_idx)))
  103. #   define MXML_GNUC_SCANF( format_idx, arg_idx )           \
  104.    __attribute__((format (scanf, format_idx, arg_idx)))
  105. #   define MXML_GNUC_FORMAT( arg_idx )                      \
  106.    __attribute__((format_arg (arg_idx)))
  107. #else
  108. #   define MXML_GNUC_PRINTF( format_idx, arg_idx )
  109. #   define MXML_GNUC_SCANF( format_idx, arg_idx )
  110. #   define MXML_GNUC_FORMAT( arg_idx )
  111. #endif
  112.  
  113. static int mxml_suppress_date_flag = 0; /* suppress writing date at the top of file. */
  114.  
  115. /* local prototypes */
  116. static PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...) MXML_GNUC_PRINTF(7, 8);
  117. static void mxml_encode(char *src, int size, int translate);
  118. static void mxml_decode(char *str);
  119. static int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent);
  120. static int mxml_write_line(MXML_WRITER *writer, const char *line);
  121. static int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent);
  122. static int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found);
  123. static int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
  124. static void *mxml_malloc(size_t size);
  125. static void *mxml_realloc(void *p, size_t size);
  126. static void mxml_free(void *p);
  127. static void mxml_deallocate(void);
  128.  
  129. /*------------------------------------------------------------------*/
  130.  
  131. static char *_encode_buffer = NULL;
  132. static char *_data_enc = NULL;
  133.  
  134. /*------------------------------------------------------------------*/
  135.  
  136. void *mxml_malloc(size_t size)
  137. {
  138.    return malloc(size);
  139. }
  140.  
  141. /*------------------------------------------------------------------*/
  142.  
  143. void *mxml_realloc(void *p, size_t size)
  144. {
  145.    return realloc(p, size);
  146. }
  147.  
  148. /*------------------------------------------------------------------*/
  149.  
  150. void mxml_free(void *p)
  151. {
  152.    free(p);
  153. }
  154.  
  155. /*------------------------------------------------------------------*/
  156.  
  157. void mxml_deallocate(void)
  158. {
  159.    if (_encode_buffer != NULL) {
  160.       mxml_free(_encode_buffer);
  161.       _encode_buffer = NULL;
  162.    }
  163.    if (_data_enc != NULL) {
  164.       mxml_free(_data_enc);
  165.       _data_enc = NULL;
  166.    }
  167. }
  168.  
  169. /*------------------------------------------------------------------*/
  170.  
  171. int mxml_write_line(MXML_WRITER *writer, const char *line)
  172. {
  173.    int len;
  174.    
  175.    len = (int)strlen(line);
  176.  
  177.    if (writer->buffer) {
  178.       if (writer->buffer_len + len >= writer->buffer_size) {
  179.          writer->buffer_size += 10000;
  180.          writer->buffer = (char *)mxml_realloc(writer->buffer, writer->buffer_size);
  181.       }
  182.       strcpy(writer->buffer + writer->buffer_len, line);
  183.       writer->buffer_len += len;
  184.       return len;
  185.    } else {
  186.       return (int)write(writer->fh, line, len);
  187.    }
  188.  
  189.    return 0;
  190. }
  191.  
  192. /*------------------------------------------------------------------*/
  193.  
  194. /**
  195.  * open a memory buffer and write XML header
  196.  */
  197. MXML_WRITER *mxml_open_buffer(void)
  198. {
  199.    char str[256], line[1000];
  200.    time_t now;
  201.    MXML_WRITER *writer;
  202.  
  203.    writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER));
  204.    memset(writer, 0, sizeof(MXML_WRITER));
  205.    writer->translate = 1;
  206.  
  207.    writer->buffer_size = 10000;
  208.    writer->buffer = (char *)mxml_malloc(10000);
  209.    writer->buffer[0] = 0;
  210.    writer->buffer_len = 0;
  211.  
  212.    /* write XML header */
  213.    strcpy(line, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
  214.    mxml_write_line(writer, line);
  215.    time(&now);
  216.    strcpy(str, ctime(&now));
  217.    str[24] = 0;
  218.    sprintf(line, "<!-- created by MXML on %s -->\n", str);
  219.    if (mxml_suppress_date_flag == 0)
  220.       mxml_write_line(writer, line);
  221.  
  222.    /* initialize stack */
  223.    writer->level = 0;
  224.    writer->element_is_open = 0;
  225.  
  226.    return writer;
  227. }
  228.  
  229. /*------------------------------------------------------------------*/
  230.  
  231. /**
  232.  * suppress writing date at the top of file.
  233.  */
  234. void mxml_suppress_date(int suppress)
  235. {
  236.    mxml_suppress_date_flag = suppress;
  237. }
  238.  
  239. /*------------------------------------------------------------------*/
  240.  
  241. /**
  242.  * open a file and write XML header
  243.  */
  244. MXML_WRITER *mxml_open_file(const char *file_name)
  245. {
  246.    char str[256], line[1000];
  247.    time_t now;
  248.    MXML_WRITER *writer;
  249.  
  250.    writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER));
  251.    memset(writer, 0, sizeof(MXML_WRITER));
  252.    writer->translate = 1;
  253.  
  254.    writer->fh = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT, 0644);
  255.  
  256.    if (writer->fh == -1) {
  257.       sprintf(line, "Unable to open file \"%s\": ", file_name);
  258.       perror(line);
  259.       mxml_free(writer);
  260.       return NULL;
  261.    }
  262.  
  263.    /* write XML header */
  264.    strcpy(line, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
  265.    mxml_write_line(writer, line);
  266.    time(&now);
  267.    strcpy(str, ctime(&now));
  268.    str[24] = 0;
  269.    sprintf(line, "<!-- created by MXML on %s -->\n", str);
  270.    if (mxml_suppress_date_flag == 0)
  271.       mxml_write_line(writer, line);
  272.  
  273.    /* initialize stack */
  274.    writer->level = 0;
  275.    writer->element_is_open = 0;
  276.  
  277.    return writer;
  278. }
  279.  
  280. /*------------------------------------------------------------------*/
  281.  
  282. /**
  283.  * convert '<' '>' '&' '"' ''' into &xx;
  284.  */
  285. void mxml_encode(char *src, int size, int translate)
  286. {
  287.    char *ps, *pd;
  288.    static int buffer_size = 1000;
  289.  
  290.    assert(size);
  291.  
  292.    if (_encode_buffer == NULL) {
  293.       _encode_buffer = (char *) mxml_malloc(buffer_size);
  294.       atexit(mxml_deallocate);
  295.    }
  296.  
  297.    if (size > buffer_size) {
  298.       _encode_buffer = (char *) mxml_realloc(_encode_buffer, size*2);
  299.       buffer_size = size;
  300.    }
  301.  
  302.    pd = _encode_buffer;
  303.    for (ps = src ; *ps && (size_t)pd - (size_t)_encode_buffer < (size_t)(size-10) ; ps++) {
  304.  
  305.      if (translate) { /* tranlate "<", ">", "&", """, "'" */
  306.          switch (*ps) {
  307.          case '<':
  308.             strcpy(pd, "&lt;");
  309.             pd += 4;
  310.             break;
  311.          case '>':
  312.             strcpy(pd, "&gt;");
  313.             pd += 4;
  314.             break;
  315.          case '&':
  316.             strcpy(pd, "&amp;");
  317.             pd += 5;
  318.             break;
  319.          case '\"':
  320.             strcpy(pd, "&quot;");
  321.             pd += 6;
  322.             break;
  323.          case '\'':
  324.             strcpy(pd, "&apos;");
  325.             pd += 6;
  326.             break;
  327.          default:
  328.             *pd++ = *ps;
  329.          }
  330.       } else {
  331.        switch (*ps) { /* translate only illegal XML characters "<" and "&" */
  332.          case '<':
  333.             strcpy(pd, "&lt;");
  334.             pd += 4;
  335.             break;
  336.          case '&':
  337.             strcpy(pd, "&amp;");
  338.             pd += 5;
  339.             break;
  340.          default:
  341.             *pd++ = *ps;
  342.          }
  343.       }
  344.    }
  345.    *pd = 0;
  346.  
  347.    strlcpy(src, _encode_buffer, size);
  348. }
  349.  
  350. /*------------------------------------------------------------------*/
  351.  
  352. /**
  353.  * reverse of mxml_encode, strip leading or trailing '"'
  354.  */
  355. void mxml_decode(char *str)
  356. {
  357.    char *p;
  358.  
  359.    p = str;
  360.    while ((p = strchr(p, '&')) != NULL) {
  361.       if (strncmp(p, "&lt;", 4) == 0) {
  362.          *(p++) = '<';
  363.          memmove(p, p+3, strlen(p+3) + 1);
  364.       }
  365.       else if (strncmp(p, "&gt;", 4) == 0) {
  366.          *(p++) = '>';
  367.          memmove(p, p+3, strlen(p+3) + 1);
  368.       }
  369.       else if (strncmp(p, "&amp;", 5) == 0) {
  370.          *(p++) = '&';
  371.          memmove(p, p+4, strlen(p+4) + 1);
  372.       }
  373.       else if (strncmp(p, "&quot;", 6) == 0) {
  374.          *(p++) = '\"';
  375.          memmove(p, p+5, strlen(p+5) + 1);
  376.       }
  377.       else if (strncmp(p, "&apos;", 6) == 0) {
  378.          *(p++) = '\'';
  379.          memmove(p, p+5, strlen(p+5) + 1);
  380.       }
  381.       else {
  382.          p++; // skip unknown entity
  383.       }
  384.    }
  385. /*   if (str[0] == '\"' && str[strlen(str)-1] == '\"') {
  386.       memmove(str, str+1, strlen(str+1) + 1);
  387.       str[strlen(str)-1] = 0;
  388.    }*/
  389. }
  390.  
  391. /*------------------------------------------------------------------*/
  392.  
  393. /**
  394.  * set translation of <,>,",',&, on/off in writer
  395.  */
  396. int mxml_set_translate(MXML_WRITER *writer, int flag)
  397. {
  398.    int old_flag;
  399.  
  400.    old_flag = writer->translate;
  401.    writer->translate = flag;
  402.    return old_flag;
  403. }
  404. /*------------------------------------------------------------------*/
  405.  
  406. /**
  407.  * start a new XML element, must be followed by mxml_end_elemnt
  408.  */
  409. int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent)
  410. {
  411.    int i;
  412.    char line[1000], name_enc[1000];
  413.  
  414.    if (writer->element_is_open) {
  415.       mxml_write_line(writer, ">\n");
  416.       writer->element_is_open = FALSE;
  417.    }
  418.  
  419.    line[0] = 0;
  420.    if (indent)
  421.       for (i=0 ; i<writer->level ; i++)
  422.          strlcat(line, XML_INDENT, sizeof(line));
  423.    strlcat(line, "<", sizeof(line));
  424.    strlcpy(name_enc, name, sizeof(name_enc));
  425.    mxml_encode(name_enc, sizeof(name_enc), writer->translate);
  426.    strlcat(line, name_enc, sizeof(line));
  427.  
  428.    /* put element on stack */
  429.    if (writer->level == 0)
  430.       writer->stack = (char **)mxml_malloc(sizeof(char *));
  431.    else
  432.       writer->stack = (char **)mxml_realloc(writer->stack, sizeof(char *)*(writer->level+1));
  433.    
  434.    writer->stack[writer->level] = (char *) mxml_malloc(strlen(name_enc)+1);
  435.    strcpy(writer->stack[writer->level], name_enc);
  436.    writer->level++;
  437.    writer->element_is_open = TRUE;
  438.    writer->data_was_written = FALSE;
  439.  
  440.    return mxml_write_line(writer, line) == (int)strlen(line);
  441. }
  442.  
  443. /*------------------------------------------------------------------*/
  444.  
  445. int mxml_start_element(MXML_WRITER *writer, const char *name)
  446. {
  447.    return mxml_start_element1(writer, name, TRUE);
  448. }
  449.  
  450. /*------------------------------------------------------------------*/
  451.  
  452. int mxml_start_element_noindent(MXML_WRITER *writer, const char *name)
  453. {
  454.    return mxml_start_element1(writer, name, FALSE);
  455. }
  456.  
  457. /*------------------------------------------------------------------*/
  458.  
  459. /**
  460.  * close an open XML element
  461.  */
  462. int mxml_end_element(MXML_WRITER *writer)
  463. {
  464.    int i;
  465.    char line[1000];
  466.  
  467.    if (writer->level == 0)
  468.       return 0;
  469.    
  470.    writer->level--;
  471.  
  472.    if (writer->element_is_open) {
  473.       writer->element_is_open = FALSE;
  474.       mxml_free(writer->stack[writer->level]);
  475.       if (writer->level == 0)
  476.          mxml_free(writer->stack);
  477.       strcpy(line, "/>\n");
  478.       return mxml_write_line(writer, line) == (int)strlen(line);
  479.    }
  480.  
  481.    line[0] = 0;
  482.    if (!writer->data_was_written) {
  483.       for (i=0 ; i<writer->level ; i++)
  484.          strlcat(line, XML_INDENT, sizeof(line));
  485.    }
  486.  
  487.    strlcat(line, "</", sizeof(line));
  488.    strlcat(line, writer->stack[writer->level], sizeof(line));
  489.    mxml_free(writer->stack[writer->level]);
  490.    if (writer->level == 0)
  491.       mxml_free(writer->stack);
  492.    strlcat(line, ">\n", sizeof(line));
  493.    writer->data_was_written = FALSE;
  494.  
  495.    return mxml_write_line(writer, line) == (int)strlen(line);
  496. }
  497.  
  498. /*------------------------------------------------------------------*/
  499.  
  500. /**
  501.  * write an attribute to the currently open XML element
  502.  */
  503. int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value)
  504. {
  505.    char name_enc[4096], val_enc[4096], line[8192];
  506.  
  507.    if (!writer->element_is_open)
  508.       return FALSE;
  509.  
  510.    strcpy(name_enc, name);
  511.    mxml_encode(name_enc, sizeof(name_enc), writer->translate);
  512.    strcpy(val_enc, value);
  513.    mxml_encode(val_enc, sizeof(val_enc), writer->translate);
  514.  
  515.    sprintf(line, " %s=\"%s\"", name_enc, val_enc);
  516.  
  517.    return mxml_write_line(writer, line) == (int)strlen(line);
  518. }
  519.  
  520. /*------------------------------------------------------------------*/
  521.  
  522. /**
  523.  * write value of an XML element, like <[name]>[value]</[name]>
  524.  */
  525. int mxml_write_value(MXML_WRITER *writer, const char *data)
  526. {
  527.    static int data_size = 0;
  528.  
  529.    if (!writer->element_is_open)
  530.       return FALSE;
  531.  
  532.    if (mxml_write_line(writer, ">") != 1)
  533.       return FALSE;
  534.    writer->element_is_open = FALSE;
  535.    writer->data_was_written = TRUE;
  536.  
  537.    if (data_size == 0) {
  538.       _data_enc = (char *)mxml_malloc(1000);
  539.       data_size = 1000;
  540.    } else if ((int)strlen(data)*2+1000 > data_size) {
  541.       data_size = 1000+(int)strlen(data)*2;
  542.       _data_enc = (char *)mxml_realloc(_data_enc, data_size);
  543.    }
  544.  
  545.    strcpy(_data_enc, data);
  546.    mxml_encode(_data_enc, data_size, writer->translate);
  547.    return mxml_write_line(writer, _data_enc) == (int)strlen(_data_enc);
  548. }
  549.  
  550. /*------------------------------------------------------------------*/
  551.  
  552. /**
  553.  * write empty line
  554.  */
  555. int mxml_write_empty_line(MXML_WRITER *writer)
  556. {
  557.    if (writer->element_is_open) {
  558.       mxml_write_line(writer, ">\n");
  559.       writer->element_is_open = FALSE;
  560.    }
  561.  
  562.    if (mxml_write_line(writer, "\n") != 1)
  563.       return FALSE;
  564.  
  565.    return TRUE;
  566. }
  567.  
  568. /*------------------------------------------------------------------*/
  569.  
  570. /**
  571.  * write a comment to an XML file, enclosed in "<!--" and "-->"
  572.  */
  573. int mxml_write_comment(MXML_WRITER *writer, const char *string)
  574. {
  575.    int  i;
  576.    char line[1000];
  577.  
  578.    if (writer->element_is_open) {
  579.       mxml_write_line(writer, ">\n");
  580.       writer->element_is_open = FALSE;
  581.    }
  582.  
  583.    line[0] = 0;
  584.    for (i=0 ; i<writer->level ; i++)
  585.       strlcat(line, XML_INDENT, sizeof(line));
  586.  
  587.    strlcat(line, "<!-- ", sizeof(line));
  588.    strlcat(line, string, sizeof(line));
  589.    strlcat(line, " -->\n", sizeof(line));
  590.    if (mxml_write_line(writer, line) != (int)strlen(line))
  591.       return FALSE;
  592.  
  593.    return TRUE;
  594. }
  595.  
  596. /*------------------------------------------------------------------*/
  597.  
  598. /**
  599.  * shortcut to write an element with a value but without attribute
  600.  */
  601. int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value)
  602. {
  603.    int i;
  604.  
  605.    i = mxml_start_element(writer, name);
  606.    i += mxml_write_value(writer, value);
  607.    i += mxml_end_element(writer);
  608.    return i;
  609. }
  610.  
  611. /*------------------------------------------------------------------*/
  612.  
  613. /**
  614.  * close a file opened with mxml_open_writer
  615.  */
  616. char *mxml_close_buffer(MXML_WRITER *writer)
  617. {
  618.    int i;
  619.    char *p;
  620.  
  621.    if (writer->element_is_open) {
  622.       writer->element_is_open = FALSE;
  623.       if (mxml_write_line(writer, ">\n") != 2)
  624.          return NULL;
  625.    }
  626.  
  627.    /* close remaining open levels */
  628.    for (i = 0 ; i<writer->level ; i++)
  629.       mxml_end_element(writer);
  630.  
  631.    p = writer->buffer;
  632.    mxml_free(writer);
  633.    return p;
  634. }
  635.  
  636. /*------------------------------------------------------------------*/
  637.  
  638. /**
  639.  * close a file opened with mxml_open_writer
  640.  */
  641. int mxml_close_file(MXML_WRITER *writer)
  642. {
  643.    int i;
  644.  
  645.    if (writer->element_is_open) {
  646.       writer->element_is_open = FALSE;
  647.       if (mxml_write_line(writer, ">\n") != 2)
  648.          return 0;
  649.    }
  650.  
  651.    /* close remaining open levels */
  652.    for (i = 0 ; i<writer->level ; i++)
  653.       mxml_end_element(writer);
  654.  
  655.    close(writer->fh);
  656.    mxml_free(writer);
  657.    return 1;
  658. }
  659.  
  660. /*------------------------------------------------------------------*/
  661.  
  662. /**
  663.  * create root node of an XML tree
  664.  */
  665. PMXML_NODE mxml_create_root_node(void)
  666. {
  667.    PMXML_NODE root;
  668.  
  669.    root = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1);
  670.    strcpy(root->name, "root");
  671.    root->node_type = DOCUMENT_NODE;
  672.  
  673.    return root;
  674. }
  675.  
  676. /*------------------------------------------------------------------*/
  677.  
  678. /**
  679.  * add a subnode (child) to an existing parent node as a specific position
  680.  */
  681. PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx)
  682. {
  683.    PMXML_NODE pnode, pchild;
  684.    int i, j;
  685.  
  686.    assert(parent);
  687.    if (parent->n_children == 0)
  688.       parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE));
  689.    else
  690.       parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
  691.    assert(parent->child);
  692.  
  693.    /* move following nodes one down */
  694.    if (idx < parent->n_children)
  695.       for (i=parent->n_children ; i > idx ; i--)
  696.          memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
  697.  
  698.    /* correct parent pointer for children */
  699.    for (i=0 ; i<parent->n_children ; i++) {
  700.       pchild = parent->child+i;
  701.       for (j=0 ; j<pchild->n_children ; j++)
  702.          pchild->child[j].parent = pchild;
  703.    }
  704.  
  705.    /* initialize new node */
  706.    pnode = &parent->child[idx];
  707.    memset(pnode, 0, sizeof(MXML_NODE));
  708.    strlcpy(pnode->name, node_name, sizeof(pnode->name));
  709.    pnode->node_type = node_type;
  710.    pnode->parent = parent;
  711.    
  712.    parent->n_children++;
  713.  
  714.    if (value && *value) {
  715.       pnode->value = (char *)mxml_malloc(strlen(value)+1);
  716.       assert(pnode->value);
  717.       strcpy(pnode->value, value);
  718.    }
  719.  
  720.    return pnode;
  721. }
  722.  
  723. /*------------------------------------------------------------------*/
  724.  
  725. /**
  726.  * add a subnode (child) to an existing parent node at the end
  727.  */
  728. PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value)
  729. {
  730.    return mxml_add_special_node_at(parent, node_type, node_name, value, parent->n_children);
  731. }
  732.  
  733. /*------------------------------------------------------------------*/
  734.  
  735. /**
  736.  * write value of an XML element, like <[name]>[value]</[name]>
  737.  */
  738. PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value)
  739. {
  740.    return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, parent->n_children);
  741. }
  742.  
  743. /*------------------------------------------------------------------*/
  744.  
  745. /**
  746.  * add a subnode (child) to an existing parent node at the end
  747.  */
  748. PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx)
  749. {
  750.    return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, idx);
  751. }
  752.  
  753. /*------------------------------------------------------------------*/
  754.  
  755. /**
  756.  * add a whole node tree to an existing parent node at a specific position
  757.  */
  758. int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx)
  759. {
  760.    PMXML_NODE pchild;
  761.    int i, j, k;
  762.  
  763.    assert(parent);
  764.    assert(tree);
  765.    if (parent->n_children == 0)
  766.       parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE));
  767.    else {
  768.       pchild = parent->child;
  769.       parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
  770.  
  771.       if (parent->child != pchild) {
  772.          /* correct parent pointer for children */
  773.          for (i=0 ; i<parent->n_children ; i++) {
  774.             pchild = parent->child+i;
  775.             for (j=0 ; j<pchild->n_children ; j++)
  776.                pchild->child[j].parent = pchild;
  777.          }
  778.       }
  779.    }
  780.    assert(parent->child);
  781.  
  782.    if (idx < parent->n_children)
  783.       for (i=parent->n_children ; i > idx ; i--) {
  784.          /* move following nodes one down */
  785.          memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
  786.  
  787.          /* correct parent pointer for children */
  788.          for (j=0 ; j<parent->n_children ; j++) {
  789.             pchild = parent->child+j;
  790.             for (k=0 ; k<pchild->n_children ; k++)
  791.                pchild->child[k].parent = pchild;
  792.          }
  793.       }
  794.  
  795.    /* initialize new node */
  796.    memcpy(parent->child+idx, tree, sizeof(MXML_NODE));
  797.    parent->n_children++;
  798.    parent->child[idx].parent = parent;
  799.  
  800.    /* correct parent pointer for children */
  801.    for (i=0 ; i<parent->n_children ; i++) {
  802.       pchild = parent->child+i;
  803.       for (j=0 ; j<pchild->n_children ; j++)
  804.          pchild->child[j].parent = pchild;
  805.    }
  806.  
  807.    return TRUE;
  808. }
  809.  
  810. /*------------------------------------------------------------------*/
  811.  
  812. /**
  813.  * add a whole node tree to an existing parent node at the end
  814.  */
  815. int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree)
  816. {
  817.    return mxml_add_tree_at(parent, tree, parent->n_children);
  818. }
  819.  
  820. /*------------------------------------------------------------------*/
  821.  
  822. /**
  823.  * add an attribute to an existing node
  824.  */
  825. int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
  826. {
  827.    if (pnode->n_attributes == 0) {
  828.       pnode->attribute_name  = (char*)mxml_malloc(MXML_NAME_LENGTH);
  829.       pnode->attribute_value = (char**)mxml_malloc(sizeof(char *));
  830.    } else {
  831.       pnode->attribute_name  = (char*)mxml_realloc(pnode->attribute_name,  MXML_NAME_LENGTH*(pnode->n_attributes+1));
  832.       pnode->attribute_value = (char**)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes+1));
  833.    }
  834.  
  835.    strlcpy(pnode->attribute_name+pnode->n_attributes*MXML_NAME_LENGTH, attrib_name, MXML_NAME_LENGTH);
  836.    pnode->attribute_value[pnode->n_attributes] = (char *)mxml_malloc(strlen(attrib_value)+1);
  837.    strcpy(pnode->attribute_value[pnode->n_attributes], attrib_value);
  838.    pnode->n_attributes++;
  839.  
  840.    return TRUE;
  841. }
  842.  
  843. /*------------------------------------------------------------------*/
  844.  
  845. /**
  846.  * return number of subnodes (children) of a node
  847.  */
  848. int mxml_get_number_of_children(PMXML_NODE pnode)
  849. {
  850.    assert(pnode);
  851.    return pnode->n_children;
  852. }
  853.  
  854. /*------------------------------------------------------------------*/
  855.  
  856. /**
  857.  * return number of subnodes (children) of a node
  858.  */
  859. PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx)
  860. {
  861.    assert(pnode);
  862.    if (idx < pnode->n_children)
  863.       return &pnode->child[idx];
  864.    return NULL;
  865. }
  866.  
  867. /*------------------------------------------------------------------*/
  868.  
  869.  
  870. int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
  871.  
  872. int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found)
  873. {
  874.    /* if at end of path, add this node */
  875.    if (*xml_path == 0) {
  876.       if (*found == 0)
  877.          *nodelist = (PMXML_NODE *)mxml_malloc(sizeof(PMXML_NODE));
  878.       else
  879.          *nodelist = (PMXML_NODE *)mxml_realloc(*nodelist, sizeof(PMXML_NODE)*(*found + 1));
  880.  
  881.       (*nodelist)[*found] = node;
  882.       (*found)++;
  883.    } else {
  884.       /* if not at end of path, branch into subtree */
  885.       return mxml_find_nodes1(node, xml_path+1, nodelist, found);
  886.    }
  887.  
  888.    return 1;
  889. }
  890.  
  891. /*------------------------------------------------------------------*/
  892.  
  893. /**
  894.    Return list of XML nodes with a subset of XPATH specifications.
  895.    Following elemets are possible
  896.  
  897.    /<node>/<node>/..../<node>          Find a node in the tree hierarchy
  898.    /<node>[idx]                        Find child #[idx] of node (index starts from 1)
  899.    /<node>[idx]/<node>                 Find subnode of the above
  900.    /<node>[<subnode>=<value>]          Find a node which has a specific subnode
  901.    /<node>[<subnode>=<value>]/<node>   Find subnode of the above
  902.    /<node>[@<attrib>=<value>]/<node>   Find a node which has a specific attribute
  903. */
  904. int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found)
  905. {
  906.    PMXML_NODE pnode;
  907.    const char *p1,*p2;
  908.    char *p3, node_name[256], condition[256];
  909.    char cond_name[MXML_MAX_CONDITION][256], cond_value[MXML_MAX_CONDITION][256];
  910.    int  cond_type[MXML_MAX_CONDITION];
  911.    int i, j, k, idx, num_cond;
  912.    int cond_satisfied,cond_index;
  913.    size_t len;
  914.  
  915.    p1 = xml_path;
  916.    pnode = tree;
  917.  
  918.    /* skip leading '/' */
  919.    if (*p1 && *p1 == '/')
  920.       p1++;
  921.  
  922.    do {
  923.       p2 = p1;
  924.       while (*p2 && *p2 != '/' && *p2 != '[')
  925.          p2++;
  926.       len = (size_t)p2 - (size_t)p1;
  927.       if (len >= sizeof(node_name))
  928.          return 0;
  929.  
  930.       memcpy(node_name, p1, len);
  931.       node_name[len] = 0;
  932.       idx = 0;
  933.       num_cond = 0;
  934.       while (*p2 == '[') {
  935.          cond_name[num_cond][0] = cond_value[num_cond][0] = cond_type[num_cond] = 0;
  936.          p2++;
  937.          if (isdigit(*p2)) {
  938.             /* evaluate [idx] */
  939.             idx = atoi(p2);
  940.             p2 = strchr(p2, ']');
  941.             if (p2 == NULL)
  942.                return 0;
  943.             p2++;
  944.          } else {
  945.             /* evaluate [<@attrib>/<subnode>=<value>] */
  946.             while (*p2 && isspace((unsigned char)*p2))
  947.                p2++;
  948.             strlcpy(condition, p2, sizeof(condition));
  949.             if (strchr(condition, ']'))
  950.                *strchr(condition, ']') = 0;
  951.             else
  952.                return 0;
  953.             p2 = strchr(p2, ']')+1;
  954.             if ((p3 = strchr(condition, '=')) != NULL) {
  955.                if (condition[0] == '@') {
  956.                   cond_type[num_cond] = 1;
  957.                   strlcpy(cond_name[num_cond], &condition[1], sizeof(cond_name[num_cond]));
  958.                } else {
  959.                   strlcpy(cond_name[num_cond], condition, sizeof(cond_name[num_cond]));
  960.                }
  961.  
  962.                *strchr(cond_name[num_cond], '=') = 0;
  963.                while (cond_name[num_cond][0] && isspace(cond_name[num_cond][strlen(cond_name[num_cond])-1]))
  964.                   cond_name[num_cond][strlen(cond_name[num_cond])-1] = 0;
  965.  
  966.                p3++;
  967.                while (*p3 && isspace(*p3))
  968.                   p3++;
  969.                if (*p3 == '\"') {
  970.                   strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
  971.                   while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
  972.                      cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
  973.                   if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\"')
  974.                      cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
  975.                } else if (*p3 == '\'') {
  976.                   strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
  977.                   while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
  978.                      cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
  979.                   if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\'')
  980.                      cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
  981.                } else {
  982.                   strlcpy(cond_value[num_cond], p3, sizeof(cond_value[num_cond]));
  983.                   while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
  984.                      cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
  985.                }
  986.                num_cond++;
  987.             }
  988.          }
  989.       }
  990.  
  991.       cond_index = 0;
  992.       for (i=j=0 ; i<pnode->n_children ; i++) {
  993.          if (num_cond) {
  994.             cond_satisfied = 0;
  995.             for (k=0;k<num_cond;k++) {
  996.                if (cond_type[k]) {
  997.                   /* search node with attribute */
  998.                   if (strcmp(pnode->child[i].name, node_name) == 0)
  999.                      if (mxml_get_attribute(pnode->child+i, cond_name[k]) &&
  1000.                         strcmp(mxml_get_attribute(pnode->child+i, cond_name[k]), cond_value[k]) == 0)
  1001.                         cond_satisfied++;
  1002.                }
  1003.                else {
  1004.                   /* search subnode */
  1005.                   for (j=0 ; j<pnode->child[i].n_children ; j++)
  1006.                      if (strcmp(pnode->child[i].child[j].name, cond_name[k]) == 0)
  1007.                         if (strcmp(pnode->child[i].child[j].value, cond_value[k]) == 0)
  1008.                            cond_satisfied++;
  1009.                }
  1010.             }
  1011.             if (cond_satisfied==num_cond) {
  1012.                cond_index++;
  1013.                if (idx == 0 || cond_index == idx) {
  1014.                   if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
  1015.                      return 0;
  1016.                }
  1017.             }
  1018.          } else {
  1019.             if (strcmp(pnode->child[i].name, node_name) == 0)
  1020.                if (idx == 0 || ++j == idx)
  1021.                   if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
  1022.                      return 0;
  1023.          }
  1024.       }
  1025.  
  1026.       if (i == pnode->n_children)
  1027.          return 1;
  1028.  
  1029.       pnode = &pnode->child[i];
  1030.       p1 = p2;
  1031.       if (*p1 == '/')
  1032.          p1++;
  1033.  
  1034.    } while (*p2);
  1035.  
  1036.    return 1;
  1037. }
  1038.  
  1039. /*------------------------------------------------------------------*/
  1040.  
  1041. int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist)
  1042. {
  1043.    int status, found = 0;
  1044.    
  1045.    status = mxml_find_nodes1(tree, xml_path, nodelist, &found);
  1046.  
  1047.    if (status == 0)
  1048.       return -1;
  1049.  
  1050.    return found;
  1051. }
  1052.  
  1053. /*------------------------------------------------------------------*/
  1054.  
  1055. /**
  1056.  *  Search for a specific XML node with a subset of XPATH specifications.
  1057.  *  Return first found node. For syntax see mxml_find_nodes()
  1058.  */
  1059. PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path)
  1060. {
  1061.    PMXML_NODE *node, pnode;
  1062.    int n;
  1063.  
  1064.    n = mxml_find_nodes(tree, xml_path, &node);
  1065.    if (n > 0) {
  1066.       pnode = node[0];
  1067.       mxml_free(node);
  1068.    } else
  1069.       pnode = NULL;
  1070.  
  1071.    return pnode;
  1072. }
  1073.  
  1074. /*------------------------------------------------------------------*/
  1075.  
  1076. PMXML_NODE mxml_get_parent(PMXML_NODE pnode)
  1077. {
  1078.    assert(pnode);
  1079.    return pnode->parent;
  1080. }
  1081.  
  1082. /*------------------------------------------------------------------*/
  1083.  
  1084. char *mxml_get_name(PMXML_NODE pnode)
  1085. {
  1086.    assert(pnode);
  1087.    return pnode->name;
  1088. }
  1089.  
  1090. /*------------------------------------------------------------------*/
  1091.  
  1092. char *mxml_get_value(PMXML_NODE pnode)
  1093. {
  1094.    assert(pnode);
  1095.    return pnode->value;
  1096. }
  1097.  
  1098. /*------------------------------------------------------------------*/
  1099.  
  1100. int mxml_get_line_number_start(PMXML_NODE pnode)
  1101. {
  1102.    assert(pnode);
  1103.    return pnode->line_number_start;
  1104. }
  1105.  
  1106. /*------------------------------------------------------------------*/
  1107.  
  1108. int mxml_get_line_number_end(PMXML_NODE pnode)
  1109. {
  1110.    assert(pnode);
  1111.    return pnode->line_number_end;
  1112. }
  1113.  
  1114. /*------------------------------------------------------------------*/
  1115.  
  1116. char *mxml_get_attribute(PMXML_NODE pnode, const char *name)
  1117. {
  1118.    int i;
  1119.  
  1120.    assert(pnode);
  1121.    for (i=0 ; i<pnode->n_attributes ; i++)
  1122.       if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, name) == 0)
  1123.          return pnode->attribute_value[i];
  1124.  
  1125.    return NULL;
  1126. }
  1127.  
  1128. /*------------------------------------------------------------------*/
  1129.  
  1130. int mxml_replace_node_name(PMXML_NODE pnode, const char *name)
  1131. {
  1132.    strlcpy(pnode->name, name, sizeof(pnode->name));
  1133.    return TRUE;
  1134. }
  1135.  
  1136. /*------------------------------------------------------------------*/
  1137.  
  1138. int mxml_replace_node_value(PMXML_NODE pnode, const char *value)
  1139. {
  1140.    if (pnode->value)
  1141.       pnode->value = (char *)mxml_realloc(pnode->value, strlen(value)+1);
  1142.    else if (value)
  1143.       pnode->value = (char *)mxml_malloc(strlen(value)+1);
  1144.    else
  1145.       pnode->value = NULL;
  1146.    
  1147.    if (value)
  1148.       strcpy(pnode->value, value);
  1149.  
  1150.    return TRUE;
  1151. }
  1152.  
  1153. /*------------------------------------------------------------------*/
  1154.  
  1155. /**
  1156.    replace value os a subnode, like
  1157.  
  1158.    <parent>
  1159.      <child>value</child>
  1160.    </parent>
  1161.  
  1162.    if pnode=parent, and "name"="child", then "value" gets replaced
  1163. */
  1164. int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value)
  1165. {
  1166.    int i;
  1167.  
  1168.    for (i=0 ; i<pnode->n_children ; i++)
  1169.       if (strcmp(pnode->child[i].name, name) == 0)
  1170.          break;
  1171.  
  1172.    if (i == pnode->n_children)
  1173.       return FALSE;
  1174.  
  1175.    return mxml_replace_node_value(&pnode->child[i], value);
  1176. }
  1177.  
  1178. /*------------------------------------------------------------------*/
  1179.  
  1180. /**
  1181.  * change the name of an attribute, keep its value
  1182.  */
  1183. int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name)
  1184. {
  1185.    int i;
  1186.  
  1187.    for (i=0 ; i<pnode->n_attributes ; i++)
  1188.       if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, old_name) == 0)
  1189.          break;
  1190.  
  1191.    if (i == pnode->n_attributes)
  1192.       return FALSE;
  1193.  
  1194.    strlcpy(pnode->attribute_name+i*MXML_NAME_LENGTH, new_name, MXML_NAME_LENGTH);
  1195.    return TRUE;
  1196. }
  1197.  
  1198. /*------------------------------------------------------------------*/
  1199.  
  1200. /**
  1201.  * change the value of an attribute
  1202.  */
  1203. int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
  1204. {
  1205.    int i;
  1206.  
  1207.    for (i=0 ; i<pnode->n_attributes ; i++)
  1208.       if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
  1209.          break;
  1210.  
  1211.    if (i == pnode->n_attributes)
  1212.       return FALSE;
  1213.  
  1214.    pnode->attribute_value[i] = (char *)mxml_realloc(pnode->attribute_value[i], strlen(attrib_value)+1);
  1215.    strcpy(pnode->attribute_value[i], attrib_value);
  1216.    return TRUE;
  1217. }
  1218.  
  1219. /*------------------------------------------------------------------*/
  1220.  
  1221. /**
  1222.  * free memory of a node and remove it from the parent's child list
  1223.  */
  1224. int mxml_delete_node(PMXML_NODE pnode)
  1225. {
  1226.    PMXML_NODE parent;
  1227.    int i, j;
  1228.  
  1229.    /* remove node from parent's list */
  1230.    parent = pnode->parent;
  1231.  
  1232.    if (parent) {
  1233.       for (i=0 ; i<parent->n_children ; i++)
  1234.          if (&parent->child[i] == pnode)
  1235.             break;
  1236.  
  1237.       /* free allocated node memory recursively */
  1238.       mxml_free_tree(pnode);
  1239.  
  1240.       if (i < parent->n_children) {
  1241.          for (j=i ; j<parent->n_children-1 ; j++)
  1242.             memcpy(&parent->child[j], &parent->child[j+1], sizeof(MXML_NODE));
  1243.          parent->n_children--;
  1244.          if (parent->n_children)
  1245.             parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children));
  1246.          else
  1247.             mxml_free(parent->child);
  1248.       }
  1249.    } else
  1250.       mxml_free_tree(pnode);
  1251.  
  1252.    return TRUE;
  1253. }
  1254.  
  1255. /*------------------------------------------------------------------*/
  1256.  
  1257. int mxml_delete_attribute(PMXML_NODE pnode, const char *attrib_name)
  1258. {
  1259.    int i, j;
  1260.  
  1261.    for (i=0 ; i<pnode->n_attributes ; i++)
  1262.       if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
  1263.          break;
  1264.  
  1265.    if (i == pnode->n_attributes)
  1266.       return FALSE;
  1267.  
  1268.    mxml_free(pnode->attribute_value[i]);
  1269.    for (j=i ; j<pnode->n_attributes-1 ; j++) {
  1270.       strcpy(pnode->attribute_name+j*MXML_NAME_LENGTH, pnode->attribute_name+(j+1)*MXML_NAME_LENGTH);
  1271.       pnode->attribute_value[j] = pnode->attribute_value[j+1];
  1272.    }
  1273.  
  1274.    if (pnode->n_attributes > 0) {
  1275.       pnode->attribute_name  = (char *)mxml_realloc(pnode->attribute_name,  MXML_NAME_LENGTH*(pnode->n_attributes-1));
  1276.       pnode->attribute_value = (char **)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes-1));
  1277.    } else {
  1278.       mxml_free(pnode->attribute_name);
  1279.       mxml_free(pnode->attribute_value);
  1280.    }
  1281.  
  1282.    return TRUE;
  1283. }
  1284.  
  1285. /*------------------------------------------------------------------*/
  1286.  
  1287. #define HERE root, file_name, line_number, error, error_size, error_line
  1288.  
  1289. /**
  1290.  * used inside mxml_parse_file for reporting errors
  1291.  */
  1292. PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...)
  1293. {
  1294.    char *msg, str[1000];
  1295.    va_list argptr;
  1296.  
  1297.    if (file_name && file_name[0])
  1298.       sprintf(str, "XML read error in file \"%s\", line %d: ", file_name, line_number);
  1299.    else
  1300.       sprintf(str, "XML read error, line %d: ", line_number);
  1301.    msg = (char *)mxml_malloc(error_size);
  1302.    if (error)
  1303.       strlcpy(error, str, error_size);
  1304.  
  1305.    va_start(argptr, format);
  1306.    vsprintf(str, (char *) format, argptr);
  1307.    va_end(argptr);
  1308.  
  1309.    if (error)
  1310.       strlcat(error, str, error_size);
  1311.    if (error_line)
  1312.       *error_line = line_number;
  1313.    
  1314.    mxml_free(msg);
  1315.    mxml_free_tree(root);
  1316.  
  1317.    return NULL;
  1318. }
  1319.  
  1320. /*------------------------------------------------------------------*/
  1321.  
  1322. /**
  1323.  * Parse a XML buffer and convert it into a tree of MXML_NODE's.
  1324.  * Return NULL in case of an error, return error description.
  1325.  * Optional file_name is used for error reporting if called from mxml_parse_file()
  1326.  */
  1327. PMXML_NODE mxml_parse_buffer(const char *buf, char *error, int error_size, int *error_line)
  1328. {
  1329.    char node_name[256], attrib_name[256], attrib_value[1000], quote;
  1330.    const char *p, *pv;
  1331.    int i,j, line_number;
  1332.    PMXML_NODE root, ptree, pnew;
  1333.    int end_element;
  1334.    size_t len;
  1335.    char *file_name = NULL; /* dummy for 'HERE' */
  1336.  
  1337.    p = buf;
  1338.    line_number = 1;
  1339.  
  1340.    root = mxml_create_root_node();
  1341.    ptree = root;
  1342.  
  1343.    /* parse file contents */
  1344.    do {
  1345.       if (*p == '<') {
  1346.  
  1347.          end_element = FALSE;
  1348.  
  1349.          /* found new element */
  1350.          p++;
  1351.          while (*p && isspace(*p)) {
  1352.             if (*p == '\n')
  1353.                line_number++;
  1354.             p++;
  1355.          }
  1356.          if (!*p)
  1357.             return read_error(HERE, "Unexpected end of file");
  1358.  
  1359.          if (strncmp(p, "!--", 3) == 0) {
  1360.            
  1361.             /* found comment */
  1362.  
  1363.             pnew = mxml_add_special_node(ptree, COMMENT_NODE, "Comment", NULL);
  1364.             pnew->line_number_start = line_number;
  1365.             pv = p+3;
  1366.             while (*pv == ' ')
  1367.                pv++;
  1368.  
  1369.             p += 3;
  1370.             if (strstr(p, "-->") == NULL)
  1371.                return read_error(HERE, "Unterminated comment");
  1372.            
  1373.             while (strncmp(p, "-->", 3) != 0) {
  1374.                if (*p == '\n')
  1375.                   line_number++;
  1376.                p++;
  1377.             }
  1378.  
  1379.             len = (size_t)p - (size_t)pv;
  1380.             pnew->value = (char *)mxml_malloc(len+1);
  1381.             memcpy(pnew->value, pv, len);
  1382.             pnew->value[len] = 0;
  1383.             pnew->line_number_end = line_number;
  1384.             mxml_decode(pnew->value);
  1385.  
  1386.             p += 3;
  1387.  
  1388.          } else if (*p == '?') {
  1389.  
  1390.             /* found ?...? element */
  1391.             pnew = mxml_add_special_node(ptree, PROCESSING_INSTRUCTION_NODE, "PI", NULL);
  1392.             pnew->line_number_start = line_number;
  1393.             pv = p+1;
  1394.  
  1395.             p++;
  1396.             if (strstr(p, "?>") == NULL)
  1397.                return read_error(HERE, "Unterminated ?...? element");
  1398.            
  1399.             while (strncmp(p, "?>", 2) != 0) {
  1400.                if (*p == '\n')
  1401.                   line_number++;
  1402.                p++;
  1403.             }
  1404.  
  1405.             len = (size_t)p - (size_t)pv;
  1406.             pnew->value = (char *)mxml_malloc(len+1);
  1407.             memcpy(pnew->value, pv, len);
  1408.             pnew->value[len] = 0;
  1409.             pnew->line_number_end = line_number;
  1410.             mxml_decode(pnew->value);
  1411.  
  1412.             p += 2;
  1413.  
  1414.          } else if (strncmp(p, "!DOCTYPE", 8) == 0 ) {
  1415.  
  1416.             /* found !DOCTYPE element , skip it */
  1417.             p += 8;
  1418.             if (strstr(p, ">") == NULL)
  1419.                return read_error(HERE, "Unterminated !DOCTYPE element");
  1420.  
  1421.             j = 0;
  1422.             while (*p && (*p != '>' || j > 0)) {
  1423.                if (*p == '\n')
  1424.                   line_number++;
  1425.                else if (*p == '<')
  1426.                   j++;
  1427.                else if (*p == '>')
  1428.                   j--;
  1429.                p++;
  1430.             }
  1431.             if (!*p)
  1432.                return read_error(HERE, "Unexpected end of file");
  1433.  
  1434.             p++;
  1435.  
  1436.          } else {
  1437.            
  1438.             /* found normal element */
  1439.             if (*p == '/') {
  1440.                end_element = TRUE;
  1441.                p++;
  1442.                while (*p && isspace((unsigned char)*p)) {
  1443.                   if (*p == '\n')
  1444.                      line_number++;
  1445.                   p++;
  1446.                }
  1447.                if (!*p)
  1448.                   return read_error(HERE, "Unexpected end of file");
  1449.             }
  1450.  
  1451.             /* extract node name */
  1452.             i = 0;
  1453.             node_name[i] = 0;
  1454.             while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<')
  1455.                node_name[i++] = *p++;
  1456.             node_name[i] = 0;
  1457.             if (!*p)
  1458.                return read_error(HERE, "Unexpected end of file");
  1459.             if (*p == '<')
  1460.                return read_error(HERE, "Unexpected \'<\' inside element \"%s\"", node_name);
  1461.  
  1462.             mxml_decode(node_name);
  1463.  
  1464.             if (end_element) {
  1465.  
  1466.                if (!ptree)
  1467.                   return read_error(HERE, "Found unexpected </%s>", node_name);
  1468.  
  1469.                /* close previously opened element */
  1470.                if (strcmp(ptree->name, node_name) != 0)
  1471.                   return read_error(HERE, "Found </%s>, expected </%s>", node_name, ptree->name);
  1472.                ptree->line_number_end = line_number;
  1473.                
  1474.                /* go up one level on the tree */
  1475.                ptree = ptree->parent;
  1476.  
  1477.             } else {
  1478.            
  1479.                if (ptree == NULL)
  1480.                   return read_error(HERE, "Unexpected second top level node");
  1481.  
  1482.                /* allocate new element structure in parent tree */
  1483.                pnew = mxml_add_node(ptree, node_name, NULL);
  1484.                pnew->line_number_start = line_number;
  1485.                pnew->line_number_end = line_number;
  1486.  
  1487.                while (*p && isspace((unsigned char)*p)) {
  1488.                   if (*p == '\n')
  1489.                      line_number++;
  1490.                   p++;
  1491.                }
  1492.                if (!*p)
  1493.                   return read_error(HERE, "Unexpected end of file");
  1494.  
  1495.                while (*p != '>' && *p != '/') {
  1496.  
  1497.                   /* found attribute */
  1498.                   pv = p;
  1499.                   while (*pv && !isspace((unsigned char)*pv) && *pv != '=' && *pv != '<' && *pv != '>')
  1500.                      pv++;
  1501.                   if (!*pv)
  1502.                      return read_error(HERE, "Unexpected end of file");
  1503.                   if (*pv == '<' || *pv == '>')
  1504.                      return read_error(HERE, "Unexpected \'%c\' inside element \"%s\"", *pv, node_name);
  1505.  
  1506.                   /* extract attribute name */
  1507.                   len = (size_t)pv - (size_t)p;
  1508.                   if (len > sizeof(attrib_name)-1)
  1509.                      len = sizeof(attrib_name)-1;
  1510.                   memcpy(attrib_name, p, len);
  1511.                   attrib_name[len] = 0;
  1512.                   mxml_decode(attrib_name);
  1513.  
  1514.                   p = pv;
  1515.                   while (*p && isspace((unsigned char)*p)) {
  1516.                      if (*p == '\n')
  1517.                         line_number++;
  1518.                      p++;
  1519.                   }
  1520.                   if (!*p)
  1521.                      return read_error(HERE, "Unexpected end of file");
  1522.                   if (*p != '=')
  1523.                      return read_error(HERE, "Expect \"=\" here");
  1524.  
  1525.                   p++;
  1526.                   while (*p && isspace((unsigned char)*p)) {
  1527.                      if (*p == '\n')
  1528.                         line_number++;
  1529.                      p++;
  1530.                   }
  1531.                   if (!*p)
  1532.                      return read_error(HERE, "Unexpected end of file");
  1533.                   if (*p != '\"' && *p != '\'')
  1534.                      return read_error(HERE, "Expect \" or \' here");
  1535.                   quote = *p;
  1536.                   p++;
  1537.  
  1538.                   /* extract attribute value */
  1539.                   pv = p;
  1540.                   while (*pv && *pv != quote)
  1541.                      pv++;
  1542.                   if (!*pv)
  1543.                      return read_error(HERE, "Unexpected end of file");
  1544.  
  1545.                   len = (size_t)pv - (size_t)p;
  1546.                   if (len > sizeof(attrib_value)-1)
  1547.                      len = sizeof(attrib_value)-1;
  1548.                   memcpy(attrib_value, p, len);
  1549.                   attrib_value[len] = 0;
  1550.                   mxml_decode(attrib_value);
  1551.  
  1552.                   /* add attribute to current node */
  1553.                   mxml_add_attribute(pnew, attrib_name, attrib_value);
  1554.  
  1555.                   p = pv+1;
  1556.                   while (*p && isspace((unsigned char)*p)) {
  1557.                      if (*p == '\n')
  1558.                         line_number++;
  1559.                      p++;
  1560.                   }
  1561.                   if (!*p)
  1562.                      return read_error(HERE, "Unexpected end of file");
  1563.                }
  1564.  
  1565.                if (*p == '/') {
  1566.  
  1567.                   /* found empty node, like <node/>, just skip closing bracket */
  1568.                   p++;
  1569.  
  1570.                   while (*p && isspace((unsigned char)*p)) {
  1571.                      if (*p == '\n')
  1572.                         line_number++;
  1573.                      p++;
  1574.                   }
  1575.                   if (!*p)
  1576.                      return read_error(HERE, "Unexpected end of file");
  1577.                   if (*p != '>')
  1578.                      return read_error(HERE, "Expected \">\" after \"/\"");
  1579.                   p++;
  1580.                }
  1581.  
  1582.                if (*p == '>') {
  1583.  
  1584.                   p++;
  1585.  
  1586.                   /* check if we have sub-element or value */
  1587.                   pv = p;
  1588.                   while (*pv && isspace((unsigned char)*pv)) {
  1589.                      if (*pv == '\n')
  1590.                         line_number++;
  1591.                      pv++;
  1592.                   }
  1593.                   if (!*pv)
  1594.                      return read_error(HERE, "Unexpected end of file");
  1595.  
  1596.                   if (*pv == '<' && *(pv+1) != '/') {
  1597.  
  1598.                      /* start new subtree */
  1599.                      ptree = pnew;
  1600.                      p = pv;
  1601.  
  1602.                   } else {
  1603.  
  1604.                      /* extract value */
  1605.                      while (*pv && *pv != '<') {
  1606.                         if (*pv == '\n')
  1607.                            line_number++;
  1608.                         pv++;
  1609.                      }
  1610.                      if (!*pv)
  1611.                         return read_error(HERE, "Unexpected end of file");
  1612.  
  1613.                      len = (size_t)pv - (size_t)p;
  1614.                      pnew->value = (char *)mxml_malloc(len+1);
  1615.                      memcpy(pnew->value, p, len);
  1616.                      pnew->value[len] = 0;
  1617.                      mxml_decode(pnew->value);
  1618.                      p = pv;
  1619.  
  1620.                      ptree = pnew;
  1621.                   }
  1622.                }
  1623.             }
  1624.          }
  1625.       }
  1626.  
  1627.       /* go to next element */
  1628.       while (*p && *p != '<') {
  1629.          if (*p == '\n')
  1630.             line_number++;
  1631.          p++;
  1632.       }
  1633.    } while (*p);
  1634.  
  1635.    return root;
  1636. }
  1637.  
  1638. /*------------------------------------------------------------------*/
  1639.  
  1640. /**
  1641.  * parse !ENTYTY entries of XML files and replace with references.
  1642.  * Return 0 in case of no errors, return error description.
  1643.  * Optional file_name is used for error reporting if called from mxml_parse_file()
  1644.  */
  1645. int mxml_parse_entity(char **buf, const char *file_name, char *error, int error_size, int *error_line)
  1646. {
  1647.    char *p;
  1648.    char *pv;
  1649.    char delimiter;
  1650.    int i, j, k, line_number, status;
  1651.    char *replacement;
  1652.    char entity_name[MXML_MAX_ENTITY][256];
  1653.    char entity_reference_name[MXML_MAX_ENTITY][256];
  1654.    char *entity_value[MXML_MAX_ENTITY];
  1655.    int entity_type[MXML_MAX_ENTITY];    /* internal or external */
  1656.    int entity_line_number[MXML_MAX_ENTITY];
  1657.    int nentity;
  1658.    int fh, length, len;
  1659.    char *buffer;
  1660.    int ip;                      /* counter for entity value */
  1661.    char directoryname[FILENAME_MAX];
  1662.    char filename[FILENAME_MAX];
  1663.    int entity_value_length[MXML_MAX_ENTITY];
  1664.    int entity_name_length[MXML_MAX_ENTITY];
  1665.  
  1666.    PMXML_NODE root = mxml_create_root_node();   /* dummy for 'HERE' */
  1667.  
  1668.    for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
  1669.       entity_value[ip] = NULL;
  1670.  
  1671.    line_number = 1;
  1672.    nentity = -1;
  1673.    status = 0;
  1674.  
  1675.    if (!buf || !(*buf) || !strlen(*buf))
  1676.       return 0;
  1677.  
  1678.    strcpy(directoryname, file_name);
  1679.    mxml_dirname(directoryname);
  1680.  
  1681.    /* copy string to temporary space */
  1682.    buffer = (char *) mxml_malloc(strlen(*buf) + 1);
  1683.    if (buffer == NULL) {
  1684.       read_error(HERE, "Cannot allocate memory.");
  1685.       status = 1;
  1686.       goto error;
  1687.    }
  1688.    strcpy(buffer, *buf);
  1689.  
  1690.    p = strstr(buffer, "!DOCTYPE");
  1691.    if (p == NULL) {             /* no entities */
  1692.       status = 0;
  1693.       goto error;
  1694.    }
  1695.  
  1696.    pv = strstr(p, "[");
  1697.    if (pv == NULL) {            /* no entities */
  1698.       status = 1;
  1699.       goto error;
  1700.    }
  1701.  
  1702.    p = pv + 1;
  1703.  
  1704.    /* search !ENTITY */
  1705.    do {
  1706.       if (*p == ']')
  1707.          break;
  1708.  
  1709.       if (*p == '<') {
  1710.  
  1711.          /* found new entity */
  1712.          p++;
  1713.          while (*p && isspace((unsigned char)*p)) {
  1714.             if (*p == '\n')
  1715.                line_number++;
  1716.             p++;
  1717.          }
  1718.          if (!*p) {
  1719.             read_error(HERE, "Unexpected end of file");
  1720.             status = 1;
  1721.             goto error;
  1722.          }
  1723.  
  1724.          if (strncmp(p, "!--", 3) == 0) {
  1725.             /* found comment */
  1726.             p += 3;
  1727.             if (strstr(p, "-->") == NULL) {
  1728.                read_error(HERE, "Unterminated comment");
  1729.                status = 1;
  1730.                goto error;
  1731.             }
  1732.  
  1733.             while (strncmp(p, "-->", 3) != 0) {
  1734.                if (*p == '\n')
  1735.                   line_number++;
  1736.                p++;
  1737.             }
  1738.             p += 3;
  1739.          }
  1740.  
  1741.          else if (strncmp(p, "!ENTITY", 7) == 0) {
  1742.             /* found entity */
  1743.             nentity++;
  1744.             if (nentity >= MXML_MAX_ENTITY) {
  1745.                read_error(HERE, "Too much entities");
  1746.                status = 1;
  1747.                goto error;
  1748.             }
  1749.  
  1750.             entity_line_number[nentity] = line_number;
  1751.            
  1752.             pv = p + 7;
  1753.             while (*pv == ' ')
  1754.                pv++;
  1755.  
  1756.             /* extract entity name */
  1757.             p = pv;
  1758.  
  1759.             while (*p && isspace((unsigned char)*p) && *p != '<' && *p != '>') {
  1760.                if (*p == '\n')
  1761.                   line_number++;
  1762.                p++;
  1763.             }
  1764.             if (!*p) {
  1765.                read_error(HERE, "Unexpected end of file");
  1766.                status = 1;
  1767.                goto error;
  1768.             }
  1769.             if (*p == '<' || *p == '>') {
  1770.                read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p);
  1771.                status = 1;
  1772.                goto error;
  1773.             }
  1774.  
  1775.             pv = p;
  1776.             while (*pv && !isspace((unsigned char)*pv) && *pv != '<' && *pv != '>')
  1777.                pv++;
  1778.  
  1779.             if (!*pv) {
  1780.                read_error(HERE, "Unexpected end of file");
  1781.                status = 1;
  1782.                goto error;
  1783.             }
  1784.             if (*pv == '<' || *pv == '>') {
  1785.                read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
  1786.                status = 1;
  1787.                goto error;
  1788.             }
  1789.  
  1790.             entity_name[nentity][0] = '&';
  1791.             i = 1;
  1792.             entity_name[nentity][i] = 0;
  1793.             while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<' && i < 253)
  1794.                entity_name[nentity][i++] = *p++;
  1795.             entity_name[nentity][i++] = ';';
  1796.             entity_name[nentity][i] = 0;
  1797.  
  1798.             if (!*p) {
  1799.                read_error(HERE, "Unexpected end of file");
  1800.                status = 1;
  1801.                goto error;
  1802.             }
  1803.             if (*p == '<') {
  1804.                read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]);
  1805.                status = 1;
  1806.                goto error;
  1807.             }
  1808.  
  1809.             /* extract replacement or SYSTEM */
  1810.             while (*p && isspace((unsigned char)*p)) {
  1811.                if (*p == '\n')
  1812.                   line_number++;
  1813.                p++;
  1814.             }
  1815.             if (!*p) {
  1816.                read_error(HERE, "Unexpected end of file");
  1817.                status = 1;
  1818.                goto error;
  1819.             }
  1820.             if (*p == '>') {
  1821.                read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
  1822.                status = 1;
  1823.                goto error;
  1824.             }
  1825.  
  1826.             /* check if SYSTEM */
  1827.             if (strncmp(p, "SYSTEM", 6) == 0) {
  1828.                entity_type[nentity] = EXTERNAL_ENTITY;
  1829.                p += 6;
  1830.             } else {
  1831.                entity_type[nentity] = INTERNAL_ENTITY;
  1832.             }
  1833.  
  1834.             /* extract replacement */
  1835.             while (*p && isspace((unsigned char)*p)) {
  1836.                if (*p == '\n')
  1837.                   line_number++;
  1838.                p++;
  1839.             }
  1840.             if (!*p) {
  1841.                read_error(HERE, "Unexpected end of file");
  1842.                status = 1;
  1843.                goto error;
  1844.             }
  1845.             if (*p == '>') {
  1846.                read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
  1847.                status = 1;
  1848.                goto error;
  1849.             }
  1850.  
  1851.             if (*p != '\"' && *p != '\'') {
  1852.                read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]);
  1853.                status = 1;
  1854.                goto error;
  1855.             }
  1856.             delimiter = *p;
  1857.             p++;
  1858.             if (!*p) {
  1859.                read_error(HERE, "Unexpected end of file");
  1860.                status = 1;
  1861.                goto error;
  1862.             }
  1863.             pv = p;
  1864.             while (*pv && *pv != delimiter)
  1865.                pv++;
  1866.  
  1867.             if (!*pv) {
  1868.                read_error(HERE, "Unexpected end of file");
  1869.                status = 1;
  1870.                goto error;
  1871.             }
  1872.             if (*pv == '<') {
  1873.                read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
  1874.                status = 1;
  1875.                goto error;
  1876.             }
  1877.  
  1878.             len = (int)((size_t) pv - (size_t) p);
  1879.             replacement = (char *) mxml_malloc(len + 1);
  1880.             if (replacement == NULL) {
  1881.                read_error(HERE, "Cannot allocate memory.");
  1882.                status = 1;
  1883.                goto error;
  1884.             }
  1885.  
  1886.             memcpy(replacement, p, len);
  1887.             replacement[len] = 0;
  1888.             mxml_decode(replacement);
  1889.  
  1890.             if (entity_type[nentity] == EXTERNAL_ENTITY) {
  1891.                strcpy(entity_reference_name[nentity], replacement);
  1892.             } else {
  1893.                entity_value[nentity] = (char *) mxml_malloc(strlen(replacement));
  1894.                if (entity_value[nentity] == NULL) {
  1895.                   read_error(HERE, "Cannot allocate memory.");
  1896.                   status = 1;
  1897.                   goto error;
  1898.                }
  1899.                strcpy(entity_value[nentity], replacement);
  1900.             }
  1901.             mxml_free(replacement);
  1902.  
  1903.             p = pv;
  1904.             while (*p && isspace((unsigned char)*p)) {
  1905.                if (*p == '\n')
  1906.                   line_number++;
  1907.                p++;
  1908.             }
  1909.             if (!*p) {
  1910.                read_error(HERE, "Unexpected end of file");
  1911.                status = 1;
  1912.                goto error;
  1913.             }
  1914.          }
  1915.       }
  1916.  
  1917.       /* go to next element */
  1918.       while (*p && *p != '<') {
  1919.          if (*p == '\n')
  1920.             line_number++;
  1921.          p++;
  1922.       }
  1923.    } while (*p);
  1924.    nentity++;
  1925.  
  1926.    /* read external file */
  1927.    for (i = 0; i < nentity; i++) {
  1928.       if (entity_type[i] == EXTERNAL_ENTITY) {
  1929.          if ( entity_reference_name[i][0] == DIR_SEPARATOR ) /* absolute path */
  1930.             strcpy(filename, entity_reference_name[i]);
  1931.          else /* relative path */
  1932.             sprintf(filename, "%s%c%s", directoryname, DIR_SEPARATOR, entity_reference_name[i]);
  1933.          fh = open(filename, O_RDONLY | O_TEXT, 0644);
  1934.  
  1935.          if (fh == -1) {
  1936.             line_number = entity_line_number[i];
  1937.             read_error(HERE, "%s is missing", entity_reference_name[i]);
  1938.             status = 1;
  1939.             goto error;
  1940.          } else {
  1941.             length = (int)lseek(fh, 0, SEEK_END);
  1942.             lseek(fh, 0, SEEK_SET);
  1943.             if (length == 0) {
  1944.                entity_value[i] = (char *) mxml_malloc(1);
  1945.                if (entity_value[i] == NULL) {
  1946.                   read_error(HERE, "Cannot allocate memory.");
  1947.                   close(fh);
  1948.                   status = 1;
  1949.                   goto error;
  1950.                }
  1951.                entity_value[i][0] = 0;
  1952.             } else {
  1953.                entity_value[i] = (char *) mxml_malloc(length);
  1954.                if (entity_value[i] == NULL) {
  1955.                   read_error(HERE, "Cannot allocate memory.");
  1956.                   close(fh);
  1957.                   status = 1;
  1958.                   goto error;
  1959.                }
  1960.  
  1961.                /* read complete file at once */
  1962.                length = (int)read(fh, entity_value[i], length);
  1963.                entity_value[i][length - 1] = 0;
  1964.                close(fh);
  1965.  
  1966.                /* recursive parse */
  1967.                if (mxml_parse_entity(&entity_value[i], filename, error, error_size, error_line) != 0) {
  1968.                   status = 1;
  1969.                   goto error;
  1970.                }
  1971.             }
  1972.          }
  1973.       }
  1974.    }
  1975.  
  1976.    /* count length of output string */
  1977.    length = (int)strlen(buffer);
  1978.    for (i = 0; i < nentity; i++) {
  1979.       p = buffer;
  1980.       entity_value_length[i] = (int)strlen(entity_value[i]);
  1981.       entity_name_length[i] = (int)strlen(entity_name[i]);
  1982.       while (1) {
  1983.          pv = strstr(p, entity_name[i]);
  1984.          if (pv) {
  1985.             length += entity_value_length[i] - entity_name_length[i];
  1986.             p = pv + 1;
  1987.          } else {
  1988.             break;
  1989.          }
  1990.       }
  1991.    }
  1992.  
  1993.    /* re-allocate memory */
  1994.    *buf = (char *) mxml_realloc(*buf, length + 1);
  1995.    if (*buf == NULL) {
  1996.       read_error(HERE, "Cannot allocate memory.");
  1997.       status = 1;
  1998.       goto error;
  1999.    }
  2000.  
  2001.    /* replace entities */
  2002.    p = buffer;
  2003.    pv = *buf;
  2004.    do {
  2005.       if (*p == '&') {
  2006.          /* found entity */
  2007.          for (j = 0; j < nentity; j++) {
  2008.             if (strncmp(p, entity_name[j], entity_name_length[j]) == 0) {
  2009.                for (k = 0; k < (int) entity_value_length[j]; k++)
  2010.                   *pv++ = entity_value[j][k];
  2011.                p += entity_name_length[j];
  2012.                break;
  2013.             }
  2014.          }
  2015.       }
  2016.       *pv++ = *p++;
  2017.    } while (*p);
  2018.    *pv = 0;
  2019.  
  2020. error:
  2021.  
  2022.    if (buffer != NULL)
  2023.       mxml_free(buffer);
  2024.    for (ip = 0; ip < MXML_MAX_ENTITY; ip++)
  2025.       if (entity_value[ip] != NULL)
  2026.          mxml_free(entity_value[ip]);
  2027.  
  2028.    return status;
  2029. }
  2030.  
  2031. /*------------------------------------------------------------------*/
  2032.  
  2033. /**
  2034.  * parse a XML file and convert it into a tree of MXML_NODE's.
  2035.  * Return NULL in case of an error, return error description
  2036.  */
  2037. PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size, int *error_line)
  2038. {
  2039.    char *buf, line[1000];
  2040.    int fh, length;
  2041.    PMXML_NODE root;
  2042.  
  2043.    if (error)
  2044.       error[0] = 0;
  2045.  
  2046.    fh = open(file_name, O_RDONLY | O_TEXT, 0644);
  2047.  
  2048.    if (fh == -1) {
  2049.       sprintf(line, "Unable to open file \"%s\": ", file_name);
  2050.       strlcat(line, strerror(errno), sizeof(line));
  2051.       strlcpy(error, line, error_size);
  2052.       return NULL;
  2053.    }
  2054.  
  2055.    length = (int)lseek(fh, 0, SEEK_END);
  2056.    lseek(fh, 0, SEEK_SET);
  2057.    buf = (char *)mxml_malloc(length+1);
  2058.    if (buf == NULL) {
  2059.       close(fh);
  2060.       sprintf(line, "Cannot allocate buffer: ");
  2061.       strlcat(line, strerror(errno), sizeof(line));
  2062.       strlcpy(error, line, error_size);
  2063.       return NULL;
  2064.    }
  2065.  
  2066.    /* read complete file at once */
  2067.    length = (int)read(fh, buf, length);
  2068.    buf[length] = 0;
  2069.    close(fh);
  2070.  
  2071.    if (mxml_parse_entity(&buf, file_name, error, error_size, error_line) != 0) {
  2072.       mxml_free(buf);
  2073.       return NULL;
  2074.    }
  2075.  
  2076.    root = mxml_parse_buffer(buf, error, error_size, error_line);
  2077.  
  2078.    mxml_free(buf);
  2079.  
  2080.    return root;
  2081. }
  2082.  
  2083. /*------------------------------------------------------------------*/
  2084.  
  2085. /**
  2086.  * write complete subtree recursively into file opened with mxml_open_document()
  2087.  */
  2088. int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent)
  2089. {
  2090.    int i;
  2091.  
  2092.    mxml_start_element1(writer, tree->name, indent);
  2093.    for (i=0 ; i<tree->n_attributes ; i++)
  2094.       if (!mxml_write_attribute(writer, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]))
  2095.          return FALSE;
  2096.    
  2097.    if (tree->value)
  2098.       if (!mxml_write_value(writer, tree->value))
  2099.          return FALSE;
  2100.  
  2101.    for (i=0 ; i<tree->n_children ; i++)
  2102.       if (!mxml_write_subtree(writer, &tree->child[i], (tree->value == NULL) || i > 0))
  2103.          return FALSE;
  2104.  
  2105.    return mxml_end_element(writer);
  2106. }
  2107.  
  2108. /*------------------------------------------------------------------*/
  2109.  
  2110. /**
  2111.  * write a complete XML tree to a file
  2112.  */
  2113. int mxml_write_tree(const char *file_name, PMXML_NODE tree)
  2114. {
  2115.    MXML_WRITER *writer;
  2116.    int i;
  2117.  
  2118.    assert(tree);
  2119.    writer = mxml_open_file(file_name);
  2120.    if (!writer)
  2121.       return FALSE;
  2122.  
  2123.    for (i=0 ; i<tree->n_children ; i++)
  2124.      if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */
  2125.          if (!mxml_write_subtree(writer, &tree->child[i], TRUE))
  2126.             return FALSE;
  2127.  
  2128.    if (!mxml_close_file(writer))
  2129.       return FALSE;
  2130.  
  2131.    return TRUE;
  2132. }
  2133.  
  2134. /*------------------------------------------------------------------*/
  2135.  
  2136. PMXML_NODE mxml_clone_tree(PMXML_NODE tree)
  2137. {
  2138.    PMXML_NODE clone;
  2139.    int i;
  2140.  
  2141.    clone = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1);
  2142.  
  2143.    /* copy name, node_type, n_attributes and n_children */
  2144.    memcpy(clone, tree, sizeof(MXML_NODE));
  2145.  
  2146.    clone->value = NULL;
  2147.    mxml_replace_node_value(clone, tree->value);
  2148.  
  2149.    clone->attribute_name = NULL;
  2150.    clone->attribute_value = NULL;
  2151.    for (i=0 ; i<tree->n_attributes ; i++)
  2152.       mxml_add_attribute(clone, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]);
  2153.  
  2154.    clone->child = NULL;
  2155.    clone->n_children = 0;
  2156.    for (i=0 ; i<tree->n_children ; i++)
  2157.       mxml_add_tree(clone, mxml_clone_tree(mxml_subnode(tree, i)));
  2158.  
  2159.    return clone;
  2160. }
  2161.  
  2162. /*------------------------------------------------------------------*/
  2163.  
  2164. /**
  2165.  * print XML tree for debugging
  2166.  */
  2167. void mxml_debug_tree(PMXML_NODE tree, int level)
  2168. {
  2169.    int i, j;
  2170.  
  2171.    for (i=0 ; i<level ; i++)
  2172.       printf("  ");
  2173.    printf("Name: %s\n", tree->name);
  2174.    for (i=0 ; i<level ; i++)
  2175.       printf("  ");
  2176.    printf("Valu: %s\n", tree->value);
  2177.    for (i=0 ; i<level ; i++)
  2178.       printf("  ");
  2179.    printf("Type: %d\n", tree->node_type);
  2180.    for (i=0 ; i<level ; i++)
  2181.       printf("  ");
  2182.    printf("Lin1: %d\n", tree->line_number_start);
  2183.    for (i=0 ; i<level ; i++)
  2184.       printf("  ");
  2185.    printf("Lin2: %d\n", tree->line_number_end);
  2186.  
  2187.    for (j=0 ; j<tree->n_attributes ; j++) {
  2188.       for (i=0 ; i<level ; i++)
  2189.          printf("  ");
  2190.       printf("%s: %s\n", tree->attribute_name+j*MXML_NAME_LENGTH,
  2191.          tree->attribute_value[j]);
  2192.    }
  2193.  
  2194.    for (i=0 ; i<level ; i++)
  2195.       printf("  ");
  2196.    printf("Addr: %08zX\n", (size_t)tree);
  2197.    for (i=0 ; i<level ; i++)
  2198.       printf("  ");
  2199.    printf("Prnt: %08zX\n", (size_t)tree->parent);
  2200.    for (i=0 ; i<level ; i++)
  2201.       printf("  ");
  2202.    printf("NCld: %d\n", tree->n_children);
  2203.  
  2204.    for (i=0 ; i<tree->n_children ; i++)
  2205.       mxml_debug_tree(tree->child+i, level+1);
  2206.  
  2207.    if (level == 0)
  2208.       printf("\n");
  2209. }
  2210.  
  2211. /*------------------------------------------------------------------*/
  2212.  
  2213. /**
  2214.  * free memory of XML tree, must be called after any
  2215.  * mxml_create_root_node() or mxml_parse_file()
  2216.  */
  2217. void mxml_free_tree(PMXML_NODE tree)
  2218. {
  2219.    int i;
  2220.  
  2221.    /* first free children recursively */
  2222.    for (i=0 ; i<tree->n_children ; i++)
  2223.       mxml_free_tree(&tree->child[i]);
  2224.    if (tree->n_children)
  2225.       mxml_free(tree->child);
  2226.  
  2227.    /* now free dynamic data */
  2228.    for (i=0 ; i<tree->n_attributes ; i++)
  2229.       mxml_free(tree->attribute_value[i]);
  2230.  
  2231.    if (tree->n_attributes) {
  2232.       mxml_free(tree->attribute_name);
  2233.       mxml_free(tree->attribute_value);
  2234.    }
  2235.    
  2236.    if (tree->value)
  2237.       mxml_free(tree->value);
  2238.  
  2239.    /* if we are the root node, free it */
  2240.    if (tree->parent == NULL)
  2241.       mxml_free(tree);
  2242. }
  2243.  
  2244. /*------------------------------------------------------------------*/
  2245.  
  2246. /*
  2247. void mxml_test()
  2248. {
  2249.    char err[256];
  2250.    PMXML_NODE tree, tree2, node;
  2251.  
  2252.    tree = mxml_parse_file("c:\\tmp\\test.xml", err, sizeof(err));
  2253.    tree2 = mxml_clone_tree(tree);
  2254.  
  2255.    printf("Orig:\n");
  2256.    mxml_debug_tree(tree, 0);
  2257.  
  2258.    printf("\nClone:\n");
  2259.    mxml_debug_tree(tree2, 0);
  2260.  
  2261.    printf("\nCombined:\n");
  2262.    node = mxml_find_node(tree2, "cddb");
  2263.    mxml_add_tree(tree, node);
  2264.    mxml_debug_tree(tree, 0);
  2265.  
  2266.    mxml_free_tree(tree);
  2267. }
  2268. */
  2269.  
  2270. /*------------------------------------------------------------------*/
  2271.  /**
  2272.    mxml_basename deletes any prefix ending with the last slash '/' character
  2273.    present in path. mxml_dirname deletes the filename portion, beginning with
  2274.    the last slash '/' character to the end of path. Followings are examples
  2275.    from these functions
  2276.  
  2277.     path               dirname   basename
  2278.     "/"                "/"       ""
  2279.     "."                "."       "."
  2280.     ""                 ""        ""
  2281.     "/test.txt"        "/"       "test.txt"
  2282.     "path/to/test.txt" "path/to" "test.txt"
  2283.     "test.txt          "."       "test.txt"
  2284.  
  2285.    Under Windows, '\\' and ':' are recognized ad separator too.
  2286.  */
  2287.  
  2288. void mxml_basename(char *path)
  2289. {
  2290.    char str[FILENAME_MAX];
  2291.    char *p;
  2292.    char *name;
  2293.  
  2294.    if (path) {
  2295.       strcpy(str, path);
  2296.       p = str;
  2297.       name = str;
  2298.       while (1) {
  2299.          if (*p == 0)
  2300.             break;
  2301.          if (*p == '/'
  2302. #ifdef _MSC_VER
  2303.              || *p == ':' || *p == '\\'
  2304. #endif
  2305.              )
  2306.             name = p + 1;
  2307.          p++;
  2308.       }
  2309.       strcpy(path, name);
  2310.    }
  2311.  
  2312.    return;
  2313. }
  2314.  
  2315. void mxml_dirname(char *path)
  2316. {
  2317.    char *p;
  2318. #ifdef _MSC_VER
  2319.    char *pv;
  2320. #endif
  2321.  
  2322.    if (!path || strlen(path) == 0)
  2323.       return;
  2324.  
  2325.    p = strrchr(path, '/');
  2326. #ifdef _MSC_VER
  2327.    pv = strrchr(path, ':');
  2328.    if (pv > p)
  2329.       p = pv;
  2330.    pv = strrchr(path, '\\');
  2331.    if (pv > p)
  2332.       p = pv;
  2333. #endif
  2334.  
  2335.    if (p == 0)                  /* current directory */
  2336.       strcpy(path, ".");
  2337.    else if (p == path)          /* root directory */
  2338.       sprintf(path, "%c", *p);
  2339.    else
  2340.       *p = 0;
  2341.  
  2342.    return;
  2343. }
  2344.  
  2345. /*------------------------------------------------------------------*/
  2346.  
  2347. /**
  2348.  * Retieve node at a certain line number
  2349.  */
  2350. PMXML_NODE mxml_get_node_at_line(PMXML_NODE tree, int line_number)
  2351. {
  2352.    int i;
  2353.    PMXML_NODE pn;
  2354.  
  2355.    if (tree->line_number_start == line_number)
  2356.       return tree;
  2357.    
  2358.    for (i=0 ; i<tree->n_children ; i++) {
  2359.       pn = mxml_get_node_at_line(&tree->child[i], line_number);
  2360.       if (pn)
  2361.          return pn;
  2362.    }
  2363.    
  2364.    return NULL;
  2365. }
  2366.  
  2367.