implement SOLVER_FLAG_BREAK_ORPHANS
[platform/upstream/libsolv.git] / ext / solv_xfopen.c
index 9d94e01..b0421bf 100644 (file)
 #include <fcntl.h>
 
 #include "solv_xfopen.h"
+#include "util.h"
+
+
+/* Evil hack for Haiku: fopencookie() is implemented internally, but not
+   exported by a header. */
+#ifdef __HAIKU__
+
+typedef struct {
+       ssize_t (*read)(void*, char*, size_t);
+       ssize_t (*write)(void*, const char*, size_t);
+       int (*seek)(off_t*, int);
+       int (*close)(void*);
+} cookie_io_functions_t;
+
+
+FILE *fopencookie(void*, const char*, cookie_io_functions_t);
+
+#endif /* __HAIKU__ */
 
 
 static FILE *cookieopen(void *cookie, const char *mode,
-       ssize_t (*cread)(void *, char *, size_t), 
-       ssize_t (*cwrite)(void *, const char *, size_t), 
+       ssize_t (*cread)(void *, char *, size_t),
+       ssize_t (*cwrite)(void *, const char *, size_t),
        int (*cclose)(void *))
 {
+#ifdef HAVE_FUNOPEN
   if (!cookie)
     return 0;
-#ifdef HAVE_FUNOPEN
-  return funopen(cookie, 
-      (int (*)(void *, char *, int))(*mode == 'r' ? cread: NULL),/* readfn */
-      (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
-      (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
+  return funopen(cookie,
+      (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL),             /* readfn */
+      (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL),      /* writefn */
+      (fpos_t (*)(void *, fpos_t, int))NULL,                                   /* seekfn */
       cclose
       );
 #elif defined(HAVE_FOPENCOOKIE)
   cookie_io_functions_t cio;
+
+  if (!cookie)
+    return 0;
   memset(&cio, 0, sizeof(cio));
   if (*mode == 'r')
     cio.read = cread;
@@ -49,179 +70,234 @@ static FILE *cookieopen(void *cookie, const char *mode,
 
 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
 {
-  return gzread((gzFile *)cookie, buf, nbytes);
+  return gzread((gzFile)cookie, buf, nbytes);
 }
 
 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
 {
-  return gzwrite((gzFile *)cookie, buf, nbytes);
+  return gzwrite((gzFile)cookie, buf, nbytes);
 }
 
 static int cookie_gzclose(void *cookie)
 {
-  return gzclose((gzFile *)cookie);
+  return gzclose((gzFile)cookie);
 }
 
 static inline FILE *mygzfopen(const char *fn, const char *mode)
 {
-  gzFile *gzf = gzopen(fn, mode);
+  gzFile gzf = gzopen(fn, mode);
   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
 }
 
 static inline FILE *mygzfdopen(int fd, const char *mode)
 {
-  gzFile *gzf = gzdopen(fd, mode);
+  gzFile gzf = gzdopen(fd, mode);
   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
 }
 
+#ifdef ENABLE_BZIP2_COMPRESSION
+
+#include <bzlib.h>
+
+/* bzip2 compression */
+
+static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
+{
+  return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
+}
+
+static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
+{
+  return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
+}
+
+static int cookie_bzclose(void *cookie)
+{
+  BZ2_bzclose((BZFILE *)cookie);
+  return 0;
+}
+
+static inline FILE *mybzfopen(const char *fn, const char *mode)
+{
+  BZFILE *bzf = BZ2_bzopen(fn, mode);
+  return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
+}
+
+static inline FILE *mybzfdopen(int fd, const char *mode)
+{
+  BZFILE *bzf = BZ2_bzdopen(fd, mode);
+  return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
+}
+
+#endif
+
 
-#ifdef ENABLE_XZ_COMPRESSION
+#ifdef ENABLE_LZMA_COMPRESSION
 
 #include <lzma.h>
 
+/* lzma code written by me in 2008 for rpm's rpmio.c */
+
 typedef struct lzfile {
-    unsigned char buf[1 << 15];
-    lzma_stream strm;
-    FILE *file;
-    int encoding;
-    int eof;
+  unsigned char buf[1 << 15];
+  lzma_stream strm;
+  FILE *file;
+  int encoding;
+  int eof;
 } LZFILE;
 
-static LZFILE *lzopen(const char *path, const char *mode, int fd, int xz)
+static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
 {
-    int level = 7;      /* Use XZ's default compression level if unspecified */
-    int encoding = 0;
-    FILE *fp;
-    LZFILE *lzfile;
-    lzma_ret ret;
-    lzma_stream init_strm = LZMA_STREAM_INIT;
-
-    if (!path && fd < 0)
-       return 0;
-    for (; *mode; mode++) {
-       if (*mode == 'w')
-           encoding = 1;
-       else if (*mode == 'r')
-           encoding = 0;
-       else if (*mode >= '1' && *mode <= '9')
-           level = *mode - '0';
+  lzma_options_lzma options;
+  lzma_lzma_preset(&options, level);
+  return lzma_alone_encoder(strm, &options);
+}
+
+static lzma_stream stream_init = LZMA_STREAM_INIT;
+
+static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
+{
+  int level = 7;
+  int encoding = 0;
+  FILE *fp;
+  LZFILE *lzfile;
+  lzma_ret ret;
+
+  if (!path && fd < 0)
+    return 0;
+  for (; *mode; mode++)
+    {
+      if (*mode == 'w')
+       encoding = 1;
+      else if (*mode == 'r')
+       encoding = 0;
+      else if (*mode >= '1' && *mode <= '9')
+       level = *mode - '0';
     }
-    if (fd != -1)
-       fp = fdopen(fd, encoding ? "w" : "r");
-    else
-       fp = fopen(path, encoding ? "w" : "r");
-    if (!fp)
-       return 0;
-    lzfile = calloc(1, sizeof(*lzfile));
-    if (!lzfile)
-       return 0;
-    lzfile->file = fp;
-    lzfile->encoding = encoding;
-    lzfile->eof = 0;
-    lzfile->strm = init_strm;
-    if (encoding) {
-       if (xz) {
-           ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
-       } else {
-           lzma_options_lzma options;
-           lzma_lzma_preset(&options, level);
-           ret = lzma_alone_encoder(&lzfile->strm, &options);
-       }
-    } else {    /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
-       ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0);
+  if (fd != -1)
+    fp = fdopen(fd, encoding ? "w" : "r");
+  else
+    fp = fopen(path, encoding ? "w" : "r");
+  if (!fp)
+    return 0;
+  lzfile = calloc(1, sizeof(*lzfile));
+  if (!lzfile)
+    {
+      fclose(fp);
+      return 0;
     }
-    if (ret != LZMA_OK) {
-       fclose(fp);
-       free(lzfile);
-       return 0;
+  lzfile->file = fp;
+  lzfile->encoding = encoding;
+  lzfile->eof = 0;
+  lzfile->strm = stream_init;
+  if (encoding)
+    {
+      if (isxz)
+       ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
+      else
+       ret = setup_alone_encoder(&lzfile->strm, level);
+    }
+  else
+    ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
+  if (ret != LZMA_OK)
+    {
+      fclose(fp);
+      free(lzfile);
+      return 0;
     }
-    return lzfile;
+  return lzfile;
 }
 
 static int lzclose(void *cookie)
 {
-    LZFILE *lzfile = cookie;
-    lzma_ret ret;
-    size_t n;
-    int rc;
-
-    if (!lzfile)
-       return -1;
-    if (lzfile->encoding) {
-       for (;;) {
-           lzfile->strm.avail_out = sizeof(lzfile->buf);
-           lzfile->strm.next_out = lzfile->buf;
-           ret = lzma_code(&lzfile->strm, LZMA_FINISH);
-           if (ret != LZMA_OK && ret != LZMA_STREAM_END)
-               return -1;
-           n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
-           if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
-               return -1;
-           if (ret == LZMA_STREAM_END)
-               break;
+  LZFILE *lzfile = cookie;
+  lzma_ret ret;
+  size_t n;
+  int rc;
+
+  if (!lzfile)
+    return -1;
+  if (lzfile->encoding)
+    {
+      for (;;)
+       {
+         lzfile->strm.avail_out = sizeof(lzfile->buf);
+         lzfile->strm.next_out = lzfile->buf;
+         ret = lzma_code(&lzfile->strm, LZMA_FINISH);
+         if (ret != LZMA_OK && ret != LZMA_STREAM_END)
+           return -1;
+         n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
+         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+           return -1;
+         if (ret == LZMA_STREAM_END)
+           break;
        }
     }
-    lzma_end(&lzfile->strm);
-    rc = fclose(lzfile->file);
-    free(lzfile);
-    return rc;
+  lzma_end(&lzfile->strm);
+  rc = fclose(lzfile->file);
+  free(lzfile);
+  return rc;
 }
 
 static ssize_t lzread(void *cookie, char *buf, size_t len)
 {
-    LZFILE *lzfile = cookie;
-    lzma_ret ret;
-    int eof = 0;
+  LZFILE *lzfile = cookie;
+  lzma_ret ret;
+  int eof = 0;
 
-    if (!lzfile || lzfile->encoding)
-      return -1;
-    if (lzfile->eof)
-      return 0;
-    lzfile->strm.next_out = (unsigned char *)buf;
-    lzfile->strm.avail_out = len;
-    for (;;) {
-       if (!lzfile->strm.avail_in) {
-           lzfile->strm.next_in = lzfile->buf;
-           lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
-           if (!lzfile->strm.avail_in)
-               eof = 1;
+  if (!lzfile || lzfile->encoding)
+    return -1;
+  if (lzfile->eof)
+    return 0;
+  lzfile->strm.next_out = (unsigned char *)buf;
+  lzfile->strm.avail_out = len;
+  for (;;)
+    {
+      if (!lzfile->strm.avail_in)
+       {
+         lzfile->strm.next_in = lzfile->buf;
+         lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
+         if (!lzfile->strm.avail_in)
+           eof = 1;
        }
-       ret = lzma_code(&lzfile->strm, LZMA_RUN);
-       if (ret == LZMA_STREAM_END) {
-           lzfile->eof = 1;
-           return len - lzfile->strm.avail_out;
+      ret = lzma_code(&lzfile->strm, LZMA_RUN);
+      if (ret == LZMA_STREAM_END)
+       {
+         lzfile->eof = 1;
+         return len - lzfile->strm.avail_out;
        }
-       if (ret != LZMA_OK)
-           return -1;
-       if (!lzfile->strm.avail_out)
-           return len;
-       if (eof)
-           return -1;
-      }
+      if (ret != LZMA_OK)
+       return -1;
+      if (!lzfile->strm.avail_out)
+       return len;
+      if (eof)
+       return -1;
+    }
 }
 
 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
 {
-    LZFILE *lzfile = cookie;
-    lzma_ret ret;
-    size_t n;
-    if (!lzfile || !lzfile->encoding)
+  LZFILE *lzfile = cookie;
+  lzma_ret ret;
+  size_t n;
+  if (!lzfile || !lzfile->encoding)
+    return -1;
+  if (!len)
+    return 0;
+  lzfile->strm.next_in = (unsigned char *)buf;
+  lzfile->strm.avail_in = len;
+  for (;;)
+    {
+      lzfile->strm.next_out = lzfile->buf;
+      lzfile->strm.avail_out = sizeof(lzfile->buf);
+      ret = lzma_code(&lzfile->strm, LZMA_RUN);
+      if (ret != LZMA_OK)
        return -1;
-    if (!len)
-       return 0;
-    lzfile->strm.next_in = (unsigned char *)buf;
-    lzfile->strm.avail_in = len;
-    for (;;) {
-       lzfile->strm.next_out = lzfile->buf;
-       lzfile->strm.avail_out = sizeof(lzfile->buf);
-       ret = lzma_code(&lzfile->strm, LZMA_RUN);
-       if (ret != LZMA_OK)
-           return -1;
-       n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
-       if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
-           return -1;
-       if (!lzfile->strm.avail_in)
-           return len;
+      n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
+      if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
+       return -1;
+      if (!lzfile->strm.avail_in)
+       return len;
     }
 }
 
@@ -249,7 +325,7 @@ static inline FILE *mylzfdopen(int fd, const char *mode)
   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
 }
 
-#endif /* ENABLE_XZ_COMPRESSION */
+#endif /* ENABLE_LZMA_COMPRESSION */
 
 
 FILE *
@@ -264,11 +340,23 @@ solv_xfopen(const char *fn, const char *mode)
   suf = strrchr(fn, '.');
   if (suf && !strcmp(suf, ".gz"))
     return mygzfopen(fn, mode);
-#ifdef ENABLE_XZ_COMPRESSION
+#ifdef ENABLE_LZMA_COMPRESSION
   if (suf && !strcmp(suf, ".xz"))
     return myxzfopen(fn, mode);
   if (suf && !strcmp(suf, ".lzma"))
     return mylzfopen(fn, mode);
+#else
+  if (suf && !strcmp(suf, ".xz"))
+    return 0;
+  if (suf && !strcmp(suf, ".lzma"))
+    return 0;
+#endif
+#ifdef ENABLE_BZIP2_COMPRESSION
+  if (suf && !strcmp(suf, ".bz2"))
+    return mybzfopen(fn, mode);
+#else
+  if (suf && !strcmp(suf, ".bz2"))
+    return 0;
 #endif
   return fopen(fn, mode);
 }
@@ -276,6 +364,7 @@ solv_xfopen(const char *fn, const char *mode)
 FILE *
 solv_xfopen_fd(const char *fn, int fd, const char *mode)
 {
+  const char *simplemode = mode;
   char *suf;
 
   suf = fn ? strrchr(fn, '.') : 0;
@@ -286,25 +375,134 @@ solv_xfopen_fd(const char *fn, int fd, const char *mode)
        return 0;
       fl &= O_RDONLY|O_WRONLY|O_RDWR;
       if (fl == O_WRONLY)
-       mode = "w";
+       mode = simplemode = "w";
       else if (fl == O_RDWR)
        {
-         if (!suf || strcmp(suf, ".gz") != 0)
-           mode = "r+";
-         else
-           mode = "r";
+         mode = "r+";
+         simplemode = "r";
        }
       else
-       mode = "r";
+       mode = simplemode = "r";
     }
   if (suf && !strcmp(suf, ".gz"))
-    return mygzfdopen(fd, mode);
-#ifdef ENABLE_XZ_COMPRESSION
+    return mygzfdopen(fd, simplemode);
+#ifdef ENABLE_LZMA_COMPRESSION
   if (suf && !strcmp(suf, ".xz"))
-    return myxzfdopen(fd, mode);
+    return myxzfdopen(fd, simplemode);
   if (suf && !strcmp(suf, ".lzma"))
-    return mylzfdopen(fd, mode);
+    return mylzfdopen(fd, simplemode);
+#else
+  if (suf && !strcmp(suf, ".xz"))
+    return 0;
+  if (suf && !strcmp(suf, ".lzma"))
+    return 0;
+#endif
+#ifdef ENABLE_BZIP2_COMPRESSION
+  if (suf && !strcmp(suf, ".bz2"))
+    return mybzfdopen(fd, simplemode);
+#else
+  if (suf && !strcmp(suf, ".bz2"))
+    return 0;
 #endif
   return fdopen(fd, mode);
 }
 
+int
+solv_xfopen_iscompressed(const char *fn)
+{
+  const char *suf = fn ? strrchr(fn, '.') : 0;
+  if (!suf)
+    return 0;
+  if (!strcmp(suf, ".gz"))
+    return 1;
+  if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
+#ifdef ENABLE_LZMA_COMPRESSION
+    return 1;
+#else
+    return -1;
+#endif
+  if (!strcmp(suf, ".bz2"))
+#ifdef ENABLE_BZIP2_COMPRESSION
+    return 1;
+#else
+    return -1;
+#endif
+  return 0;
+}
+
+struct bufcookie {
+  char **bufp;
+  size_t *buflp;
+  char *freemem;
+  size_t bufl_int;
+};
+
+static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
+{
+  struct bufcookie *bc = cookie;
+  size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
+  if (n)
+    {
+      memcpy(buf, *bc->bufp, n);
+      *bc->bufp += n;
+      *bc->buflp -= n;
+    }
+  return n;
+}
+
+static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
+{
+  struct bufcookie *bc = cookie;
+  int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
+  if (n)
+    {
+      *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
+      memcpy(*bc->bufp, buf, n);
+      (*bc->bufp)[n] = 0;      /* zero-terminate */
+      *bc->buflp += n;
+    }
+  return n;
+}
+
+static int cookie_bufclose(void *cookie)
+{
+  struct bufcookie *bc = cookie;
+  if (bc->freemem)
+    solv_free(bc->freemem);
+  solv_free(bc);
+  return 0;
+}
+
+FILE *
+solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
+{
+  struct bufcookie *bc;
+  FILE *fp;
+  if (*mode != 'r' && *mode != 'w')
+    return 0;
+  bc = solv_calloc(1, sizeof(*bc));
+  bc->freemem = 0;
+  bc->bufp = bufp;
+  if (!buflp)
+    {
+      bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
+      buflp = &bc->bufl_int;
+    }
+  bc->buflp = buflp;
+  if (*mode == 'w')
+    {
+      *bc->bufp = solv_extend(0, 0, 1, 1, 4095);       /* always zero-terminate */
+      (*bc->bufp)[0] = 0;
+      *bc->buflp = 0;
+    }
+  fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
+  if (!strcmp(mode, "rf"))     /* auto-free */
+    bc->freemem = *bufp;
+  if (!fp)
+    {
+      if (*mode == 'w')
+       *bc->bufp = solv_free(*bc->bufp);
+      cookie_bufclose(bc);
+    }
+  return fp;
+}