]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Move tar handling from repo_arch into its own file
authorMichael Schroeder <mls@suse.de>
Thu, 28 Nov 2024 12:08:39 +0000 (13:08 +0100)
committerMichael Schroeder <mls@suse.de>
Thu, 28 Nov 2024 12:08:39 +0000 (13:08 +0100)
We'll need it for apk packages.

ext/CMakeLists.txt
ext/repo_arch.c
ext/tarhead.c [new file with mode: 0644]
ext/tarhead.h [new file with mode: 0644]

index 589fa9469f9bf1c67d93e107a3472e9c88d0d882..2cf62423aa56127f59d25c8b2a324a859409367b 100644 (file)
@@ -144,6 +144,11 @@ IF (ENABLE_ZCHUNK_COMPRESSION)
        solv_zchunk.c)
 ENDIF (ENABLE_ZCHUNK_COMPRESSION)
 
+IF (ENABLE_ARCHREPO)
+    SET (libsolvext_SRCS ${libsolvext_SRCS}
+       tarhead.c)
+ENDIF (ENABLE_ARCHREPO)
+
 IF (NOT MSVC)
 SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
 ENDIF (NOT MSVC)
index 698d5063c768d0e1b876f0c9df7e7a633f9f9ee5..4221b058a05876eaa1a931189c08e7b3592c546f 100644 (file)
 #include "util.h"
 #include "chksum.h"
 #include "solv_xfopen.h"
+#include "tarhead.h"
 #include "repo_arch.h"
 
-static long long parsenum(unsigned char *p, int cnt)
-{
-  long long x = 0;
-  if (!cnt)
-    return -1;
-  if (*p & 0x80)
-    {
-      /* binary format */
-      x = *p & 0x40 ? (-1 << 8 | *p)  : (*p ^ 0x80);
-      while (--cnt > 0)
-       x = (x << 8) | *p++;
-      return x;
-    }
-  while (cnt > 0 && (*p == ' ' || *p == '\t'))
-    cnt--, p++;
-  if (*p == '-')
-    return -1;
-  for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
-    x = (x << 3) | (*p - '0');
-  return x;
-}
-
-static int readblock(FILE *fp, unsigned char *blk)
-{
-  int r, l = 0;
-  while (l < 512)
-    {
-      r = fread(blk + l, 1, 512 - l, fp);
-      if (r <= 0)
-       return -1;
-      l += r;
-    }
-  return 0;
-}
-
-struct tarhead {
-  FILE *fp;
-  unsigned char blk[512];
-  int type;
-  long long length;
-  char *path;
-  int eof;
-  int ispax;
-  int off;
-  int end;
-};
-
-static char *getsentry(struct tarhead *th, char *s, int size)
-{
-  char *os = s;
-  if (th->eof || size <= 1)
-    return 0;
-  size--;      /* terminating 0 */
-  for (;;)
-    {
-      int i;
-      for (i = th->off; i < th->end; i++)
-       {
-         *s++ = th->blk[i];
-         size--;
-         if (!size || th->blk[i] == '\n')
-           {
-             th->off = i + 1;
-             *s = 0;
-             return os;
-           }
-       }
-      th->off = i;
-      if (!th->path)
-       {
-         /* fake entry */
-         th->end = fread(th->blk, 1, 512, th->fp);
-         if (th->end <= 0)
-           {
-             th->eof = 1;
-             return 0;
-           }
-         th->off = 0;
-         continue;
-       }
-      if (th->length <= 0)
-       return 0;
-      if (readblock(th->fp, th->blk))
-       {
-         th->eof = 1;
-         return 0;
-       }
-      th->off = 0;
-      th->end = th->length > 512 ? 512 : th->length;
-      th->length -= th->end;
-    }
-}
-
-static void skipentry(struct tarhead *th)
-{
-  for (; th->length > 0; th->length -= 512)
-    {
-      if (readblock(th->fp, th->blk))
-       {
-         th->eof = 1;
-         th->length = 0;
-         return;
-       }
-    }
-  th->length = 0;
-  th->off = th->end = 0;
-}
-
-static void inittarhead(struct tarhead *th, FILE *fp)
-{
-  memset(th, 0, sizeof(*th));
-  th->fp = fp;
-}
-
-static void freetarhead(struct tarhead *th)
-{
-  solv_free(th->path);
-}
-
-static int gettarhead(struct tarhead *th)
-{
-  int l, type;
-  long long length;
-
-  th->path = solv_free(th->path);
-  th->ispax = 0;
-  th->type = 0;
-  th->length = 0;
-  th->off = 0;
-  th->end = 0;
-  if (th->eof)
-    return 0;
-  for (;;)
-    {
-      int r = readblock(th->fp, th->blk);
-      if (r)
-       {
-         if (feof(th->fp))
-           {
-             th->eof = 1;
-             return 0;
-           }
-         return -1;
-       }
-      if (th->blk[0] == 0)
-       {
-          th->eof = 1;
-         return 0;
-       }
-      length = parsenum(th->blk + 124, 12);
-      if (length < 0)
-       return -1;
-      type = 0;
-      switch (th->blk[156])
-       {
-       case 'S': case '0':
-         type = 1;     /* file */
-         break;
-       case '1':
-         /* hard link, special length magic... */
-         if (!th->ispax)
-           length = 0;
-         break;
-       case '5':
-         type = 2;     /* dir */
-         break;
-       case '2': case '3': case '4': case '6':
-         length = 0;
-         break;
-       case 'X': case 'x': case 'L':
-         {
-           char *data, *pp;
-           if (length < 1 || length >= 1024 * 1024)
-             return -1;
-           data = pp = solv_malloc(length + 512);
-           for (l = length; l > 0; l -= 512, pp += 512)
-             if (readblock(th->fp, (unsigned char *)pp))
-               {
-                 solv_free(data);
-                 return -1;
-               }
-           data[length] = 0;
-           type = 3;           /* extension */
-           if (th->blk[156] == 'L')
-             {
-               solv_free(th->path);
-               th->path = data;
-               length = 0;
-               break;
-             }
-           pp = data;
-           while (length > 0)
-             {
-               int ll = 0;
-               for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
-                 ll = ll * 10 + (pp[l] - '0');
-               if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
-                 {
-                   solv_free(data);
-                   return -1;
-                 }
-               length -= ll;
-               pp += l + 1;
-               ll -= l + 1;
-               pp[ll - 1] = 0;
-               if (!strncmp(pp, "path=", 5))
-                 {
-                   solv_free(th->path);
-                   th->path = solv_strdup(pp + 5);
-                 }
-               pp += ll;
-             }
-           solv_free(data);
-           th->ispax = 1;
-           length = 0;
-           break;
-         }
-       default:
-         type = 3;     /* extension */
-         break;
-       }
-      if ((type == 1 || type == 2) && !th->path)
-       {
-         char path[157];
-         memcpy(path, th->blk, 156);
-         path[156] = 0;
-         if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
-           {
-             /* POSIX ustar with prefix */
-             char prefix[156];
-             memcpy(prefix, th->blk + 345, 155);
-             prefix[155] = 0;
-             l = strlen(prefix);
-             if (l && prefix[l - 1] == '/')
-               prefix[l - 1] = 0;
-             th->path = solv_dupjoin(prefix, "/", path);
-           }
-         else
-           th->path = solv_dupjoin(path, 0, 0);
-       }
-      if (type == 1 || type == 2)
-       {
-         l = strlen(th->path);
-         if (l && th->path[l - 1] == '/')
-           {
-             if (l > 1)
-               th->path[l - 1] = 0;
-             type = 2;
-           }
-       }
-      if (type != 3)
-       break;
-      while (length > 0)
-       {
-         r = readblock(th->fp, th->blk);
-         if (r)
-           return r;
-         length -= 512;
-       }
-    }
-  th->type = type;
-  th->length = length;
-  return 1;
-}
-
 static Offset
 adddep(Repo *repo, Offset olddeps, char *line)
 {
@@ -333,10 +69,10 @@ repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
   Repodata *data;
   FILE *fp;
   struct tarhead th;
-  char line[4096];
-  int ignoreline;
+  char *line = 0;
+  size_t line_alloc = 0, l;
   Solvable *s;
-  int l, fd;
+  int fd;
   struct stat stb;
   Chksum *pkgidchk = 0;
 
@@ -359,36 +95,24 @@ repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
       return 0;
     }
   s = 0;
-  inittarhead(&th, fp);
-  while (gettarhead(&th) > 0)
+  tarhead_init(&th, fp);
+  while (tarhead_next(&th) > 0)
     {
       if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
        {
-          skipentry(&th);
+          tarhead_skip(&th);
          continue;
        }
-      ignoreline = 0;
       s = pool_id2solvable(pool, repo_add_solvable(repo));
       if (flags & ARCH_ADD_WITH_PKGID)
        pkgidchk = solv_chksum_create(REPOKEY_TYPE_MD5);
-      while (getsentry(&th, line, sizeof(line)))
+      while ((l = tarhead_gets(&th, &line, &line_alloc)) > 0)
        {
-         l = strlen(line);
-         if (l == 0)
-           continue;
          if (pkgidchk)
            solv_chksum_add(pkgidchk, line, l);
-         if (line[l - 1] != '\n')
-           {
-             ignoreline = 1;
-             continue;
-           }
-         if (ignoreline)
-           {
-             ignoreline = 0;
-             continue;
-           }
-         line[--l] = 0;
+         l = strlen(line);     /* no NULs please */
+         if (l && line[l - 1] == '\n')
+           line[--l] = 0;
          if (l == 0 || line[0] == '#')
            continue;
          if (!strncmp(line, "pkgname = ", 10))
@@ -432,7 +156,8 @@ repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
        }
       break;
     }
-  freetarhead(&th);
+  solv_free(line);
+  tarhead_free(&th);
   fclose(fp);
   if (!s)
     {
@@ -472,32 +197,6 @@ repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
   return s ? s - pool->solvables : 0;
 }
 
-static char *getsentrynl(struct tarhead *th, char *s, int size)
-{
-  int l;
-  if (!getsentry(th, s, size))
-    {
-      *s = 0;  /* eof */
-      return 0;
-    }
-  l = strlen(s);
-  if (!l)
-    return 0;
-  if (l && s[l - 1] == '\n')
-    {
-      s[l - 1] = 0;
-      return s;
-    }
-  while (getsentry(th, s, size))
-    {
-      l = strlen(s);
-      if (!l || s[l - 1] == '\n')
-       return 0;
-    }
-  *s = 0;      /* eof */
-  return 0;
-}
-
 static Hashtable
 joinhash_init(Repo *repo, Hashval *hmp)
 {
@@ -557,41 +256,50 @@ joinhash_lookup(Repo *repo, Hashtable ht, Hashval hm, const char *fn)
   return 0;
 }
 
+static int getsentrynl(struct tarhead *th, char **linep, size_t *line_allocp)
+{
+  size_t l = tarhead_gets(th, linep, line_allocp);
+  if (l)
+    l = strlen(*linep);
+  if (l && (*linep)[l - 1] == '\n')
+    (*linep)[--l] = 0;
+  return l ? 1 : 0;
+}
+
 static void
 adddata(Repodata *data, Solvable *s, struct tarhead *th)
 {
   Repo *repo = data->repo;
   Pool *pool = repo->pool;
-  char line[4096];
-  int l;
+  char *line = 0;
+  size_t l, line_alloc = 0;
   int havesha256 = 0;
 
-  while (getsentry(th, line, sizeof(line)))
+  while ((l = tarhead_gets(th, &line, &line_alloc)) > 0)
     {
       l = strlen(line);
-      if (l == 0 || line[l - 1] != '\n')
-       continue;
-      line[--l] = 0;
+      if (l && line[l - 1] == '\n')
+        line[--l] = 0;
       if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
        continue;
       if (!strcmp(line, "%FILENAME%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_location(data, s - pool->solvables, 0, 0, line);
        }
       else if (!strcmp(line, "%NAME%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            s->name = pool_str2id(pool, line, 1);
        }
       else if (!strcmp(line, "%VERSION%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            s->evr = pool_str2id(pool, line, 1);
        }
       else if (!strcmp(line, "%DESC%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            {
              repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
              repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
@@ -599,27 +307,27 @@ adddata(Repodata *data, Solvable *s, struct tarhead *th)
        }
       else if (!strcmp(line, "%GROUPS%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_GROUP, line);
        }
       else if (!strcmp(line, "%CSIZE%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
        }
       else if (!strcmp(line, "%ISIZE%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
        }
       else if (!strcmp(line, "%MD5SUM%"))
        {
-         if (getsentrynl(th, line, sizeof(line)) && !havesha256)
+         if (getsentrynl(th, &line, &line_alloc) && !havesha256)
            repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
        }
       else if (!strcmp(line, "%SHA256SUM%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            {
              repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
              havesha256 = 1;
@@ -627,52 +335,52 @@ adddata(Repodata *data, Solvable *s, struct tarhead *th)
        }
       else if (!strcmp(line, "%URL%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
        }
       else if (!strcmp(line, "%LICENSE%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_add_poolstr_array(data, s - pool->solvables, SOLVABLE_LICENSE, line);
        }
       else if (!strcmp(line, "%ARCH%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            s->arch = pool_str2id(pool, line, 1);
        }
       else if (!strcmp(line, "%BUILDDATE%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
        }
       else if (!strcmp(line, "%PACKAGER%"))
        {
-         if (getsentrynl(th, line, sizeof(line)))
+         if (getsentrynl(th, &line, &line_alloc))
            repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
        }
       else if (!strcmp(line, "%REPLACES%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            s->obsoletes = adddep(repo, s->obsoletes, line);
        }
       else if (!strcmp(line, "%DEPENDS%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            s->requires = adddep(repo, s->requires, line);
        }
       else if (!strcmp(line, "%CONFLICTS%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            s->conflicts = adddep(repo, s->conflicts, line);
        }
       else if (!strcmp(line, "%PROVIDES%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            s->provides = adddep(repo, s->provides, line);
        }
       else if (!strcmp(line, "%OPTDEPENDS%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            {
              char *p = strchr(line, ':');
              if (p && p > line)
@@ -682,7 +390,7 @@ adddata(Repodata *data, Solvable *s, struct tarhead *th)
        }
       else if (!strcmp(line, "%FILES%"))
        {
-         while (getsentrynl(th, line, sizeof(line)) && *line)
+         while (getsentrynl(th, &line, &line_alloc) && *line)
            {
              char *p;
              Id id;
@@ -714,9 +422,10 @@ adddata(Repodata *data, Solvable *s, struct tarhead *th)
              repodata_add_dirstr(data, s - pool->solvables, SOLVABLE_FILELIST, id, p);
            }
        }
-      while (*line)
-       getsentrynl(th, line, sizeof(line));
+      while (*line && getsentrynl(th, &line, &line_alloc))
+       ;
     }
+  solv_free(line);
 }
 
 static void
@@ -754,24 +463,24 @@ repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
   if (flags & REPO_EXTEND_SOLVABLES)
     joinhash = joinhash_init(repo, &joinhashmask);
 
-  inittarhead(&th, fp);
-  while (gettarhead(&th) > 0)
+  tarhead_init(&th, fp);
+  while (tarhead_next(&th) > 0)
     {
       char *bn;
       if (th.type != 1)
        {
-          skipentry(&th);
+          tarhead_skip(&th);
          continue;
        }
       bn = strrchr(th.path, '/');
       if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0 && strcmp(bn + 1, "files") != 0))
        {
-          skipentry(&th);
+          tarhead_skip(&th);
          continue;
        }
       if ((flags & REPO_EXTEND_SOLVABLES) != 0 && (!strcmp(bn + 1, "desc") || !strcmp(bn + 1, "depends")))
        {
-          skipentry(&th);
+          tarhead_skip(&th);
          continue;     /* skip those when we're extending */
        }
       if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
@@ -786,7 +495,7 @@ repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
              s = joinhash_lookup(repo, joinhash, joinhashmask, lastdn);
              if (!s)
                {
-                 skipentry(&th);
+                 tarhead_skip(&th);
                  continue;
                }
            }
@@ -831,17 +540,17 @@ repo_add_arch_local(Repo *repo, const char *dir, int flags)
          if ((fp = fopen(file, "r")) != 0)
            {
              struct tarhead th;
-             inittarhead(&th, fp);
+             tarhead_init(&th, fp);
              s = pool_id2solvable(pool, repo_add_solvable(repo));
              adddata(data, s, &th);
-             freetarhead(&th);
+             tarhead_free(&th);
              fclose(fp);
              file = pool_tmpjoin(repo->pool, entrydir, "/files", 0);
              if ((fp = fopen(file, "r")) != 0)
                {
-                 inittarhead(&th, fp);
+                 tarhead_init(&th, fp);
                  adddata(data, s, &th);
-                 freetarhead(&th);
+                 tarhead_free(&th);
                  fclose(fp);
                }
            }
diff --git a/ext/tarhead.c b/ext/tarhead.c
new file mode 100644 (file)
index 0000000..e229b43
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2012, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include "util.h"
+#include "tarhead.h"
+
+static long long parsenum(unsigned char *p, int cnt)
+{
+  long long x = 0;
+  if (!cnt)
+    return -1;
+  if (*p & 0x80)
+    {
+      /* binary format */
+      x = *p & 0x40 ? (-1 << 8 | *p)  : (*p ^ 0x80);
+      while (--cnt > 0)
+       x = (x << 8) | *p++;
+      return x;
+    }
+  while (cnt > 0 && (*p == ' ' || *p == '\t'))
+    cnt--, p++;
+  if (*p == '-')
+    return -1;
+  for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
+    x = (x << 3) | (*p - '0');
+  return x;
+}
+
+static int readblock(FILE *fp, unsigned char *blk)
+{
+  int r, l = 0;
+  while (l < 512)
+    {
+      r = fread(blk + l, 1, 512 - l, fp);
+      if (r <= 0)
+       return -1;
+      l += r;
+    }
+  return 0;
+}
+
+void tarhead_skip(struct tarhead *th)
+{
+  for (; th->length > 0; th->length -= 512)
+    {
+      if (readblock(th->fp, th->blk))
+       {
+         th->eof = 1;
+         th->length = 0;
+         return;
+       }
+    }
+  th->length = 0;
+  th->off = th->end = 0;
+}
+
+void tarhead_init(struct tarhead *th, FILE *fp)
+{
+  memset(th, 0, sizeof(*th));
+  th->fp = fp;
+}
+
+void tarhead_free(struct tarhead *th)
+{
+  solv_free(th->path);
+}
+
+int tarhead_next(struct tarhead *th)
+{
+  int l, type;
+  long long length;
+
+  th->path = solv_free(th->path);
+  th->ispax = 0;
+  th->type = 0;
+  th->length = 0;
+  th->off = 0;
+  th->end = 0;
+  if (th->eof)
+    return 0;
+  for (;;)
+    {
+      int r = readblock(th->fp, th->blk);
+      if (r)
+       {
+         if (feof(th->fp))
+           {
+             th->eof = 1;
+             return 0;
+           }
+         return -1;
+       }
+      if (th->blk[0] == 0)
+       {
+          th->eof = 1;
+         return 0;
+       }
+      length = parsenum(th->blk + 124, 12);
+      if (length < 0)
+       return -1;
+      type = 0;
+      switch (th->blk[156])
+       {
+       case 'S': case '0':
+         type = 1;     /* file */
+         break;
+       case '1':
+         /* hard link, special length magic... */
+         if (!th->ispax)
+           length = 0;
+         break;
+       case '5':
+         type = 2;     /* dir */
+         break;
+       case '2': case '3': case '4': case '6':
+         length = 0;
+         break;
+       case 'X': case 'x': case 'L':
+         {
+           char *data, *pp;
+           if (length < 1 || length >= 1024 * 1024)
+             return -1;
+           data = pp = solv_malloc(length + 512);
+           for (l = length; l > 0; l -= 512, pp += 512)
+             if (readblock(th->fp, (unsigned char *)pp))
+               {
+                 solv_free(data);
+                 return -1;
+               }
+           data[length] = 0;
+           type = 3;           /* extension */
+           if (th->blk[156] == 'L')
+             {
+               solv_free(th->path);
+               th->path = data;
+               length = 0;
+               break;
+             }
+           pp = data;
+           while (length > 0)
+             {
+               int ll = 0;
+               for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
+                 ll = ll * 10 + (pp[l] - '0');
+               if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
+                 {
+                   solv_free(data);
+                   return -1;
+                 }
+               length -= ll;
+               pp += l + 1;
+               ll -= l + 1;
+               pp[ll - 1] = 0;
+               if (!strncmp(pp, "path=", 5))
+                 {
+                   solv_free(th->path);
+                   th->path = solv_strdup(pp + 5);
+                 }
+               pp += ll;
+             }
+           solv_free(data);
+           th->ispax = 1;
+           length = 0;
+           break;
+         }
+       default:
+         type = 3;     /* extension */
+         break;
+       }
+      if ((type == 1 || type == 2) && !th->path)
+       {
+         char path[157];
+         memcpy(path, th->blk, 156);
+         path[156] = 0;
+         if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
+           {
+             /* POSIX ustar with prefix */
+             char prefix[156];
+             memcpy(prefix, th->blk + 345, 155);
+             prefix[155] = 0;
+             l = strlen(prefix);
+             if (l && prefix[l - 1] == '/')
+               prefix[l - 1] = 0;
+             th->path = solv_dupjoin(prefix, "/", path);
+           }
+         else
+           th->path = solv_dupjoin(path, 0, 0);
+       }
+      if (type == 1 || type == 2)
+       {
+         l = strlen(th->path);
+         if (l && th->path[l - 1] == '/')
+           {
+             if (l > 1)
+               th->path[l - 1] = 0;
+             type = 2;
+           }
+       }
+      if (type != 3)
+       break;
+      while (length > 0)
+       {
+         r = readblock(th->fp, th->blk);
+         if (r)
+           return r;
+         length -= 512;
+       }
+    }
+  th->type = type;
+  th->length = length;
+  return 1;
+}
+
+size_t tarhead_gets(struct tarhead *th, char **linep , size_t *allocsizep)
+{
+  char *line = *linep;
+  size_t lsize = allocsizep ? *allocsizep : 0, size = 0;
+  int i;
+  if (th->eof)
+    return 0;
+  for (;;)
+    {
+      size_t fsize = lsize - size;
+      if (fsize < 2)
+       {
+         line = *linep = solv_realloc(line, lsize += 1024);
+         fsize = lsize - size;
+       }
+      for (i = th->off; i < th->end && fsize > 1;)
+       {
+         fsize--;
+         if ((line[size++] = th->blk[i++]) == '\n')
+           {
+             th->off = i;
+             line[size] = 0;
+             return size;
+           }
+       }
+      /* end of block reached, read next block */
+      th->off = i;
+      if (th->off < th->end)
+       continue;
+      if (!th->path)
+       {
+         /* fake entry */
+         th->off = 0;
+         th->end = fread(th->blk, 1, 512, th->fp);
+         if (th->end <= 0)
+           {
+             th->eof = 1;
+             if (th->end < 0)
+               return 0;
+             break;
+           }
+         continue;
+       }
+      if (th->length <= 0)
+       break;          /* reached end of entry */
+      if (readblock(th->fp, th->blk))
+       {
+         th->eof = 1;
+         return 0;
+       }
+      th->off = 0;
+      th->end = th->length > 512 ? 512 : th->length;
+      th->length -= th->end;
+    }
+  line[size] = 0;
+  return size;
+}
+
diff --git a/ext/tarhead.h b/ext/tarhead.h
new file mode 100644 (file)
index 0000000..a5ba48e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+struct tarhead {
+  FILE *fp;
+  unsigned char blk[512];
+  int type;
+  long long length;
+  char *path;
+  int eof;
+  int ispax;
+
+  int off;
+  int end;
+};
+
+extern void tarhead_init(struct tarhead *th, FILE *fp);
+extern void tarhead_free(struct tarhead *th);
+extern int tarhead_next(struct tarhead *th);
+extern void tarhead_skip(struct tarhead *th);
+extern size_t tarhead_gets(struct tarhead *th, char **line, size_t *allocp);