use sed instead of grep to get rid of the <?xml...> line
[platform/upstream/libsolv.git] / ext / repo_susetags.c
index db4a590..440e2d5 100644 (file)
 #include "pool.h"
 #include "repo.h"
 #include "hash.h"
+#include "chksum.h"
 #include "tools_util.h"
 #include "repo_susetags.h"
 
+struct datashare {
+  Id name;
+  Id evr;
+  Id arch;
+};
+
 struct parsedata {
-  char *kind;
+  int ret;
+  Pool *pool;
   Repo *repo;
   Repodata *data;
-  struct parsedata_common common;
+  char *kind;
+  int flags;
   int last_found_source;
-  char **share_with;
+  struct datashare *share_with;
   int nshare;
-  Id (*dirs)[3]; // dirid, size, nfiles
+  Id (*dirs)[3];                       /* dirid, size, nfiles */
   int ndirs;
-  Id langcache[ID_NUM_INTERNAL];
+  struct joindata jd;
+  char *language;                      /* the default language */
+  Id langcache[ID_NUM_INTERNAL];       /* cache for the default language */
   int lineno;
+  char *filelist;
+  int afilelist;                       /* allocated */
+  int nfilelist;                       /* used */
 };
 
 static char *flagtab[] = {
@@ -45,13 +59,14 @@ static char *flagtab[] = {
 static Id
 langtag(struct parsedata *pd, Id tag, const char *language)
 {
-  if (language && !language[0])
-    language = 0;
-  if (!language || tag >= ID_NUM_INTERNAL)
+  if (language && *language)
     return pool_id2langid(pd->repo->pool, tag, language, 1);
-  return pool_id2langid(pd->repo->pool, tag, language, 1);
+  if (!pd->language)
+    return tag;
+  if (tag >= ID_NUM_INTERNAL)
+    return pool_id2langid(pd->repo->pool, tag, pd->language, 1);
   if (!pd->langcache[tag])
-    pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, language, 1);
+    pd->langcache[tag] = pool_id2langid(pd->repo->pool, tag, pd->language, 1);
   return pd->langcache[tag];
 }
 
@@ -70,20 +85,20 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id ma
   if (line[6] == '/')
     {
       /* A file dependency. Do not try to parse it */
-      id = str2id(pool, line + 6, 1);
+      id = pool_str2id(pool, line + 6, 1);
     }
   else
     {
       i = split(line + 6, sp, 4); /* name, <op>, evr, ? */
       if (i != 1 && i != 3) /* expect either 'name' or 'name' <op> 'evr' */
         {
-         pool_debug(pool, SAT_FATAL, "susetags: bad dependency line: %d: %s\n", pd->lineno, line);
-          exit(1);
+         pd->ret = pool_error(pool, -1, "susetags: line %d: bad dependency: '%s'\n", pd->lineno, line);
+          return olddeps;
         }
       if (kind)
-        id = str2id(pool, join2(kind, ":", sp[0]), 1);
+        id = pool_str2id(pool, join2(&pd->jd, kind, ":", sp[0]), 1);
       else
-        id = str2id(pool, sp[0], 1);
+        id = pool_str2id(pool, sp[0], 1);
       if (i == 3)
         {
           evrid = makeevr(pool, sp[2]);
@@ -92,10 +107,15 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id ma
               break;
           if (flags == 6)
             {
-             pool_debug(pool, SAT_FATAL, "susetags: unknown relation in %d: '%s'\n", pd->lineno, sp[1]);
-              exit(1);
+             if (!strcmp(sp[1], "<>"))
+               flags = 4;
+             else
+               {
+                 pd->ret = pool_error(pool, -1, "susetags: line %d: unknown relation: '%s'\n", pd->lineno, sp[1]);
+                 return olddeps;
+               }
             }
-          id = rel2id(pool, id, evrid, flags + 1, 1);
+          id = pool_rel2id(pool, id, evrid, flags + 1, 1);
         }
     }
   return repo_addid_dep(pd->repo, olddeps, id, marker);
@@ -110,28 +130,39 @@ adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id ma
 static void
 add_source(struct parsedata *pd, char *line, Solvable *s, Id handle)
 {
-  Repo *repo = s->repo;
-  Pool *pool = repo->pool;
+  Pool *pool = s->repo->pool;
   char *sp[5];
+  Id name;
+  Id arch;
+  const char *evr, *sevr;
 
   if (split(line, sp, 5) != 4)
     {
-      pool_debug(pool, SAT_FATAL, "susetags: bad source line: %d: %s\n", pd->lineno, line);
-      exit(1);
+      pd->ret = pool_error(pool, -1, "susetags: line %d: bad source line '%s'\n", pd->lineno, line);
+      return;
     }
 
-  Id name = str2id(pool, sp[0], 1);
-  Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
-  Id arch = str2id(pool, sp[3], 1);
-  /* XXX: could record a dep here, depends on where we want to store the data */
+  name = pool_str2id(pool, sp[0], 1);
+  arch = pool_str2id(pool, sp[3], 1);  /* do this before id2str */
+  evr = join2(&pd->jd, sp[1], "-", sp[2]);
+  sevr = pool_id2str(pool, s->evr);
+  if (sevr)
+    {
+      /* strip epoch */
+      const char *p;
+      for (p = sevr; *p >= '0' && *p <= '9'; p++)
+       ;
+      if (p != sevr && *p == ':' && p[1])
+       sevr = p;
+    }
   if (name == s->name)
     repodata_set_void(pd->data, handle, SOLVABLE_SOURCENAME);
   else
     repodata_set_id(pd->data, handle, SOLVABLE_SOURCENAME, name);
-  if (evr == s->evr)
+  if (sevr && !strcmp(sevr, evr))
     repodata_set_void(pd->data, handle, SOLVABLE_SOURCEEVR);
   else
-    repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, evr);
+    repodata_set_id(pd->data, handle, SOLVABLE_SOURCEEVR, pool_str2id(pool, evr, 1));
   repodata_set_constantid(pd->data, handle, SOLVABLE_SOURCEARCH, arch);
 }
 
@@ -145,17 +176,20 @@ static void
 add_dirline(struct parsedata *pd, char *line)
 {
   char *sp[6];
+  long filesz;
+  long filenum;
+  Id dirid;
   if (split(line, sp, 6) != 5)
     return;
-  pd->dirs = sat_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
-  long filesz = strtol(sp[1], 0, 0);
+  pd->dirs = solv_extend(pd->dirs, pd->ndirs, 1, sizeof(pd->dirs[0]), 31);
+  filesz = strtol(sp[1], 0, 0);
   filesz += strtol(sp[2], 0, 0);
-  long filenum = strtol(sp[3], 0, 0);
+  filenum = strtol(sp[3], 0, 0);
   filenum += strtol(sp[4], 0, 0);
   /* hack: we know that there's room for a / */
   if (*sp[0] != '/')
     *--sp[0] = '/';
-  unsigned dirid = repodata_str2dir(pd->data, sp[0], 1);
+  dirid = repodata_str2dir(pd->data, sp[0], 1);
 #if 0
 fprintf(stderr, "%s -> %d\n", sp[0], dirid);
 #endif
@@ -169,32 +203,27 @@ static void
 set_checksum(struct parsedata *pd, Repodata *data, Id handle, Id keyname, char *line)
 {
   char *sp[3];
-  int l;
   Id type;
   if (split(line, sp, 3) != 2)
     {
-      pool_debug(pd->repo->pool, SAT_FATAL, "susetags: bad checksum line: %d: %s\n", pd->lineno, line);
-      exit(1);
+      pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum line '%s'\n", pd->lineno, line);
+      return;
     }
-  if (!strcasecmp(sp[0], "sha1"))
-    l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
-  else if (!strcasecmp(sp[0], "sha256"))
-    l = SIZEOF_SHA256 * 2, type = REPOKEY_TYPE_SHA256;
-  else if (!strcasecmp(sp[0], "md5"))
-    l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
-  else
+  type = solv_chksum_str2type(sp[0]);
+  if (!type)
     {
-      pool_debug(pd->repo->pool, SAT_FATAL, "susetags: unknown checksum type: %d: %s\n", pd->lineno, sp[0]);
-      exit(1);
+      pd->ret = pool_error(pd->pool, -1, "susetags: line %d: unknown checksum type: '%s'\n", pd->lineno, sp[0]);
+      return;
     }
-  if (strlen(sp[1]) != l)
+  if (strlen(sp[1]) != 2 * solv_chksum_len(type))
     {
-      pool_debug(pd->repo->pool, SAT_FATAL, "susetags: bad checksum length: %d: for %s: %s\n", pd->lineno, sp[0], sp[1]);
-      exit(1);
+      pd->ret = pool_error(pd->pool, -1, "susetags: line %d: bad checksum length for type %s: '%s'\n", pd->lineno, sp[0], sp[1]);
+      return;
     }
   repodata_set_checksum(data, handle, keyname, type, sp[1]);
 }
 
+
 /*
  * id3_cmp
  * compare
@@ -218,26 +247,26 @@ id3_cmp(const void *v1, const void *v2, void *dp)
 static void
 commit_diskusage(struct parsedata *pd, Id handle)
 {
-  unsigned i;
+  int i;
   Dirpool *dp = &pd->data->dirpool;
   /* Now sort in dirid order.  This ensures that parents come before
      their children.  */
   if (pd->ndirs > 1)
-    sat_sort(pd->dirs, pd->ndirs, sizeof(pd->dirs[0]), id3_cmp, 0);
+    solv_sort(pd->dirs, pd->ndirs, sizeof(pd->dirs[0]), id3_cmp, 0);
   /* Substract leaf numbers from all parents to make the numbers
      non-cumulative.  This must be done post-order (i.e. all leafs
      adjusted before parents).  We ensure this by starting at the end of
      the array moving to the start, hence seeing leafs before parents.  */
   for (i = pd->ndirs; i--;)
     {
-      unsigned p = dirpool_parent(dp, pd->dirs[i][0]);
-      unsigned j = i;
+      Id p = dirpool_parent(dp, pd->dirs[i][0]);
+      int j = i;
       for (; p; p = dirpool_parent(dp, p))
         {
           for (; j--;)
            if (pd->dirs[j][0] == p)
              break;
-         if (j < pd->ndirs)
+         if (j >= 0)
            {
              if (pd->dirs[j][1] < pd->dirs[i][1])
                pd->dirs[j][1] = 0;
@@ -319,74 +348,34 @@ tag_from_string(char *cs)
  */
 
 static void
-finish_solvable(struct parsedata *pd, Solvable *s, Id handle, Offset freshens)
+finish_solvable(struct parsedata *pd, Solvable *s, Offset freshens)
 {
   Pool *pool = pd->repo->pool;
+  Id handle = s - pool->solvables;
 
-#if 1
-  /* move file provides to filelist */
-  /* relies on the fact that rpm inserts self-provides at the end */
-  if (s->provides)
+  if (pd->nfilelist)
     {
-      Id *p, *lastreal, did;
-      const char *str, *sp;
-      lastreal = pd->repo->idarraydata + s->provides;
-      for (p = lastreal; *p; p++)
-       if (ISRELDEP(*p))
-         lastreal = p + 1;
-      for (p = lastreal; *p; p++)
-       {
-         str = id2str(pool, *p);
-         if (*str != '/')
-           lastreal = p + 1;
-       }
-      if (*lastreal)
-       {
-         for (p = lastreal; *p; p++)
-           {
-             char fname_buf[128];
-             const char *fname;
-             str = id2str(pool, *p);
-             sp = strrchr(str, '/');
-             /* Need to copy filename now, before we add string that could
-                realloc the stringspace (and hence invalidate str).  */
-             fname = sp + 1;
-             if (strlen(fname) >= 128)
-               fname = strdup(fname);
-             else
-               {
-                 memcpy(fname_buf, fname, strlen(fname) + 1);
-                 fname = fname_buf;
-               }
-             if (sp - str >= 128)
-               {
-                 char *sdup = strdup(str);
-                 sdup[sp - str] = 0;
-                 did = repodata_str2dir(pd->data, sdup, 1);
-                 free(sdup);
-               }
-             else
-               {
-                 char sdup[128];
-                 strncpy(sdup, str, sp - str);
-                 sdup[sp - str] = 0;
-                 did = repodata_str2dir(pd->data, sdup, 1);
-               }
-             if (!did)
-               did = repodata_str2dir(pd->data, "/", 1);
-             repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, fname);
-             if (fname != fname_buf)
-               free((char*)fname);
-             *p = 0;
-           }
+      int l;
+      Id did;
+      for (l = 0; l < pd->nfilelist; l += strlen(pd->filelist + l) + 1)
+        {
+         char *p = strrchr(pd->filelist + l, '/');
+         if (!p)
+           continue;
+         *p++ = 0;
+         did = repodata_str2dir(pd->data, pd->filelist + l, 1);
+         p[-1] = '/';
+         if (!did)
+           did = repodata_str2dir(pd->data, "/", 1);
+         repodata_add_dirstr(pd->data, handle, SOLVABLE_FILELIST, did, p);
        }
+      pd->nfilelist = 0;
     }
-#endif
   /* A self provide, except for source packages.  This is harmless
      to do twice (in case we see the same package twice).  */
   if (s->name && 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);
+               pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
   /* XXX This uses repo_addid_dep internally, so should also be
      harmless to do twice.  */
   s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, freshens);
@@ -396,10 +385,10 @@ finish_solvable(struct parsedata *pd, Solvable *s, Id handle, Offset freshens)
 }
 
 static Hashtable
-joinhash_init(Repo *repo, Hashmask *hmp)
+joinhash_init(Repo *repo, Hashval *hmp)
 {
-  Hashmask hm = mkmask(repo->nsolvables);
-  Hashtable ht = sat_calloc(hm + 1, sizeof(*ht));
+  Hashval hm = mkmask(repo->nsolvables);
+  Hashtable ht = solv_calloc(hm + 1, sizeof(*ht));
   Hashval h, hh;
   Solvable *s;
   int i;
@@ -417,7 +406,7 @@ joinhash_init(Repo *repo, Hashmask *hmp)
 }
 
 static Solvable *
-joinhash_lookup(Repo *repo, Hashtable ht, Hashmask hm, Id name, Id evr, Id arch)
+joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, Id name, Id evr, Id arch, Id start)
 {
   Hashval h, hh;
 
@@ -428,13 +417,38 @@ joinhash_lookup(Repo *repo, Hashtable ht, Hashmask hm, Id name, Id evr, Id arch)
   while (ht[h])
     {
       Solvable *s = repo->pool->solvables + ht[h];
-      if (s->name == name && s->evr == evr && s->arch == arch)
+      if (ht[h] >= start && s->name == name && s->evr == evr && s->arch == arch)
        return s;
       h = HASHCHAIN_NEXT(h, hh, hm);
     }
   return 0;
 }
 
+static Id
+lookup_shared_id(Repodata *data, Id p, Id keyname, Id voidid, int uninternalized)
+{
+  Id r;
+  r = repodata_lookup_type(data, p, keyname);
+  if (r)
+    {
+      if (r == REPOKEY_TYPE_VOID)
+       return voidid;
+      r = repodata_lookup_id(data, p, keyname);
+      if (r)
+       return r;
+    }
+  if (uninternalized)
+    return repodata_lookup_id_uninternalized(data, p, keyname, voidid);
+  return 0;
+}
+
+static inline Id
+toevr(Pool *pool, struct parsedata *pd, const char *version, const char *release)
+{
+  return makeevr(pool, !release || (release[0] == '-' && !release[1]) ?
+       version : join2(&pd->jd, version, "-", release));
+}
+
 
 /*
  * parse susetags
@@ -445,7 +459,7 @@ joinhash_lookup(Repo *repo, Hashtable ht, Hashmask hm, Id name, Id evr, Id arch)
  * flags: flags
  */
 
-void
+int
 repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int flags)
 {
   Pool *pool = repo->pool;
@@ -456,13 +470,16 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
   int intag = 0;
   int cummulate = 0;
   int indesc = 0;
+  int indelta = 0;
   int last_found_pack = 0;
+  Id first_new_pkg = 0;
   char *sp[5];
   struct parsedata pd;
   Repodata *data = 0;
   Id handle = 0;
   Hashtable joinhash = 0;
-  Hashmask joinhashm = 0;
+  Hashval joinhashm = 0;
+  int createdpkgs = 0;
 
   if ((flags & (SUSETAGS_EXTEND|REPO_EXTEND_SOLVABLES)) != 0 && repo->nrepodata)
     {
@@ -473,17 +490,55 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
   data = repo_add_repodata(repo, flags);
 
   memset(&pd, 0, sizeof(pd));
-  line = malloc(1024);
+  line = solv_malloc(1024);
   aline = 1024;
 
-  pd.repo = pd.common.repo = repo;
+  pd.pool = pool;
+  pd.repo = repo;
   pd.data = data;
-  pd.common.pool = pool;
+  pd.flags = flags;
+  pd.language = language && *language ? solv_strdup(language) : 0;
 
   linep = line;
   s = 0;
   freshens = 0;
 
+  /* if this is a join setup the recorded share data */
+  if (joinhash)
+    {
+      Repodata *sdata;
+      int i;
+
+      FOR_REPODATAS(repo, i, sdata)
+       {
+         int p;
+         if (!repodata_has_keyname(sdata, SUSETAGS_SHARE_NAME))
+           continue;
+         for (p = sdata->start; p < sdata->end; p++)
+           {
+             Id name, evr, arch;
+             name = lookup_shared_id(sdata, p, SUSETAGS_SHARE_NAME, pool->solvables[p].name, sdata == data);
+             if (!name)
+               continue;
+             evr = lookup_shared_id(sdata, p, SUSETAGS_SHARE_EVR, pool->solvables[p].evr, sdata == data);
+             if (!evr)
+               continue;
+             arch = lookup_shared_id(sdata, p, SUSETAGS_SHARE_ARCH, pool->solvables[p].arch, sdata == data);
+             if (!arch)
+               continue;
+             if (p - repo->start >= pd.nshare)
+               {
+                 pd.share_with = solv_realloc2(pd.share_with, p - repo->start + 256, sizeof(*pd.share_with));
+                 memset(pd.share_with + pd.nshare, 0, (p - repo->start + 256 - pd.nshare) * sizeof(*pd.share_with));
+                 pd.nshare = p - repo->start + 256;
+               }
+             pd.share_with[p - repo->start].name = name;
+             pd.share_with[p - repo->start].evr = evr;
+             pd.share_with[p - repo->start].arch = arch;
+           }
+       }
+    }
+
   /*
    * read complete file
    *
@@ -497,10 +552,13 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
       char *olinep; /* old line pointer */
       char line_lang[6];
       int keylen = 3;
+
+      if (pd.ret)
+       break;
       if (linep - line + 16 > aline)              /* (re-)alloc buffer */
        {
          aline = linep - line;
-         line = realloc(line, aline + 512);
+         line = solv_realloc(line, aline + 512);
          linep = line + aline;
          aline += 512;
        }
@@ -525,7 +583,7 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
          if (is_end
              && strncmp(linep - 1 - intag, line + 1, intag))
            {
-             pool_debug(pool, SAT_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
+             pool_debug(pool, SOLV_ERROR, "susetags: Nonmatching multi-line tags: %d: '%s' '%s' %d\n", pd.lineno, linep - 1 - intag, line + 1, intag);
            }
          if (cummulate && !is_end)
            {
@@ -557,25 +615,23 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
          char *tagend = strchr(line, ':');
          if (!tagend)
            {
-             pool_debug(pool, SAT_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
-             exit(1);
+             pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+             break;
            }
          intag = tagend - (line + 1);
          cummulate = 0;
          switch (tag_from_string(line))       /* check if accumulation is needed */
            {
-             case CTAG('+', 'P', 'r', 'q'):
-             case CTAG('+', 'P', 'r', 'c'):
-             case CTAG('+', 'P', 's', 'g'):
-               if (!pd.kind || !(flags & SUSETAGS_KINDS_SEPARATELY))
-                 break;
              case CTAG('+', 'D', 'e', 's'):
              case CTAG('+', 'E', 'u', 'l'):
              case CTAG('+', 'I', 'n', 's'):
              case CTAG('+', 'D', 'e', 'l'):
              case CTAG('+', 'A', 'u', 't'):
-               if (line[4] == ':')
+               if (line[4] == ':' || line[4] == '.')
                  cummulate = 1;
+               break;
+             default:
+               break;
            }
          line[0] = '=';                       /* handle lines between +Key:/-Key: as =Key: */
          line[intag + keylen - 1] = ' ';
@@ -586,36 +642,109 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
        continue;
       if (! (line[0] && line[1] && line[2] && line[3] && (line[4] == ':' || line[4] == '.')))
         continue;
+      line_lang[0] = 0;
       if (line[4] == '.')
         {
           char *endlang;
           endlang = strchr(line + 5, ':');
           if (endlang)
             {
+             int langsize = endlang - (line + 5);
               keylen = endlang - line - 1;
-              strncpy(line_lang, line + 5, 5);
-              line_lang[endlang - line - 5] = 0;
+             if (langsize > 5)
+               langsize = 5;
+              strncpy(line_lang, line + 5, langsize);
+              line_lang[langsize] = 0;
             }
         }
-      else
-        line_lang[0] = 0;
       tag = tag_from_string(line);
 
+      if (indelta)
+       {
+         /* Example:
+           =Dlt: subversion 1.6.16 1.3.1 i586
+           =Dsq: subversion 1.6.15 4.2 i586 d57b3fc86e7a2f73796e8e35b96fa86212c910
+           =Cks: SHA1 14a8410cf741856a5d70d89dab62984dba6a1ca7
+           =Loc: 1 subversion-1.6.15_1.6.16-4.2_1.3.1.i586.delta.rpm
+           =Siz: 81558
+          */
+         switch (tag)
+           {
+           case CTAG('=', 'D', 's', 'q'):
+             {
+               Id evr;
+               if (split(line + 5, sp, 5) != 5)
+                 continue;
+               repodata_set_id(data, handle, DELTA_SEQ_NAME, pool_str2id(pool, sp[0], 1));
+               evr = toevr(pool, &pd, sp[1], sp[2]);
+               repodata_set_id(data, handle, DELTA_SEQ_EVR, evr);
+               /* repodata_set_id(data, handle, DELTA_SEQ_ARCH, pool_str2id(pool, sp[3], 1)); */
+               repodata_set_str(data, handle, DELTA_SEQ_NUM, sp[4]);
+               repodata_set_id(data, handle, DELTA_BASE_EVR, evr);
+               continue;
+             }
+           case CTAG('=', 'C', 'k', 's'):
+             set_checksum(&pd, data, handle, DELTA_CHECKSUM, line + 6);
+             continue;
+           case CTAG('=', 'L', 'o', 'c'):
+             {
+               int i = split(line + 6, sp, 3);
+               if (i != 2 && i != 3)
+                 {
+                   pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
+                   continue;
+                 }
+               repodata_set_deltalocation(data, handle, atoi(sp[0]), i == 3 ? sp[2] : 0, sp[1]);
+               continue;
+             }
+           case CTAG('=', 'S', 'i', 'z'):
+             if (split(line + 6, sp, 3) == 2)
+               repodata_set_num(data, handle, DELTA_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
+             continue;
+           case CTAG('=', 'P', 'k', 'g'):
+           case CTAG('=', 'P', 'a', 't'):
+           case CTAG('=', 'D', 'l', 't'):
+             handle = 0;
+             indelta = 0;
+             break;
+           default:
+             pool_debug(pool, SOLV_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
+             continue;
+           }
+       }
 
       /*
-       * start of (next) package or pattern
+       * start of (next) package or pattern or delta
        *
        * =Pkg: <name> <version> <release> <architecture>
        * (=Pat: ...)
        */
-
-      if ((tag == CTAG('=', 'P', 'k', 'g')
-          || tag == CTAG('=', 'P', 'a', 't')))
+      if (tag == CTAG('=', 'D', 'l', 't'))
+       {
+         if (s)
+           finish_solvable(&pd, s, freshens);
+         s = 0;
+         pd.kind = 0;
+          if (split(line + 5, sp, 5) != 4)
+           {
+             pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+             break;
+           }
+         handle = repodata_new_handle(data);
+         repodata_set_id(data, handle, DELTA_PACKAGE_NAME, pool_str2id(pool, sp[0], 1));
+         repodata_set_id(data, handle, DELTA_PACKAGE_EVR, toevr(pool, &pd, sp[1], sp[2]));
+         repodata_set_id(data, handle, DELTA_PACKAGE_ARCH, pool_str2id(pool, sp[3], 1));
+         repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DELTAINFO, handle);
+         indelta = 1;
+         continue;
+       }
+      if (tag == CTAG('=', 'P', 'k', 'g')
+          || tag == CTAG('=', 'P', 'a', 't'))
        {
          /* If we have an old solvable, complete it by filling in some
             default stuff.  */
          if (s)
-           finish_solvable(&pd, s, handle, freshens);
+           finish_solvable(&pd, s, freshens);
 
          /*
           * define kind
@@ -631,8 +760,8 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
 
           if (split(line + 5, sp, 5) != 4)
            {
-             pool_debug(pool, SAT_FATAL, "susetags: bad line: %d: %s\n", pd.lineno, line);
-             exit(1);
+             pd.ret = pool_error(pool, -1, "susetags: line %d: bad line '%s'\n", pd.lineno, line);
+             break;
            }
          s = 0;
           freshens = 0;
@@ -644,22 +773,24 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
              Id name, evr, arch;
              /* we don't use the create flag here as a simple pre-check for existance */
              if (pd.kind)
-               name = str2id(pool, join2(pd.kind, ":", sp[0]), 0);
+               name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 0);
              else
-               name = str2id(pool, sp[0], 0);
-             evr = makeevr(pool, join2(sp[1], "-", sp[2]));
-             arch = str2id(pool, sp[3], 0);
+               name = pool_str2id(pool, sp[0], 0);
+             evr = toevr(pool, &pd, sp[1], sp[2]);
+             arch = pool_str2id(pool, sp[3], 0);
              if (name && arch)
                {
-                 if (repo->start + last_found_pack + 1 < repo->end)
+                 Id start = (flags & REPO_EXTEND_SOLVABLES) ? 0 : first_new_pkg;
+                 if (repo->start + last_found_pack + 1 >= start && repo->start + last_found_pack + 1 < repo->end)
                    {
                      s = pool->solvables + repo->start + last_found_pack + 1;
                      if (s->repo != repo || s->name != name || s->evr != evr || s->arch != arch)
                        s = 0;
                    }
                  if (!s)
-                   s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch);
+                   s = joinhash_lookup(repo, joinhash, joinhashm, name, evr, arch, start);
                }
+             /* do not create new packages in EXTEND_SOLVABLES mode */
              if (!s && (flags & REPO_EXTEND_SOLVABLES) != 0)
                continue;
              /* fallthrough to package creation */
@@ -669,14 +800,16 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
              /* normal operation. create a new solvable. */
              s = pool_id2solvable(pool, repo_add_solvable(repo));
              if (pd.kind)
-               s->name = str2id(pool, join2(pd.kind, ":", sp[0]), 1);
+               s->name = pool_str2id(pool, join2(&pd.jd, pd.kind, ":", sp[0]), 1);
              else
-               s->name = str2id(pool, sp[0], 1);
-             s->evr = makeevr(pool, join2(sp[1], "-", sp[2]));
-             s->arch = str2id(pool, sp[3], 1);
+               s->name = pool_str2id(pool, sp[0], 1);
+             s->evr = toevr(pool, &pd, sp[1], sp[2]);
+             s->arch = pool_str2id(pool, sp[3], 1);
              s->vendor = defvendor;
+             if (!first_new_pkg)
+               first_new_pkg = s - pool->solvables;
+             createdpkgs = 1;
            }
-         else
          last_found_pack = (s - pool->solvables) - repo->start;
          if (data)
            handle = s - pool->solvables;
@@ -688,13 +821,33 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
       if (indesc >= 2 && !s)
         {
 #if 0
-         pool_debug(pool, SAT_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
+         pool_debug(pool, SOLV_ERROR, "susetags: huh %d: %s?\n", pd.lineno, line);
 #endif
           continue;
        }
       switch (tag)
         {
          case CTAG('=', 'P', 'r', 'v'):                                        /* provides */
+           if (line[6] == '/')
+             {
+               /* probably a filelist entry. stash it away for now */
+               int l = strlen(line + 6) + 1;
+               if (pd.nfilelist + l > pd.afilelist)
+                 {
+                   pd.afilelist = pd.nfilelist + l + 512;
+                   pd.filelist = solv_realloc(pd.filelist, pd.afilelist);
+                 }
+               memcpy(pd.filelist + pd.nfilelist, line + 6, l);
+               pd.nfilelist += l;
+               break;
+             }
+           if (pd.nfilelist)
+             {
+               int l;
+               for (l = 0; l < pd.nfilelist; l += strlen(pd.filelist + l) + 1)
+                 s->provides = repo_addid_dep(pd.repo, s->provides, pool_str2id(pool, pd.filelist + l, 1), 0);
+               pd.nfilelist = 0;
+             }
            s->provides = adddep(pool, &pd, s->provides, line, 0, pd.kind);
            continue;
          case CTAG('=', 'R', 'e', 'q'):                                        /* requires */
@@ -703,10 +856,7 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
           case CTAG('=', 'P', 'r', 'q'):                                        /* pre-requires / packages required */
            if (pd.kind)
              {
-               if (flags & SUSETAGS_KINDS_SEPARATELY)
-                 repodata_set_poolstr(data, handle, str2id(pool, "solvable:must", 1), line + 6);
-               else
-                 s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
+               s->requires = adddep(pool, &pd, s->requires, line, 0, 0);           /* patterns: a required package */
              }
            else
              s->requires = adddep(pool, &pd, s->requires, line, SOLVABLE_PREREQMARKER, 0); /* package: pre-requires */
@@ -733,60 +883,39 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
            freshens = adddep(pool, &pd, freshens, line, 0, pd.kind);
            continue;
           case CTAG('=', 'P', 'r', 'c'):                                        /* packages recommended */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             repodata_set_poolstr(data, handle, str2id(pool, "solvable:should", 1), line + 6);
-           else
-             s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
+           s->recommends = adddep(pool, &pd, s->recommends, line, 0, 0);
            continue;
           case CTAG('=', 'P', 's', 'g'):                                        /* packages suggested */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             repodata_set_poolstr(data, handle, str2id(pool, "solvable:may", 1), line + 6);
-           else
-             s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
+           s->suggests = adddep(pool, &pd, s->suggests, line, 0, 0);
            continue;
           case CTAG('=', 'P', 'c', 'n'):                                        /* pattern: package conflicts */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             fprintf(stderr, "Unsupported: pattern -> package conflicts\n");
-           else
-             s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
+           s->conflicts = adddep(pool, &pd, s->conflicts, line, 0, 0);
            continue;
          case CTAG('=', 'P', 'o', 'b'):                                        /* pattern: package obsoletes */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             fprintf(stderr, "Unsupported: pattern -> package obsoletes\n");
-           else
-             s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
+           s->obsoletes = adddep(pool, &pd, s->obsoletes, line, 0, 0);
            continue;
           case CTAG('=', 'P', 'f', 'r'):                                        /* pattern: package freshens */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             fprintf(stderr, "Unsupported: pattern -> package freshens\n");
-           else
-             freshens = adddep(pool, &pd, freshens, line, 0, 0);
+           freshens = adddep(pool, &pd, freshens, line, 0, 0);
            continue;
           case CTAG('=', 'P', 's', 'p'):                                        /* pattern: package supplements */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             fprintf(stderr, "Unsupported: pattern -> package supplements\n");
-           else
-             s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
+           s->supplements = adddep(pool, &pd, s->supplements, line, 0, 0);
            continue;
           case CTAG('=', 'P', 'e', 'n'):                                        /* pattern: package enhances */
-           if (flags & SUSETAGS_KINDS_SEPARATELY)
-             fprintf(stderr, "Unsupported: pattern -> package enhances\n");
-           else
-             s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
+           s->enhances = adddep(pool, &pd, s->enhances, line, 0, 0);
            continue;
           case CTAG('=', 'V', 'e', 'r'):                                        /* - version - */
            last_found_pack = 0;
            handle = 0;
            indesc++;
-           if (indesc > 1)
+           if (createdpkgs)
              {
-               sat_free(joinhash);
-               repodata_internalize(data);
+               solv_free(joinhash);
                joinhash = joinhash_init(repo, &joinhashm);
+               createdpkgs = 0;
              }
            continue;
           case CTAG('=', 'V', 'n', 'd'):                                        /* vendor */
-            s->vendor = str2id(pool, line + 6, 1);
+            s->vendor = pool_str2id(pool, line + 6, 1);
             continue;
 
         /* From here it's the attribute tags.  */
@@ -801,10 +930,10 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
              int i = split(line + 6, sp, 3);
              if (i != 2 && i != 3)
                {
-                 pool_debug(pool, SAT_FATAL, "susetags: bad location line: %d: %s\n", pd.lineno, line);
-                 exit(1);
+                 pd.ret = pool_error(pool, -1, "susetags: line %d: bad location line '%s'\n", pd.lineno, line);
+                 continue;
                }
-             repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : id2str(pool, s->arch), sp[1]);
+             repodata_set_location(data, handle, atoi(sp[0]), i == 3 ? sp[2] : pool_id2str(pool, s->arch), sp[1]);
            }
            continue;
           case CTAG('=', 'S', 'r', 'c'):
@@ -813,8 +942,8 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
           case CTAG('=', 'S', 'i', 'z'):
            if (split(line + 6, sp, 3) == 2)
              {
-               repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)(atoi(sp[0]) + 1023) / 1024);
-               repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (unsigned int)(atoi(sp[1]) + 1023) / 1024);
+               repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(sp[0], 0, 10));
+               repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, strtoull(sp[1], 0, 10));
              }
            continue;
           case CTAG('=', 'T', 'i', 'm'):
@@ -831,38 +960,66 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
            repodata_set_str(data, handle, SOLVABLE_AUTHORS, line + 6);
            continue;
           case CTAG('=', 'S', 'u', 'm'):
-            repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, language ? language : line_lang), line + 3 + keylen );
+            repodata_set_str(data, handle, langtag(&pd, SOLVABLE_SUMMARY, line_lang), line + 3 + keylen);
             continue;
           case CTAG('=', 'D', 'e', 's'):
-           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, language ? language : line_lang), line + 3 + keylen );
+           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_DESCRIPTION, line_lang), line + 3 + keylen);
            continue;
           case CTAG('=', 'E', 'u', 'l'):
-           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, language), line + 6);
+           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_EULA, line_lang), line + 6);
            continue;
           case CTAG('=', 'I', 'n', 's'):
-           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, language), line + 6);
+           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEINS, line_lang), line + 6);
            continue;
           case CTAG('=', 'D', 'e', 'l'):
-           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, language), line + 6);
+           repodata_set_str(data, handle, langtag(&pd, SOLVABLE_MESSAGEDEL, line_lang), line + 6);
            continue;
           case CTAG('=', 'V', 'i', 's'):
            {
              /* Accept numbers and textual bools.  */
-             unsigned k;
+             int k;
              k = atoi(line + 6);
              if (k || !strcasecmp(line + 6, "true"))
-               repodata_set_void(data, handle, SOLVABLE_ISVISIBLE );
+               repodata_set_void(data, handle, SOLVABLE_ISVISIBLE);
            }
            continue;
           case CTAG('=', 'S', 'h', 'r'):
-           if (last_found_pack >= pd.nshare)
-             {
-               pd.share_with = sat_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
-               memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
-               pd.nshare = last_found_pack + 256;
-             }
-           pd.share_with[last_found_pack] = strdup(line + 6);
-           continue;
+           {
+             Id name, evr, arch;
+             if (split(line + 6, sp, 5) != 4)
+               {
+                 pd.ret = pool_error(pool, -1, "susetags: line %d: bad =Shr line '%s'\n", pd.lineno, line);
+                 continue;
+               }
+             name = pool_str2id(pool, sp[0], 1);
+             evr = toevr(pool, &pd, sp[1], sp[2]);
+             arch = pool_str2id(pool, sp[3], 1);
+             if (last_found_pack >= pd.nshare)
+               {
+                 pd.share_with = solv_realloc2(pd.share_with, last_found_pack + 256, sizeof(*pd.share_with));
+                 memset(pd.share_with + pd.nshare, 0, (last_found_pack + 256 - pd.nshare) * sizeof(*pd.share_with));
+                 pd.nshare = last_found_pack + 256;
+               }
+             pd.share_with[last_found_pack].name = name;
+             pd.share_with[last_found_pack].evr = evr;
+             pd.share_with[last_found_pack].arch = arch;
+             if ((flags & SUSETAGS_RECORD_SHARES) != 0)
+               {
+                 if (s->name == name)
+                   repodata_set_void(data, handle, SUSETAGS_SHARE_NAME);
+                 else
+                   repodata_set_id(data, handle, SUSETAGS_SHARE_NAME, name);
+                 if (s->evr == evr)
+                   repodata_set_void(data, handle, SUSETAGS_SHARE_EVR);
+                 else
+                   repodata_set_id(data, handle, SUSETAGS_SHARE_EVR, evr);
+                 if (s->arch == arch)
+                   repodata_set_void(data, handle, SUSETAGS_SHARE_ARCH);
+                 else
+                   repodata_set_id(data, handle, SUSETAGS_SHARE_ARCH, arch);
+               }
+             continue;
+           }
          case CTAG('=', 'D', 'i', 'r'):
            add_dirline(&pd, line + 6);
            continue;
@@ -878,22 +1035,26 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
            repodata_set_str(data, handle, SOLVABLE_ICON, line + 6);
            break;
          case CTAG('=', 'E', 'x', 't'):
-           repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2("pattern", ":", line + 6));
+           repodata_add_poolstr_array(data, handle, SOLVABLE_EXTENDS, join2(&pd.jd, "pattern", ":", line + 6));
            break;
          case CTAG('=', 'I', 'n', 'c'):
-           repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2("pattern", ":", line + 6));
+           repodata_add_poolstr_array(data, handle, SOLVABLE_INCLUDES, join2(&pd.jd, "pattern", ":", line + 6));
            break;
          case CTAG('=', 'C', 'k', 's'):
            set_checksum(&pd, data, handle, SOLVABLE_CHECKSUM, line + 6);
            break;
          case CTAG('=', 'L', 'a', 'n'):
-           language = strdup(line + 6);
+           pd.language = solv_free(pd.language);
+           memset(pd.langcache, 0, sizeof(pd.langcache));
+           if (line[6])
+             pd.language = solv_strdup(line + 6);
            break;
 
          case CTAG('=', 'F', 'l', 's'):
            {
              char *p = strrchr(line + 6, '/');
              Id did;
+             /* strip trailing slash */
              if (p && p != line + 6 && !p[1])
                {
                  *p = 0;
@@ -930,14 +1091,17 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
            break;
 
          default:
-           pool_debug(pool, SAT_ERROR, "susetags: unknown line: %d: %s\n", pd.lineno, line);
+#if 0
+           pool_debug(pool, SOLV_WARN, "susetags: unknown line: %d: %s\n", pd.lineno, line);
+#endif
            break;
        }
 
-    } /* for(;;) */
+    }
 
   if (s)
-    finish_solvable(&pd, s, handle, freshens);
+    finish_solvable(&pd, s, freshens);
+  solv_free(pd.filelist);
 
   /* Shared attributes
    *  (e.g. multiple binaries built from same source)
@@ -945,49 +1109,61 @@ repo_add_susetags(Repo *repo, FILE *fp, Id defvendor, const char *language, int
   if (pd.nshare)
     {
       int i, last_found;
+      Map keyidmap;
+
+      map_init(&keyidmap, data->nkeys);
+      for (i = 1; i < data->nkeys; i++)
+       {
+         Id keyname = data->keys[i].name;
+         if (keyname == SOLVABLE_INSTALLSIZE || keyname == SOLVABLE_DISKUSAGE || keyname == SOLVABLE_FILELIST)
+           continue;
+         if (keyname == SOLVABLE_MEDIADIR || keyname == SOLVABLE_MEDIAFILE || keyname == SOLVABLE_MEDIANR)
+           continue;
+         if (keyname == SOLVABLE_DOWNLOADSIZE || keyname == SOLVABLE_CHECKSUM)
+           continue;
+         if (keyname == SOLVABLE_SOURCENAME || keyname == SOLVABLE_SOURCEARCH || keyname == SOLVABLE_SOURCEEVR)
+           continue;
+         if (keyname == SOLVABLE_PKGID || keyname == SOLVABLE_HDRID || keyname == SOLVABLE_LEADSIGID)
+           continue;
+         if (keyname == SUSETAGS_SHARE_NAME || keyname == SUSETAGS_SHARE_EVR || keyname == SUSETAGS_SHARE_ARCH)
+           continue;
+         MAPSET(&keyidmap, i);
+       }
       last_found = 0;
       for (i = 0; i < pd.nshare; i++)
-        if (pd.share_with[i])
-         {
-           if (split(pd.share_with[i], sp, 5) != 4)
-             {
-               pool_debug(pool, SAT_FATAL, "susetags: bad =Shr line: %s\n", pd.share_with[i]);
-               exit(1);
-             }
-
-           Id name = str2id(pool, sp[0], 1);
-           Id evr = makeevr(pool, join2(sp[1], "-", sp[2]));
-           Id arch = str2id(pool, sp[3], 1);
-           unsigned n, nn;
-           Solvable *found = 0;
-           for (n = repo->start, nn = repo->start + last_found;
-                n < repo->end; n++, nn++)
-             {
-               if (nn >= repo->end)
-                 nn = repo->start;
-               found = pool->solvables + nn;
-               if (found->repo == repo
-                   && found->name == name
-                   && found->evr == evr
-                   && found->arch == arch)
-                 {
-                   last_found = nn - repo->start;
-                   break;
-                 }
-             }
-           if (n != repo->end)
-             repodata_merge_attrs(data, repo->start + i, repo->start + last_found);
-           free(pd.share_with[i]);
-         }
+       {
+         unsigned int n, nn;
+         Solvable *found = 0;
+          if (!pd.share_with[i].name)
+           continue;
+         for (n = repo->start, nn = repo->start + last_found; n < repo->end; n++, nn++)
+           {
+             if (nn >= repo->end)
+               nn = repo->start;
+             found = pool->solvables + nn;
+             if (found->repo == repo
+                 && found->name == pd.share_with[i].name
+                 && found->evr == pd.share_with[i].evr
+                 && found->arch == pd.share_with[i].arch)
+               {
+                 last_found = nn - repo->start;
+                 break;
+               }
+           }
+         if (n != repo->end)
+           repodata_merge_some_attrs(data, repo->start + i, repo->start + last_found, &keyidmap, 0);
+        }
       free(pd.share_with);
+      map_free(&keyidmap);
     }
 
-  sat_free(joinhash);
+  solv_free(joinhash);
+  repodata_free_dircache(data);
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
 
-  if (pd.common.tmp)
-    free(pd.common.tmp);
-  free(line);
-  join_freemem();
+  solv_free(pd.language);
+  solv_free(line);
+  join_freemem(&pd.jd);
+  return pd.ret;
 }