xml_parser: Add generic parser.
authorTomas Mlcoch <tmlcoch@redhat.com>
Mon, 27 May 2013 14:49:46 +0000 (16:49 +0200)
committerTomas Mlcoch <tmlcoch@redhat.com>
Mon, 27 May 2013 14:49:46 +0000 (16:49 +0200)
src/xml_parser.c
src/xml_parser.h
src/xml_parser_filelists.c
src/xml_parser_internal.h

index 67255c3..71e53e8 100644 (file)
@@ -8,7 +8,6 @@ cr_ParserData *
 cr_xml_parser_data(unsigned int numstates)
 {
     cr_ParserData *pd = g_new0(cr_ParserData, 1);
-    pd->ret = CRE_OK;
     pd->content = g_malloc(CONTENT_REALLOC_STEP);
     pd->acontent = CONTENT_REALLOC_STEP;
     pd->msgs = g_string_new(0);
@@ -37,7 +36,7 @@ cr_char_handler(void *pdata, const XML_Char *s, int len)
     char *c;
     cr_ParserData *pd = pdata;
 
-    if (pd->ret != CRE_OK)
+    if (pd->err)
         return; /* There was an error -> do nothing */
 
     if (!pd->docontent)
@@ -76,3 +75,83 @@ cr_newpkgcb(cr_Package **pkg,
 
     return CRE_OK;
 }
+
+int
+cr_xml_parser_generic(XML_Parser parser,
+                      cr_ParserData *pd,
+                      const char *path,
+                      GError **err)
+{
+    /* Note: This function uses .err members of cr_ParserData! */
+
+    int ret = CRE_OK;
+    CR_FILE *f;
+    GError *tmp_err = NULL;
+
+    assert(parser);
+    assert(pd);
+    assert(path);
+    assert(!err || *err == NULL);
+
+    f = cr_open(path, CR_CW_MODE_READ, CR_CW_AUTO_DETECT_COMPRESSION, &tmp_err);
+    if (tmp_err) {
+        int code = tmp_err->code;
+        g_propagate_prefixed_error(err, tmp_err, "Cannot open %s: ", path);
+        return code;
+    }
+
+    while (1) {
+        int len;
+        void *buf = XML_GetBuffer(parser, XML_BUFFER_SIZE);
+        if (!buf) {
+            ret = CRE_MEMORY;
+            g_set_error(err, CR_XML_PARSER_FIL_ERROR, CRE_MEMORY,
+                        "Out of memory: Cannot allocate buffer for xml parser");
+            break;
+        }
+
+        len = cr_read(f, buf, XML_BUFFER_SIZE, &tmp_err);
+        if (tmp_err) {
+            ret = tmp_err->code;
+            g_critical("%s: Error while reading xml : %s\n",
+                       __func__, tmp_err->message);
+            g_propagate_prefixed_error(err, tmp_err, "Read error: ");
+            break;
+        }
+
+        if (!XML_ParseBuffer(parser, len, len == 0)) {
+            ret = CRE_XMLPARSER;
+            g_critical("%s: parsing error: %s\n",
+                       __func__, XML_ErrorString(XML_GetErrorCode(parser)));
+            g_set_error(err, CR_XML_PARSER_FIL_ERROR, CRE_XMLPARSER,
+                        "Parse error at line: %d (%s)",
+                        (int) XML_GetCurrentLineNumber(parser),
+                        (char *) XML_ErrorString(XML_GetErrorCode(parser)));
+            break;
+        }
+
+        if (pd->err) {
+            ret = pd->err->code;
+            g_propagate_error(err, pd->err);
+            break;
+        }
+
+        if (len == 0)
+            break;
+    }
+
+    if (ret != CRE_OK) {
+        // An error already encoutentered
+        // just close the file without error checking
+        cr_close(f, NULL);
+    } else {
+        // No error encountered yet
+        cr_close(f, &tmp_err);
+        if (tmp_err) {
+            ret = tmp_err->code;
+            g_propagate_prefixed_error(err, tmp_err, "Error while closing: ");
+        }
+    }
+
+    return ret;
+}
index 14be9c0..078dab2 100644 (file)
@@ -31,11 +31,15 @@ extern "C" {
  *  @{
  */
 
+#define CR_CB_RET_OK    0
+#define CR_CB_RET_ERR   1
+
+
 /** Callback for XML parser wich is called when a package element is parsed.
  * @param pkg       Currently parsed package.
  * @param cbdata    User data.
  * @err             GError **
- * @return          0 - OK, 1 - ERROR (stops the parsing)
+ * @return          CR_CB_RET_OK (0) or CR_CB_RET_ERR (1) - stops the parsing
  */
 typedef int (*cr_XmlParserPkgCb)(cr_Package *pkg,
                                  void *cbdata,
@@ -47,13 +51,13 @@ typedef int (*cr_XmlParserPkgCb)(cr_Package *pkg,
  * filled (by other XML parsers) package object.
  * If the pointer is set to NULL, current package will be skiped.
  * Note: For the primary.xml file pkgId, name and arch are NULL!
- * @param pkg           Package that will be populated.
- * @param pkgId         pkgId (hash) of the new package.
- * @param name          Name of the new package.
- * @param arch          Arch of the new package.
- * @param cbdata        User data.
- * @param err           GError **
- * @return              0 - OK, 1 - ERR (stops the parsing)
+ * @param pkg       Package that will be populated.
+ * @param pkgId     pkgId (hash) of the new package.
+ * @param name      Name of the new package.
+ * @param arch      Arch of the new package.
+ * @param cbdata    User data.
+ * @param err       GError **
+ * @return          CR_CB_RET_OK (0) or CR_CB_RET_ERR (1) - stops the parsing
  */
 typedef int (*cr_XmlParserNewPkgCb)(cr_Package **pkg,
                                     const char *pkgId,
index 037eee6..574773a 100644 (file)
@@ -60,7 +60,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
     cr_ParserData *pd = pdata;
     cr_StatesSwitch *sw;
 
-    if (pd->ret != CRE_OK)
+    if (pd->err)
         return;  // There was an error -> do nothing
 
     if (pd->depth != pd->statedepth) {
@@ -103,7 +103,6 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
 
         if (!pkgId) {
             // Package without a pkgid attr is error
-            pd->ret = CRE_BADXMLFILELISTS;
             g_set_error(&pd->err, CR_XML_PARSER_FIL_ERROR, CRE_BADXMLFILELISTS,
                         "Package pkgid attributte is missing!");
             break;
@@ -118,7 +117,6 @@ cr_start_handler(void *pdata, const char *element, const char **attr)
                          pd->newpkgcb_data,
                          &tmp_err))
         {
-            pd->ret = CRE_CBINTERRUPTED;
             if (tmp_err)
                 g_propagate_prefixed_error(&pd->err,
                                            tmp_err,
@@ -190,7 +188,7 @@ cr_end_handler(void *pdata, const char *element)
 
     CR_UNUSED(element);
 
-    if (pd->ret != CRE_OK)
+    if (pd->err)
         return; /* There was an error -> do nothing */
 
     if (pd->depth != pd->statedepth) {
@@ -215,7 +213,6 @@ cr_end_handler(void *pdata, const char *element)
             return;
 
         if (pd->pkgcb && pd->pkgcb(pd->pkg, pd->pkgcb_data, &tmp_err)) {
-            pd->ret = CRE_CBINTERRUPTED;
             if (tmp_err)
                 g_propagate_prefixed_error(&pd->err,
                                            tmp_err,
@@ -266,7 +263,6 @@ cr_xml_parse_filelists(const char *path,
                        GError **err)
 {
     int ret = CRE_OK;
-    CR_FILE *f;
     cr_ParserData *pd;
     XML_Parser parser;
     char *msgs;
@@ -280,12 +276,7 @@ cr_xml_parse_filelists(const char *path,
     if (!newpkgcb)  // Use default newpkgcb
         newpkgcb = cr_newpkgcb;
 
-    f = cr_open(path, CR_CW_MODE_READ, CR_CW_AUTO_DETECT_COMPRESSION, &tmp_err);
-    if (tmp_err) {
-        int code = tmp_err->code;
-        g_propagate_prefixed_error(err, tmp_err, "Cannot open %s: ", path);
-        return code;
-    }
+    // Init
 
     parser = XML_ParserCreate(NULL);
     XML_SetElementHandler(parser, cr_start_handler, cr_end_handler);
@@ -306,47 +297,13 @@ cr_xml_parse_filelists(const char *path,
 
     XML_SetUserData(parser, pd);
 
-    while (1) {
-        int len;
-        void *buf = XML_GetBuffer(parser, XML_BUFFER_SIZE);
-        if (!buf) {
-            ret = CRE_MEMORY;
-            g_set_error(err, CR_XML_PARSER_FIL_ERROR, CRE_MEMORY,
-                        "Out of memory: Cannot allocate buffer for xml parser");
-            break;
-        }
-
-        len = cr_read(f, buf, XML_BUFFER_SIZE, &tmp_err);
-        if (tmp_err) {
-            ret = tmp_err->code;
-            g_critical("%s: Error while reading xml : %s\n",
-                       __func__, tmp_err->message);
-            g_propagate_prefixed_error(err, tmp_err, "Read error: ");
-            break;
-        }
-
-        if (!XML_ParseBuffer(parser, len, len == 0)) {
-            ret = CRE_XMLPARSER;
-            g_critical("%s: parsing error: %s\n",
-                       __func__, XML_ErrorString(XML_GetErrorCode(parser)));
-            g_set_error(err, CR_XML_PARSER_FIL_ERROR, CRE_XMLPARSER,
-                        "Parse error at line: %d (%s)",
-                        (int) XML_GetCurrentLineNumber(parser),
-                        (char *) XML_ErrorString(XML_GetErrorCode(parser)));
-            break;
-        }
-
-        if (pd->ret != CRE_OK) {
-            ret = pd->ret;
-            break;
-        }
+    // Parsing
 
-        if (len == 0)
-            break;
-    }
+    ret = cr_xml_parser_generic(parser, pd, path, &tmp_err);
+    if (tmp_err)
+        g_propagate_error(err, tmp_err);
 
-    if (pd->err)
-        g_propagate_error(err, pd->err);
+    // Clean up
 
     if (ret != CRE_OK && newpkgcb == cr_newpkgcb) {
         // Prevent memory leak when the parsing is interrupted by an error.
@@ -359,18 +316,12 @@ cr_xml_parse_filelists(const char *path,
     }
 
     msgs = cr_xml_parser_data_free(pd);
-    XML_ParserFree(parser);
-    cr_close(f, &tmp_err);
-    if (tmp_err) {
-        int code = tmp_err->code;
-        g_propagate_prefixed_error(err, tmp_err, "Error while closing: ");
-        return code;
-    }
-
     if (messages)
         *messages = msgs;
     else
         g_free(msgs);
 
+    XML_ParserFree(parser);
+
     return ret;
 }
index ae8caef..e011061 100644 (file)
@@ -33,6 +33,15 @@ extern "C" {
 #define XML_BUFFER_SIZE         8192
 #define CONTENT_REALLOC_STEP    256
 
+/* Some notes about XML parsing (primary, filelists, other)
+ * ========================================================
+ * - Error during parsing is indicated via cr_ParserData->err member.
+ * - User specified callback have to be sanitized! User callbacks
+ *   are allowed return CR_CB_RET_ERR and do not set the GError.
+ *   So if the CR_CB_RET_ERR is returned and GError not setted, caller
+ *   of the callback has to set the GError by himself.
+ */
+
 /* File types in filelists.xml */
 typedef enum {
     FILE_FILE,
@@ -49,10 +58,11 @@ typedef struct {
 } cr_StatesSwitch;
 
 typedef struct _cr_ParserData {
-    int          ret;        /*!< status of parsing (return code) */
     int          depth;
     int          statedepth;
     unsigned int state;      /*!< current state */
+    GError       *err;       /*!< Error message */
+
 
     /* Tag content related values */
 
@@ -81,8 +91,6 @@ typedef struct _cr_ParserData {
         The package which is currently loaded. */
     GString                 *msgs;              /*!<
         Messages from xml parser (warnings about unknown elements etc.) */
-    GError *err;                                /*!<
-        Error message */
 
     /* Filelists related stuff */
 
@@ -127,6 +135,14 @@ int cr_newpkgcb(cr_Package **pkg,
                 void *cbdata,
                 GError **err);
 
+/** Generic parser.
+ */
+int
+cr_xml_parser_generic(XML_Parser parser,
+                      cr_ParserData *pd,
+                      const char *path,
+                      GError **err);
+
 #ifdef __cplusplus
 }
 #endif