- make unknown elements less fatal
[platform/upstream/libsolv.git] / tools / repo_patchxml.c
index f9a06f1..f7fdb0e 100644 (file)
 #include <expat.h>
 
 #include "pool.h"
+#include "repo.h"
 #include "repo_patchxml.h"
 #include "repo_rpmmd.h"
 
+//#define TESTMM
 
 enum state {
   STATE_START,
@@ -25,6 +27,16 @@ enum state {
   STATE_NAME,
   STATE_ARCH,
   STATE_VERSION,
+  STATE_SUMMARY,
+  STATE_DESCRIPTION,
+  STATE_CATEGORY,
+  STATE_PKGFILES,
+  STATE_DELTARPM,
+  STATE_DLOCATION,
+  STATE_DCHECKSUM,
+  STATE_DTIME,
+  STATE_DSIZE,
+  STATE_DBASEVERSION,
   STATE_REQUIRES,
   STATE_REQUIRESENTRY,
   STATE_PROVIDES,
@@ -43,11 +55,11 @@ enum state {
   STATE_ENHANCESENTRY,
   STATE_FRESHENS,
   STATE_FRESHENSENTRY,
+  STATE_REBOOT,
+  STATE_RESTART,
   NUMSTATES
 };
 
-#define PACK_BLOCK 255
-
 
 struct stateswitch {
   enum state from;
@@ -66,6 +78,11 @@ static struct stateswitch stateswitches[] = {
   { STATE_PATCH,       "name",            STATE_NAME, 1 },
   { STATE_PATCH,       "arch",            STATE_ARCH, 1 },
   { STATE_PATCH,       "version",         STATE_VERSION, 0 },
+  { STATE_PATCH,       "summary",         STATE_SUMMARY, 1 },
+  { STATE_PATCH,       "description",     STATE_DESCRIPTION, 1 },
+  { STATE_PATCH,       "category",        STATE_CATEGORY, 1 },
+  { STATE_PATCH,       "reboot-needed",   STATE_REBOOT, 0 },
+  { STATE_PATCH,       "package-manager", STATE_RESTART, 0 },
   { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
   { STATE_PATCH,       "rpm:provides",    STATE_PROVIDES, 0 },
   { STATE_PATCH,       "rpm:requires",    STATE_REQUIRES, 0 },
@@ -78,6 +95,14 @@ static struct stateswitch stateswitches[] = {
   { STATE_PATCH,       "rpm:freshens",    STATE_FRESHENS, 0 },
   { STATE_PATCH,       "suse:freshens",   STATE_FRESHENS, 0 },
   { STATE_PATCH,       "atoms",          STATE_START, 0 },
+  { STATE_PATCH,       "pkgfiles",        STATE_PKGFILES, 0 },
+  { STATE_PKGFILES,    "deltarpm",        STATE_DELTARPM, 0 },
+  { STATE_PKGFILES,    "patchrpm",        STATE_DELTARPM, 0 },
+  { STATE_DELTARPM,    "location",        STATE_DLOCATION, 0 },
+  { STATE_DELTARPM,    "checksum",        STATE_DCHECKSUM, 1 },
+  { STATE_DELTARPM,    "time",            STATE_DTIME, 0 },
+  { STATE_DELTARPM,    "size",            STATE_DSIZE, 0 },
+  { STATE_DELTARPM,    "base-version",    STATE_DBASEVERSION, 0 },
   { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
   { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
   { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
@@ -91,6 +116,24 @@ static struct stateswitch stateswitches[] = {
   { NUMSTATES}
 };
 
+/* Cumulated info about the current deltarpm or patchrpm */
+struct deltarpm {
+  Id locdir;
+  Id locname;
+  Id locevr;
+  Id locsuffix;
+  unsigned buildtime;
+  unsigned downloadsize, archivesize;
+  char *filechecksum;
+  /* Baseversions.  deltarpm only has one, patchrpm may have more.  */
+  Id *bevr;
+  unsigned nbevr;
+  /* If deltarpm, then this is filled.  */
+  Id seqname;
+  Id seqevr;
+  char *seqnum;
+};
+
 struct parsedata {
   int depth;
   enum state state;
@@ -99,16 +142,44 @@ struct parsedata {
   int lcontent;
   int acontent;
   int docontent;
-  int pack;
   Pool *pool;
   Repo *repo;
-  Solvable *start;
+  Repodata *data;
+  unsigned int datanum;
+  Solvable *solvable;
   char *kind;
-
+  Offset freshens;
+  unsigned int timestamp;
+  
   struct stateswitch *swtab[NUMSTATES];
   enum state sbtab[NUMSTATES];
+  char *tempstr;
+  int ltemp;
+  int atemp;
+  struct deltarpm delta;
 };
 
+#if 0
+static void
+append_str(struct parsedata *pd, const char *s)
+{
+  if (!s)
+    return;
+  int l = pd->ltemp + strlen(s) + 1;
+  if (l > pd->atemp)
+    {
+      pd->tempstr = realloc(pd->tempstr, l + 256);
+      pd->atemp = l + 256;
+    }
+  strcpy(pd->tempstr + pd->ltemp, s);
+  pd->ltemp += strlen(s);
+}
+#endif
+
+/*
+ * create evr (as Id) from 'epoch', 'ver' and 'rel' attributes
+ */
+
 static Id
 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
 {
@@ -174,6 +245,27 @@ makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
   return str2id(pool, pd->content, 1);
 }
 
+
+/*
+ * find attribute
+ */
+
+static const char *
+find_attr(const char *txt, const char **atts)
+{
+  for (; *atts; atts += 2)
+    {
+      if (!strcmp(*atts, txt))
+        return atts[1];
+    }
+  return 0;
+}
+
+
+/*
+ * relation comparision operators
+ */
+
 static char *flagtab[] = {
   "GT",
   "EQ",
@@ -183,14 +275,20 @@ static char *flagtab[] = {
   "LE"
 };
 
+
+/*
+ * add dependency
+ */
+
 static unsigned int
 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
 {
-  Id id, name;
+  Id id, name, marker;
   const char *n, *f, *k;
   const char **a;
 
   n = f = k = 0;
+  marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
   for (a = atts; *a; a += 2)
     {
       if (!strcmp(*a, "name"))
@@ -200,11 +298,11 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts
       else if (!strcmp(*a, "kind"))
        k = a[1];
       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
-       isreq = 2;
+       marker = SOLVABLE_PREREQMARKER;
     }
   if (!n)
     return olddeps;
-  if (k && !strcmp(k, "package"))
+  if (k && !strcmp(k, "package"))            /* kind 'package' -> ignore */
     k = 0;
   if (k)
     {
@@ -214,12 +312,12 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts
          pd->content = realloc(pd->content, l + 256);
          pd->acontent = l + 256;
        }
-      sprintf(pd->content, "%s:%s", k, n); 
+      sprintf(pd->content, "%s:%s", k, n);   /* prepend kind to name */
       name = str2id(pool, pd->content, 1); 
     }
   else
     name = str2id(pool, (char *)n, 1);
-  if (f)
+  if (f)                                     /* flags means name,operator,relation */
     {
       Id evr = makeevr_atts(pool, pd, atts);
       int flags;
@@ -234,7 +332,7 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts
 #if 0
   fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
 #endif
-  return repo_addid_dep(pd->repo, olddeps, id, isreq);
+  return repo_addid_dep(pd->repo, olddeps, id, marker);
 }
 
 
@@ -243,8 +341,9 @@ startElement(void *userData, const char *name, const char **atts)
 {
   struct parsedata *pd = userData;
   Pool *pool = pd->pool;
-  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+  Solvable *s = pd->solvable;
   struct stateswitch *sw;
+  const char *str;
 
   if (pd->depth != pd->statedepth)
     {
@@ -256,9 +355,12 @@ startElement(void *userData, const char *name, const char **atts)
     return;
 
   pd->depth++;
-  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
+  if (!pd->swtab[pd->state])
+    return;
+  for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)  /* find name in statetable */
     if (!strcmp(sw->ename, name))
       break;
+  
   if (sw->from != pd->state)
     {
 #if 0
@@ -283,33 +385,145 @@ startElement(void *userData, const char *name, const char **atts)
         }
       break;
     case STATE_PATCH:
+      if (sw->from == STATE_START)
+        {
+         if ((str = find_attr("timestamp", atts)))
+           {
+             pd->timestamp = strtoul(str, NULL, 10);
+           }
+        }
+      /*FALLTHRU*/
     case STATE_ATOM:
       if (pd->state == STATE_ATOM)
        {
          /* HACK: close patch */
          if (pd->kind && !strcmp(pd->kind, "patch"))
            {
-             s->repo = pd->repo;
              if (!s->arch)
                s->arch = ARCH_NOARCH;
              s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
-             pd->pack++;
            }
          pd->kind = "atom";
          pd->state = STATE_PATCH;
        }
       else
         pd->kind = "patch";
-      if ((pd->pack & PACK_BLOCK) == 0)
+      
+      pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
+      pd->freshens = 0;
+
+      if (!strcmp(pd->kind, "patch"))
         {
-          pool->solvables = realloc(pool->solvables, (pool->nsolvables + pd->pack + PACK_BLOCK + 1) * sizeof(Solvable));
-          pd->start = pool->solvables + pool->nsolvables;
-          memset(pd->start + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(Solvable));
-        }
+          pd->datanum = pd->solvable - pool->solvables;
+          repodata_set_num(pd->data, pd->datanum, SOLVABLE_BUILDTIME, pd->timestamp);
+       }
 #if 0
-      fprintf(stderr, "package #%d\n", pd->pack);
+      fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
 #endif
       break;
+    case STATE_DELTARPM:
+      memset(&pd->delta, 0, sizeof (pd->delta));
+      *pd->tempstr = 0;
+      pd->ltemp = 0;
+      break;
+    case STATE_DLOCATION:
+      if ((str = find_attr("href", atts)))
+        {
+         /* Separate the filename into its different parts.
+            rpm/x86_64/alsa-1.0.14-31_31.2.x86_64.delta.rpm
+            --> dir = rpm/x86_64
+                name = alsa
+                evr = 1.0.14-31_31.2
+                suffix = x86_64.delta.rpm.  */
+          char *real_str = strdup(str);
+         char *s = real_str;
+          char *s1, *s2;
+         s1 = strrchr (s, '/');
+         if (s1)
+           {
+             pd->delta.locdir = strn2id(pool, s, s1 - s, 1);
+             s = s1 + 1;
+           }
+         /* Guess suffix.  */
+         s1 = strrchr (s, '.');
+         if (s1)
+           {
+             for (s2 = s1 - 1; s2 > s; s2--)
+               if (*s2 == '.')
+                 break;
+             if (!strcmp (s2, ".delta.rpm") || !strcmp (s2, ".patch.rpm"))
+               {
+                 s1 = s2;
+                 /* We accept one more item as suffix.  */
+                 for (s2 = s1 - 1; s2 > s; s2--)
+                   if (*s2 == '.')
+                     break;
+                 s1 = s2;
+               }
+             if (*s1 == '.')
+               *s1++ = 0;
+             pd->delta.locsuffix = str2id(pool, s1, 1); 
+           }
+         /* Last '-'.  */
+         s1 = strrchr (s, '-');
+         if (s1)
+           {
+             /* Second to last '-'.  */
+             for (s2 = s1 - 1; s2 > s; s2--)
+               if (*s2 == '-')
+                 break;
+           }
+         else
+           s2 = 0;
+         if (s2 > s && *s2 == '-')
+           {
+             *s2++ = 0;
+             pd->delta.locevr = str2id(pool, s2, 1);
+           }
+         pd->delta.locname = str2id(pool, s, 1);
+         free(real_str);
+        }
+      break;
+    case STATE_DTIME:
+      str = find_attr("build", atts);
+      if (str)
+       pd->delta.buildtime = atoi(str);
+      break;
+    case STATE_DSIZE:
+      if ((str = find_attr("package", atts)))
+       pd->delta.downloadsize = atoi(str);
+      if ((str = find_attr("archive", atts)))
+       pd->delta.archivesize = atoi(str);
+      break;
+    case STATE_DBASEVERSION:
+      if ((str = find_attr("sequence_info", atts)))
+       {
+         const char *s1, *s2;
+         s1 = strrchr(str, '-');
+         if (s1)
+           {
+             for (s2 = s1 - 1; s2 > str; s2--)
+               if (*s2 == '-')
+                 break;
+             if (*s2 == '-')
+               {
+                 for (s2 = s2 - 1; s2 > str; s2--)
+                   if (*s2 == '-')
+                     break;
+                 if (*s2 == '-')
+                   {
+                     pd->delta.seqevr = strn2id(pool, s2 + 1, s1 - s2 - 1, 1);
+                     pd->delta.seqname = strn2id(pool, str, s2 - str, 1);
+                     str = s1 + 1;
+                   }
+               }
+           }
+         pd->delta.seqnum = strdup(str);
+       }
+      pd->delta.nbevr++;
+      pd->delta.bevr = sat_realloc (pd->delta.bevr, pd->delta.nbevr * sizeof(Id));
+      pd->delta.bevr[pd->delta.nbevr - 1] = makeevr_atts(pool, pd, atts);
+      break;
     case STATE_VERSION:
       s->evr = makeevr_atts(pool, pd, atts);
       break;
@@ -362,11 +576,17 @@ startElement(void *userData, const char *name, const char **atts)
       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
       break;
     case STATE_FRESHENS:
-      s->freshens = 0;
+      pd->freshens = 0;
       break;
     case STATE_FRESHENSENTRY:
-      s->freshens = adddep(pool, pd, s->freshens, atts, 0);
+      pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
       break;
+    case STATE_REBOOT:
+      repodata_set_void(pd->data, pd->datanum, UPDATE_REBOOT);
+      break;  
+    case STATE_RESTART:
+      repodata_set_void(pd->data, pd->datanum, UPDATE_RESTART);
+      break;  
     default:
       break;
     }
@@ -377,7 +597,7 @@ endElement(void *userData, const char *name)
 {
   struct parsedata *pd = userData;
   Pool *pool = pd->pool;
-  Solvable *s = pd->start ? pd->start + pd->pack : 0;
+  Solvable *s = pd->solvable;
 
   if (pd->depth != pd->statedepth)
     {
@@ -396,13 +616,13 @@ endElement(void *userData, const char *name)
     case STATE_PATCH:
       if (!strcmp(name, "patch") && strcmp(pd->kind, "patch"))
        break;  /* already closed */
-      s->repo = pd->repo;
       if (!s->arch)
        s->arch = ARCH_NOARCH;
       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
         s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
-      s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
-      pd->pack++;
+      s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
+      s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
+      pd->freshens = 0;
       break;
     case STATE_NAME:
       s->name = str2id(pool, pd->content, 1);
@@ -410,6 +630,56 @@ endElement(void *userData, const char *name)
     case STATE_ARCH:
       s->arch = str2id(pool, pd->content, 1);
       break;
+    case STATE_SUMMARY:
+      repodata_set_str(pd->data, pd->datanum, SOLVABLE_SUMMARY, pd->content);
+      break;
+    case STATE_DESCRIPTION:
+      repodata_set_str(pd->data, pd->datanum, SOLVABLE_DESCRIPTION, pd->content);
+      break;
+    case STATE_CATEGORY:  
+      repodata_set_str(pd->data, pd->datanum, SOLVABLE_PATCHCATEGORY, pd->content);
+      break;
+    case STATE_DELTARPM:
+#ifdef TESTMM
+      {
+       int i;
+        struct deltarpm *d = &pd->delta;
+       fprintf (stderr, "found deltarpm for %s:\n", id2str(pool, s->name));
+       fprintf (stderr, "   loc: %s %s %s %s\n", id2str(pool, d->locdir),
+                id2str(pool, d->locname), id2str(pool, d->locevr),
+                id2str(pool, d->locsuffix));
+       fprintf (stderr, "  time: %u\n", d->buildtime);
+       fprintf (stderr, "  size: %d down, %d archive\n", d->downloadsize,
+                d->archivesize);
+       fprintf (stderr, "  chek: %s\n", d->filechecksum);
+       if (d->seqnum)
+         {
+           fprintf (stderr, "  base: %s, seq: %s %s %s\n",
+                    id2str(pool, d->bevr[0]), id2str(pool, d->seqname),
+                    id2str(pool, d->seqevr), d->seqnum);
+           if (d->seqevr != d->bevr[0])
+             fprintf (stderr, "XXXXX evr\n");
+           /* Name of package ("atom:xxxx") should match the sequence info
+              name.  */
+           if (strcmp(id2str(pool, d->seqname), id2str(pool, s->name) + 5))
+             fprintf (stderr, "XXXXX name\n");
+         }
+       else
+         {
+           fprintf (stderr, "  base:");
+           for (i = 0; i < d->nbevr; i++)
+             fprintf (stderr, " %s", id2str(pool, d->bevr[i]));
+           fprintf (stderr, "\n");
+         }
+      }
+#endif
+      free(pd->delta.filechecksum);
+      free(pd->delta.bevr);
+      free(pd->delta.seqnum);
+      break;
+    case STATE_DCHECKSUM:
+      pd->delta.filechecksum = strdup(pd->content);
+      break;
     default:
       break;
     }
@@ -444,19 +714,19 @@ characterData(void *userData, const XML_Char *s, int len)
 #define BUFF_SIZE 8192
 
 void
-repo_add_patchxml(Repo *repo, FILE *fp)
+repo_add_patchxml(Repo *repo, FILE *fp, int flags)
 {
   Pool *pool = repo->pool;
   struct parsedata pd;
   char buf[BUFF_SIZE];
   int i, l;
   struct stateswitch *sw;
+  Repodata *data;
 
-  if (repo->start && repo->start + repo->nsolvables != pool->nsolvables)
-    abort();
-  if (!repo->start || repo->start == repo->end)
-    repo->start = pool->nsolvables;
-  repo->end = pool->nsolvables;
+  if (!(flags & REPO_REUSE_REPODATA))
+    data = repo_add_repodata(repo, 0);
+  else
+    data = repo_last_repodata(repo);
 
   memset(&pd, 0, sizeof(pd));
   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
@@ -467,9 +737,14 @@ repo_add_patchxml(Repo *repo, FILE *fp)
     }
   pd.pool = pool;
   pd.repo = repo;
+  pd.data = data;
+
   pd.content = malloc(256);
   pd.acontent = 256;
   pd.lcontent = 0;
+  pd.tempstr = malloc(256);
+  pd.atemp = 256;
+  pd.ltemp = 0;
   XML_Parser parser = XML_ParserCreate(NULL);
   XML_SetUserData(parser, &pd);
   XML_SetElementHandler(parser, startElement, endElement);
@@ -479,7 +754,7 @@ repo_add_patchxml(Repo *repo, FILE *fp)
       l = fread(buf, 1, sizeof(buf), fp);
       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
        {
-         fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
+         pool_debug(pool, SAT_FATAL, "repo_patchxml: %s at line %u:%u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser), (unsigned int)XML_GetCurrentColumnNumber(parser));
          exit(1);
        }
       if (l == 0)
@@ -487,9 +762,8 @@ repo_add_patchxml(Repo *repo, FILE *fp)
     }
   XML_ParserFree(parser);
 
-  pool->nsolvables += pd.pack;
-  repo->nsolvables += pd.pack;
-  repo->end += pd.pack;
+  if (!(flags & REPO_NO_INTERNALIZE))
+    repodata_internalize(data);
 
   free(pd.content);
 }