Imported Upstream version 0.7.19
[platform/upstream/libsolv.git] / ext / repo_helix.c
index 25148fd..f4bea55 100644 (file)
@@ -7,20 +7,26 @@
 
 /*
  * repo_helix.c
- * 
+ *
  * Parse 'helix' XML representation
  * and create 'repo'
- * 
+ *
+ * A bit of history: "Helix Code" was the name of the company that
+ * wrote Red Carpet. The company was later renamed to Ximian.
+ * The Red Carpet solver was merged into the ZYPP project, the
+ * library used both by ZENworks and YaST for package management.
+ * Red Carpet came with solver testcases in its own repository
+ * format, the 'helix' format.
+ *
  */
 
 #include <sys/types.h>
-#include <limits.h>
-#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <expat.h>
 
+#include "queue.h"
+#include "solv_xmlparser.h"
 #include "repo_helix.h"
 #include "evr.h"
 
@@ -68,37 +74,27 @@ enum state {
   STATE_PATCH,
   STATE_PRODUCT,
 
-  STATE_PEPOCH,
-  STATE_PVERSION,
-  STATE_PRELEASE,
-  STATE_PARCH,
-
   NUMSTATES
 };
 
-struct stateswitch {
-  enum state from;
-  char *ename;
-  enum state to;
-  int docontent;
-};
-
-static struct stateswitch stateswitches[] = {
+static struct solv_xmlparser_element stateswitches[] = {
   { STATE_START,       "channel",         STATE_CHANNEL, 0 },
   { STATE_CHANNEL,     "subchannel",      STATE_SUBCHANNEL, 0 },
   { STATE_SUBCHANNEL,  "package",         STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "srcpackage",      STATE_PACKAGE, 0 },
   { STATE_SUBCHANNEL,  "selection",       STATE_PACKAGE, 0 },
   { STATE_SUBCHANNEL,  "pattern",         STATE_PACKAGE, 0 },
   { STATE_SUBCHANNEL,  "atom",            STATE_PACKAGE, 0 },
   { STATE_SUBCHANNEL,  "patch",           STATE_PACKAGE, 0 },
   { STATE_SUBCHANNEL,  "product",         STATE_PACKAGE, 0 },
+  { STATE_SUBCHANNEL,  "application",     STATE_PACKAGE, 0 },
   { STATE_PACKAGE,     "name",            STATE_NAME, 1 },
   { STATE_PACKAGE,     "vendor",          STATE_VENDOR, 1 },
   { STATE_PACKAGE,     "buildtime",       STATE_BUILDTIME, 1 },
-  { STATE_PACKAGE,     "epoch",           STATE_PEPOCH, 1 },
-  { STATE_PACKAGE,     "version",         STATE_PVERSION, 1 },
-  { STATE_PACKAGE,     "release",         STATE_PRELEASE, 1 },
-  { STATE_PACKAGE,     "arch",            STATE_PARCH, 1 },
+  { STATE_PACKAGE,     "epoch",           STATE_EPOCH, 1 },
+  { STATE_PACKAGE,     "version",         STATE_VERSION, 1 },
+  { STATE_PACKAGE,     "release",         STATE_RELEASE, 1 },
+  { STATE_PACKAGE,     "arch",            STATE_ARCH, 1 },
   { STATE_PACKAGE,     "history",         STATE_HISTORY, 0 },
   { STATE_PACKAGE,     "provides",        STATE_PROVIDES, 0 },
   { STATE_PACKAGE,     "requires",        STATE_REQUIRES, 0 },
@@ -110,6 +106,7 @@ static struct stateswitch stateswitches[] = {
   { STATE_PACKAGE,     "suggests",        STATE_SUGGESTS, 0 },
   { STATE_PACKAGE,     "enhances",        STATE_ENHANCES, 0 },
   { STATE_PACKAGE,     "freshens",        STATE_FRESHENS, 0 },
+  { STATE_PACKAGE,     "deps",            STATE_PACKAGE, 0 },  /* ignore deps element */
 
   { STATE_HISTORY,     "update",          STATE_UPDATE, 0 },
   { STATE_UPDATE,      "epoch",           STATE_EPOCH, 1 },
@@ -135,81 +132,68 @@ static struct stateswitch stateswitches[] = {
  * parser data
  */
 
-typedef struct _parsedata {
-  // XML parser data
-  int depth;
-  enum state state;    // current state
-  int statedepth;
-  char *content;       // buffer for content of node
-  int lcontent;                // actual length of current content
-  int acontent;                // actual buffer size
-  int docontent;       // handle content
-
-  // repo data
-  Pool *pool;          // current pool
-  Repo *repo;          // current repo
-  Repodata *data;       // current repo data
-  Solvable *solvable;  // current solvable
-  Offset freshens;     // current freshens vector
-
-  // package data
-  int  epoch;          // epoch (as offset into evrspace)
-  int  version;                // version (as offset into evrspace)
-  int  release;                // release (as offset into evrspace)
-  char *evrspace;      // buffer for evr
-  int  aevrspace;      // actual buffer space
-  int  levrspace;      // actual evr length
+struct parsedata {
+  int ret;
+  /* repo data */
+  Pool *pool;          /* current pool */
+  Repo *repo;          /* current repo */
+  Repodata *data;       /* current repo data */
+  Solvable *solvable;  /* current solvable */
+  Offset freshens;     /* current freshens vector */
+
+  /* package data */
+  int  srcpackage;     /* is srcpackage element */
+  int  epoch;          /* epoch (as offset into evrspace) */
+  int  version;                /* version (as offset into evrspace) */
+  int  release;                /* release (as offset into evrspace) */
+  char *evrspace;      /* buffer for evr */
+  int  aevrspace;      /* actual buffer space */
+  int  levrspace;      /* actual evr length */
   char *kind;
 
-  struct stateswitch *swtab[NUMSTATES];
-  enum state sbtab[NUMSTATES];
-} Parsedata;
+  struct solv_xmlparser xmlp;
+};
 
 
 /*------------------------------------------------------------------*/
 /* E:V-R handling */
 
-// create Id from epoch:version-release
+/* create Id from epoch:version-release */
 
 static Id
-evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
+evr2id(Pool *pool, struct parsedata *pd, const char *e, const char *v, const char *r)
 {
-  char *c;
+  char *c, *space;
   int l;
 
-  // treat explitcit 0 as NULL
-  if (e && !strcmp(e, "0"))
-    e = NULL;
+  /* treat explitcit 0 as NULL */
+  if (e && (!*e || !strcmp(e, "0")))
+    e = 0;
 
   if (v && !e)
     {
       const char *v2;
-      // scan version for ":"
-      for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)     // skip leading digits
+      /* scan version for ":" */
+      for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)     /* skip leading digits */
         ;
-      // if version contains ":", set epoch to "0"
+      /* if version contains ":", set epoch to "0" */
       if (v2 > v && *v2 == ':')
        e = "0";
     }
-  
-  // compute length of Id string
-  l = 1;  // for the \0
+
+  /* compute length of Id string */
+  l = 1;  /* for the \0 */
   if (e)
-    l += strlen(e) + 1;  // e:
+    l += strlen(e) + 1;  /* e: */
   if (v)
-    l += strlen(v);      // v
+    l += strlen(v);      /* v */
   if (r)
-    l += strlen(r) + 1;  // -r
+    l += strlen(r) + 1;  /* -r */
 
-  // extend content if not sufficient
-  if (l > pd->acontent)
-    {
-      pd->content = (char *)realloc(pd->content, l + 256);
-      pd->acontent = l + 256;
-    }
+  /* get content space */
+  c = space = solv_xmlparser_contentspace(&pd->xmlp, l);
 
-  // copy e-v-r to content
-  c = pd->content;
+  /* copy e-v-r */
   if (e)
     {
       strcpy(c, e);
@@ -228,24 +212,24 @@ evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
       c += strlen(c);
     }
   *c = 0;
-  // if nothing inserted, return Id 0
-  if (!*pd->content)
-    return ID_NULL;
+  /* if nothing inserted, return Id 0 */
+  if (!*space)
+    return 0;
 #if 0
-  fprintf(stderr, "evr: %s\n", pd->content);
+  fprintf(stderr, "evr: %s\n", space);
 #endif
-  // intern and create
-  return pool_str2id(pool, pd->content, 1);
+  /* intern and create */
+  return pool_str2id(pool, space, 1);
 }
 
 
-// create e:v-r from attributes
-// atts is array of name,value pairs, NULL at end
-//   even index into atts is name
-//   odd index is value
-//
+/* create e:v-r from attributes
+ * atts is array of name,value pairs, NULL at end
+ *   even index into atts is name
+ *   odd index is value
+ */
 static Id
-evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
+evr_atts2id(Pool *pool, struct parsedata *pd, const char **atts)
 {
   const char *e, *v, *r;
   e = v = r = 0;
@@ -302,7 +286,7 @@ static struct flagtab flagtab[] = {
  */
 
 static unsigned int
-adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, Id marker)
+adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, Id marker)
 {
   Id id, name;
   const char *n, *f, *k;
@@ -332,13 +316,9 @@ adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, Id ma
   if (k)                              /* if kind!=package, intern <kind>:<name> */
     {
       int l = strlen(k) + 1 + strlen(n) + 1;
-      if (l > pd->acontent)           /* extend buffer if needed */
-       {
-         pd->content = (char *)realloc(pd->content, l + 256);
-         pd->acontent = l + 256;
-       }
-      sprintf(pd->content, "%s:%s", k, n);
-      name = pool_str2id(pool, pd->content, 1);
+      char *space = solv_xmlparser_contentspace(&pd->xmlp, l);
+      sprintf(space, "%s:%s", k, n);
+      name = pool_str2id(pool, space, 1);
     }
   else
     {
@@ -372,76 +352,30 @@ adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, Id ma
 
 /*----------------------------------------------------------------*/
 
-/*
- * XML callback
- * <name>
- * 
- */
-
-static void XMLCALL
-startElement(void *userData, const char *name, const char **atts)
+static void
+startElement(struct solv_xmlparser *xmlp, int state, const char *name, const char **atts)
 {
-  Parsedata *pd = (Parsedata *)userData;
-  struct stateswitch *sw;
+  struct parsedata *pd = xmlp->userdata;
   Pool *pool = pd->pool;
   Solvable *s = pd->solvable;
 
-  if (pd->depth != pd->statedepth)
-    {
-      pd->depth++;
-      return;
-    }
-
-  /* ignore deps element */
-  if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
-    return;
-
-  pd->depth++;
-
-  /* find node name in stateswitch */
-  if (!pd->swtab[pd->state])
-    return;
-  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
-  {
-    if (!strcmp(sw->ename, name))
-      break;
-  }
-
-  /* check if we're at the right level */
-  if (sw->from != pd->state)
-    {
-#if 0
-      fprintf(stderr, "into unknown: %s\n", name);
-#endif
-      return;
-    }
-  
-  // set new state
-  pd->state = sw->to;
-
-  pd->docontent = sw->docontent;
-  pd->statedepth = pd->depth;
-
-  // start with empty content
-  // (will collect data until end element
-  pd->lcontent = 0;
-  *pd->content = 0;
-
-  switch (pd->state)
+  switch (state)
     {
 
     case STATE_NAME:
       if (pd->kind)                   /* if kind is set (non package) */
         {
-          strcpy(pd->content, pd->kind);
-          pd->lcontent = strlen(pd->content);
-         pd->content[pd->lcontent++] = ':';   /* prefix name with '<kind>:' */
-         pd->content[pd->lcontent] = 0;
+          strcpy(xmlp->content, pd->kind);
+          xmlp->lcontent = strlen(xmlp->content);
+         xmlp->content[xmlp->lcontent++] = ':';   /* prefix name with '<kind>:' */
+         xmlp->content[xmlp->lcontent] = 0;
        }
       break;
 
     case STATE_PACKAGE:                       /* solvable name */
       pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+      pd->srcpackage = 0;
+      pd->kind = NULL;                /* default is (src)package */
       if (!strcmp(name, "selection"))
         pd->kind = "selection";
       else if (!strcmp(name, "pattern"))
@@ -452,8 +386,10 @@ startElement(void *userData, const char *name, const char **atts)
         pd->kind = "product";
       else if (!strcmp(name, "patch"))
         pd->kind = "patch";
-      else
-        pd->kind = NULL;              /* default is package */
+      else if (!strcmp(name, "application"))
+        pd->kind = "application";
+      else if (!strcmp(name, "srcpackage"))
+       pd->srcpackage = 1;
       pd->levrspace = 1;
       pd->epoch = 0;
       pd->version = 0;
@@ -530,11 +466,12 @@ startElement(void *userData, const char *name, const char **atts)
     }
 }
 
-static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
+static const char *
+findKernelFlavor(struct parsedata *pd, Solvable *s)
 {
   Pool *pool = pd->pool;
   Id pid, *pidp;
-  
+
   if (s->provides)
     {
       pidp = pd->repo->idarraydata + s->provides;
@@ -542,7 +479,7 @@ static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
        {
          Reldep *prd;
          const char *depname;
-         
+
          if (!ISRELDEP(pid))
            continue;               /* wrong provides name */
          prd = GETRELDEP(pool, pid);
@@ -562,8 +499,8 @@ static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
          if (!ISRELDEP(pid))
            {
              depname = pool_id2str(pool, pid);
-           } 
-         else 
+           }
+         else
            {
              Reldep *prd = GETRELDEP(pool, pid);
              depname = pool_id2str(pool, prd->name);
@@ -577,40 +514,22 @@ static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
 }
 
 
-/*
- * XML callback
- * </name>
- * 
- * create Solvable from collected data
- */
-
-static void XMLCALL
-endElement(void *userData, const char *name)
+static void
+endElement(struct solv_xmlparser *xmlp, int state, char *content)
 {
-  Parsedata *pd = (Parsedata *)userData;
+  struct parsedata *pd = xmlp->userdata;
   Pool *pool = pd->pool;
   Solvable *s = pd->solvable;
   Id evr;
   unsigned int t = 0;
   const char *flavor;
 
-  if (pd->depth != pd->statedepth)
-    {
-      pd->depth--;
-      // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-      return;
-    }
-
-  /* ignore deps element */
-  if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
-    return;
-
-  pd->depth--;
-  pd->statedepth--;
-  switch (pd->state)
+  switch (state)
     {
 
     case STATE_PACKAGE:                       /* package complete */
+      if (pd->srcpackage && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+       s->arch = ARCH_SRC;
       if (!s->arch)                    /* default to "noarch" */
        s->arch = ARCH_NOARCH;
 
@@ -622,13 +541,12 @@ endElement(void *userData, const char *name)
       /* ensure self-provides */
       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
         s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
-      s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
-      s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
+      repo_rewrite_suse_deps(s, pd->freshens);
       pd->freshens = 0;
 
       /* see bugzilla bnc#190163 */
       flavor = findKernelFlavor(pd, s);
-      if (flavor) 
+      if (flavor)
        {
          char *cflavor = solv_strdup(flavor);  /* make pointer safe */
 
@@ -707,13 +625,13 @@ endElement(void *userData, const char *name)
        }
       break;
     case STATE_NAME:
-      s->name = pool_str2id(pool, pd->content, 1);
+      s->name = pool_str2id(pool, content, 1);
       break;
     case STATE_VENDOR:
-      s->vendor = pool_str2id(pool, pd->content, 1);
+      s->vendor = pool_str2id(pool, content, 1);
       break;
     case STATE_BUILDTIME:
-      t = atoi (pd->content);
+      t = atoi(content);
       if (t)
        repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
       break;   
@@ -727,148 +645,75 @@ endElement(void *userData, const char *name)
       pd->version = 0;
       pd->release = 0;
       /* use highest evr */
-      if (!s->evr || pool_evrcmp(pool, s->evr, evr, EVRCMP_MATCH_RELEASE) <= 0)
+      if (!s->evr || pool_evrcmp(pool, s->evr, evr, EVRCMP_COMPARE) <= 0)
        s->evr = evr;
       break;
     case STATE_EPOCH:
     case STATE_VERSION:
     case STATE_RELEASE:
-    case STATE_PEPOCH:
-    case STATE_PVERSION:
-    case STATE_PRELEASE:
       /* ensure buffer space */
-      if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
+      if (xmlp->lcontent + 1 + pd->levrspace > pd->aevrspace)
        {
-         pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
-         pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
+         pd->aevrspace = xmlp->lcontent + 1 + pd->levrspace + 256;
+         pd->evrspace = (char *)solv_realloc(pd->evrspace, pd->aevrspace);
        }
-      memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
-      if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
+      memcpy(pd->evrspace + pd->levrspace, xmlp->content, xmlp->lcontent + 1);
+      if (state == STATE_EPOCH)
        pd->epoch = pd->levrspace;
-      else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
+      else if (state == STATE_VERSION)
        pd->version = pd->levrspace;
       else
        pd->release = pd->levrspace;
-      pd->levrspace += pd->lcontent + 1;
+      pd->levrspace += xmlp->lcontent + 1;
       break;
     case STATE_ARCH:
-    case STATE_PARCH:
-      s->arch = pool_str2id(pool, pd->content, 1);
+      s->arch = pool_str2id(pool, content, 1);
       break;
     default:
       break;
     }
-  pd->state = pd->sbtab[pd->state];
-  pd->docontent = 0;
-  // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
-}
-
-
-/*
- * XML callback
- * character data
- * 
- */
-
-static void XMLCALL
-characterData(void *userData, const XML_Char *s, int len)
-{
-  Parsedata *pd = (Parsedata *)userData;
-  int l;
-  char *c;
-
-  // check if current nodes content is interesting
-  if (!pd->docontent)
-    return;
-
-  // adapt content buffer
-  l = pd->lcontent + len + 1;
-  if (l > pd->acontent)
-    {
-      pd->content = (char *)realloc(pd->content, l + 256);
-      pd->acontent = l + 256;
-    }
-  // append new content to buffer
-  c = pd->content + pd->lcontent;
-  pd->lcontent += len;
-  while (len-- > 0)
-    *c++ = *s++;
-  *c = 0;
 }
 
 /*-------------------------------------------------------------------*/
 
-#define BUFF_SIZE 8192
-
 /*
  * read 'helix' type xml from fp
  * add packages to pool/repo
- * 
+ *
  */
 
 int
 repo_add_helix(Repo *repo, FILE *fp, int flags)
 {
   Pool *pool = repo->pool;
-  Parsedata pd;
+  struct parsedata pd;
   Repodata *data;
-  char buf[BUFF_SIZE];
-  int i, l;
-  struct stateswitch *sw;
   unsigned int now;
-  XML_Parser parser;
 
   now = solv_timems(0);
   data = repo_add_repodata(repo, flags);
-  
+
   /* prepare parsedata */
   memset(&pd, 0, sizeof(pd));
-  for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
-    {
-      if (!pd.swtab[sw->from])
-        pd.swtab[sw->from] = sw;
-      pd.sbtab[sw->to] = sw->from;
-    }
-
   pd.pool = pool;
   pd.repo = repo;
-
-  pd.content = (char *)malloc(256);    /* must hold all solvable kinds! */
-  pd.acontent = 256;
-  pd.lcontent = 0;
-
-  pd.evrspace = (char *)malloc(256);
-  pd.aevrspace= 256;
-  pd.levrspace = 1;
   pd.data = data;
 
-  // set up XML parser
+  pd.evrspace = (char *)solv_malloc(256);
+  pd.aevrspace = 256;
+  pd.levrspace = 1;
 
-  parser = XML_ParserCreate(NULL);
-  XML_SetUserData(parser, &pd);       /* make parserdata available to XML callbacks */
-  XML_SetElementHandler(parser, startElement, endElement);
-  XML_SetCharacterDataHandler(parser, characterData);
+  solv_xmlparser_init(&pd.xmlp, stateswitches, &pd, startElement, endElement);
+  if (solv_xmlparser_parse(&pd.xmlp, fp) != SOLV_XMLPARSER_OK)
+    pd.ret = pool_error(pd.pool, -1, "repo_helix: %s at line %u", pd.xmlp.errstr, pd.xmlp.line);
+  solv_xmlparser_free(&pd.xmlp);
 
-  // read/parse XML file
-  for (;;)
-    {
-      l = fread(buf, 1, sizeof(buf), fp);
-      if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
-       {
-         pool_debug(pool, SOLV_FATAL, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
-         exit(1);
-       }
-      if (l == 0)
-       break;
-    }
-  XML_ParserFree(parser);
-  free(pd.content);
-  free(pd.evrspace);
+  solv_free(pd.evrspace);
 
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
   POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_helix took %d ms\n", solv_timems(now));
   POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
   POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
-  return 0;
+  return pd.ret;
 }