[libvirt] [PATCH RFC 1/2] XML: Improve XML parsing error messages

Daniel Veillard veillard at redhat.com
Tue Sep 6 07:50:34 UTC 2011


On Mon, Sep 05, 2011 at 02:33:39PM +0200, Peter Krempa wrote:
> This patch modifies error handling function for the XML parser provided
> by libxml2.
> 
> Originaly only a line number and error message were logged. With this
> new error handler function, the user is provided with a more complex
> description of the parsing error.
> 
> Context of the error is printed in libXML2 style and filename of the
> file, that caused the error is printed. Example of an parse error:
> 
> 13:41:36.262: 16032: error : catchXMLError:706 :
> /etc/libvirt/qemu/rh_bad.xml:58: Opening and ending tag mismatch: name
> line 2 and domain
> </domain>
> ---------^
> 
> Context of the error gives the user hints that may help to quickly
> locate a corrupt xml file.
> 
> fixes BZs:
> ----------
> Bug 708735 - [RFE] Show column and line on XML parsing error
> https://bugzilla.redhat.com/show_bug.cgi?id=708735
> 
> Bug 726771 - libvirt does not specify problem file if persistent xml is
> invalid
> https://bugzilla.redhat.com/show_bug.cgi?id=726771
> ---
>  src/util/xml.c |   88 ++++++++++++++++++++++++++++++++++++++++++++++++-------
>  1 files changed, 76 insertions(+), 12 deletions(-)
> 
> diff --git a/src/util/xml.c b/src/util/xml.c
> index d301af6..b0942da 100644
> --- a/src/util/xml.c
> +++ b/src/util/xml.c
> @@ -633,28 +633,92 @@ virXPathNodeSet(const char *xpath,
>   * catchXMLError:
>   *
>   * Called from SAX on parsing errors in the XML.
> + *
> + * This version is heavily based on xmlParserPrintFileContextInternal from libxml2.
>   */
>  static void
>  catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
>  {
> -    int domcode = VIR_FROM_XML;
>      xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
> 
> -    if (ctxt) {
> -        if (ctxt->_private)
> +    const xmlChar *cur, *base;
> +    unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
> +    int domcode = VIR_FROM_XML;
> +
> +    virBuffer buf = VIR_BUFFER_INITIALIZER;
> +    char *contextstr = NULL;
> +    char *pointerstr = NULL;
> +
> +
> +    /* conditions for error printing */
> +    if (!ctxt ||
> +        (virGetLastError() != NULL) ||
> +        ctxt->input == NULL ||
> +        ctxt->lastError.level != XML_ERR_FATAL ||
> +        ctxt->lastError.message == NULL)
> +        return;
> +
> +    if (ctxt->_private)
>              domcode = ((struct virParserData *) ctxt->_private)->domcode;
> 
> -        if (virGetLastError() == NULL &&
> -            ctxt->lastError.level == XML_ERR_FATAL &&
> -            ctxt->lastError.message != NULL) {
> -            virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
> -                                  _("at line %d: %s"),
> -                                  ctxt->lastError.line,
> -                                  ctxt->lastError.message);
> -        }
> +
> +    cur = ctxt->input->cur;
> +    base = ctxt->input->base;
> +
> +    /* skip backwards over any end-of-lines */
> +    while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
> +        cur--;
>      }
> -}
> 
> +    /* search backwards for beginning-of-line (to max buff size) */
> +    while ((cur > base) && (*(cur) != '\n') && (*(cur) != '\r'))
> +        cur--;
> +    if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
> +
> +    /* calculate the error position in terms of the current position */
> +    col = ctxt->input->cur - cur;
> +
> +    /* search forward for end-of-line (to max buff size) */
> +    /* copy selected text to our buffer */
> +    while ((*cur != 0) && (*(cur) != '\n') && (*(cur) != '\r')) {
> +        virBufferAddChar(&buf, *cur++);
> +    }
> +
> +    /* create blank line with problem pointer */
> +    contextstr = virBufferContentAndReset(&buf);
> +
> +    /* (leave buffer space for pointer + line terminator) */
> +    for  (n = 0; (n<col) && (contextstr[n] != 0); n++) {
> +        if (contextstr[n] == '\t')
> +            virBufferAddChar(&buf, '\t');
> +        else
> +            virBufferAddChar(&buf, '-');
> +    }
> +
> +    virBufferAddChar(&buf, '^');
> +
> +    pointerstr = virBufferContentAndReset(&buf);
> +
> +    if (ctxt->lastError.file) {
> +        virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
> +                              _("%s:%d: %s%s\n%s"),
> +                              ctxt->lastError.file,
> +                              ctxt->lastError.line,
> +                              ctxt->lastError.message,
> +                              contextstr,
> +                              pointerstr);
> +    } else {
> +         virGenericReportError(domcode, VIR_ERR_XML_DETAIL,
> +                              _("at line %d: %s%s\n%s"),
> +                              ctxt->lastError.line,
> +                              ctxt->lastError.message,
> +                              contextstr,
> +                              pointerstr);
> +    }
> +
> +    VIR_FREE(contextstr);
> +    VIR_FREE(pointerstr);
> +}
> 

  ACK, looks okay, it's a bit convoluted, but the libxml2 routine
got an awful lot of use over the years and works, so ...

  pushed, thanks !

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list