Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

cabextract.c

/*
 * (C) 2000-2006 Stuart Caie <kyzer@4u.net>
 * (C) 2007-2008 Ben Motmans <ben.motmans@gmail.com> (modifications for native interop with mono)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* cabextract uses libmspack to access cabinet files. libmspack is
 * available from http://www.kyz.uklinux.net/libmspack/
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdbool.h>

#include <libgalaxium.h>
#include <mspack/mspack.h>

struct mscab_decompressor *cabd = NULL;

/* prototypes */
static void load_spanning_cabinets(struct mscabd_cabinet *basecab, char *filename);
static char *find_cabinet_file(char *origcab, char *cabname);
static int unix_path_seperators(struct mscabd_file *files);
static char *create_output_name(unsigned char *fname, unsigned char *dirq);

static struct mspack_file *cabx_open(struct mspack_system *this, char *filename, int mode);

static void cabx_close(struct mspack_file *file);
static int cabx_read(struct mspack_file *file, void *buffer, int bytes);
static int cabx_write(struct mspack_file *file, void *buffer, int bytes);
static int cabx_seek(struct mspack_file *file, off_t offset, int mode);
static off_t cabx_tell(struct mspack_file *file);
static void cabx_msg(struct mspack_file *file, char *format, ...);
static void *cabx_alloc(struct mspack_system *this, size_t bytes);
static void cabx_free(void *buffer);
static void cabx_copy(void *src, void *dest, size_t bytes);

struct mspack_file_p
{
      FILE *fh;
      char *name, regular_file;
};

static struct mspack_system cabextract_system =
{
      &cabx_open, &cabx_close, &cabx_read,  &cabx_write, &cabx_seek,
      &cabx_tell, &cabx_msg, &cabx_alloc, &cabx_free, &cabx_copy, NULL
};

bool libgalaxium_cab_init ()
{
      int err;
      MSPACK_SYS_SELFTEST(err);
      if (err) {
            fprintf(stderr, "selftest error %d\n", err);
            return false;
      }

      if (!(cabd = mspack_create_cab_decompressor(&cabextract_system))) {
            fprintf(stderr, "can't create libmspack CAB decompressor\n");
            return false;
      }

      return true;
}

bool libgalaxium_cab_extract (char* filename, char* output_dir)
{
      struct mscabd_cabinet *basecab, *cab, *cab2;
      struct mscabd_file *file;
      int isunix, errors = 0;
      char *name;

      /* search the file for cabinets */
      if (!(basecab = cabd->search(cabd, filename))) {
            return false;
      }

      /* iterate over all cabinets found in that file */
      for (cab = basecab; cab; cab = cab->next) {
            /* load all spanning cabinets */
            load_spanning_cabinets(cab, filename);

            /* determine whether UNIX or MS-DOS path seperators are used */
            isunix = unix_path_seperators(cab->files);

            /* process all files */
            for (file = cab->files; file; file = file->next) {
                  /* create the full UNIX output filename */
                  if (!(name = create_output_name((unsigned char *)file->filename, (unsigned char *)output_dir))) {
                        errors++;
                        continue;
                  }

                  printf("  extracting %s\n", name);

                  if (cabd->extract(cabd, file, name)) {
                        errors++;
                  }

                  free(name);
            }

            /* free the spanning cabinet filenames [not freed by cabd->close()] */
            for (cab2 = cab->prevcab; cab2; cab2 = cab2->prevcab) free(cab2->filename);
            for (cab2 = cab->nextcab; cab2; cab2 = cab2->nextcab) free(cab2->filename);
      }

      /* free all loaded cabinets */
      cabd->close(cabd, basecab);
      if (errors > 0)
            return false;
      else
            return true;
}

void libgalaxium_cab_destroy ()
{
      mspack_destroy_cab_decompressor(cabd);
}

static char *create_output_name (unsigned char* filename, unsigned char* output_dir)
{
      unsigned int len = strlen ((char *)filename) + strlen ((char *)output_dir) + 1;

      unsigned char* fullname = malloc(len);

      *fullname = '\0';
      strcpy((char *)fullname, (char *)output_dir);
      strcat((char *)fullname, "/");
      strcat((char *)fullname, (char *)filename);

      return (char *)fullname;
}

static int unix_path_seperators(struct mscabd_file *files)
{
      struct mscabd_file *fi;
      char slash=0, backslash=0, *oldname;
      int oldlen;

      for (fi = files; fi; fi = fi->next) {
            char *p;
            for (p = fi->filename; *p; p++) {
                  if (*p == '/') slash = 1;
                  if (*p == '\\') backslash = 1;
            }
            if (slash && backslash) break;
      }

      if (slash) {
            /* slashes, but no backslashes = UNIX */
            if (!backslash) return 1;
      } else {
            /* no slashes = MS-DOS */
            return 0;
      }

      /* special case if there's only one file - just take the first slash */
      if (!files->next) {
            char c, *p = fi->filename;
            while ((c = *p++)) {
                  if (c == '\\') return 0; /* backslash = MS-DOS */
                  if (c == '/')  return 1; /* slash = UNIX */
            }
            /* should not happen - at least one slash was found! */
            return 0;
      }

      oldname = NULL;
      oldlen = 0;
      for (fi = files; fi; fi = fi->next) {
            char *name = fi->filename;
            int len = 0;
            while (name[len]) {
                  if ((name[len] == '\\') || (name[len] == '/')) break;
                  len++;
            }
            if (!name[len]) len = 0; else len++;

            if (len && (len == oldlen)) {
                  if (strncmp(name, oldname, (size_t) len) == 0)
                  return (name[len-1] == '\\') ? 0 : 1;
            }
            oldname = name;
            oldlen = len;
      }

      return 0;
}

static void load_spanning_cabinets(struct mscabd_cabinet *basecab, char *filename)
{
      struct mscabd_cabinet *cab, *cab2;
      char *name;

      /* load any spanning cabinets -- backwards */
      for (cab = basecab; cab->flags & MSCAB_HDR_PREVCAB; cab = cab->prevcab) {
            if (!(name = find_cabinet_file(filename, cab->prevname))) {
                  fprintf(stderr, "%s: can't find %s\n", filename, cab->prevname);
                  break;
            }
            if (!(cab2 = cabd->open(cabd,name)) || cabd->prepend(cabd, cab, cab2)) {
                  if (cab2) cabd->close(cabd, cab2);
                  break;
            }
      }

      /* load any spanning cabinets -- forwards */
      for (cab = basecab; cab->flags & MSCAB_HDR_NEXTCAB; cab = cab->nextcab) {
            if (!(name = find_cabinet_file(filename, cab->nextname))) {
                  fprintf(stderr, "%s: can't find %s\n", filename, cab->nextname);
                  break;
            }
            if (!(cab2 = cabd->open(cabd,name)) || cabd->append(cabd, cab, cab2)) {
                  if (cab2) cabd->close(cabd, cab2);
                  break;
            }
      }
}

static char *find_cabinet_file(char *origcab, char *cabname)
{
      return origcab;
}

static struct mspack_file *cabx_open(struct mspack_system *this, char *filename, int mode)
{
      struct mspack_file_p *fh;
      char *fmode;

      /* ensure that mode is one of READ, WRITE, UPDATE or APPEND */
      switch (mode) {
            case MSPACK_SYS_OPEN_READ:   fmode = "rb";  break;
            case MSPACK_SYS_OPEN_WRITE:  fmode = "wb";  break;
            case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break;
            case MSPACK_SYS_OPEN_APPEND: fmode = "ab";  break;
            default: return NULL;
      }

      if ((fh = malloc(sizeof(struct mspack_file_p)))) {
            fh->name = filename;

            /* regular file - simply attempt to open it */
            fh->regular_file = 1;
            if ((fh->fh = fopen(filename, fmode))) {
                  return (struct mspack_file *) fh;
            }

            /* error - free file handle and return NULL */
            free(fh);
      }
      return NULL;
}

static void cabx_close(struct mspack_file *file)
{
      struct mspack_file_p *this = (struct mspack_file_p *) file;
      if (this) {
            if (this->regular_file) {
                  fclose(this->fh);
            }
            free(this);
      }
}

static int cabx_read(struct mspack_file *file, void *buffer, int bytes)
{
      struct mspack_file_p *this = (struct mspack_file_p *) file;
      if (this && this->regular_file) {
            size_t count = fread(buffer, 1, (size_t) bytes, this->fh);
            if (!ferror(this->fh)) return (int) count;
      }
      return -1;
}

static int cabx_write(struct mspack_file *file, void *buffer, int bytes)
{
      struct mspack_file_p *this = (struct mspack_file_p *) file;
      if (this) {
            /* regular files and the stdout writer */
            size_t count = fwrite(buffer, 1, (size_t) bytes, this->fh);
            if (!ferror(this->fh)) return (int) count;
      }
      return -1;
}

static int cabx_seek(struct mspack_file *file, off_t offset, int mode)
{
      struct mspack_file_p *this = (struct mspack_file_p *) file;
      if (this && this->regular_file) {
            switch (mode) {
                  case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break;
                  case MSPACK_SYS_SEEK_CUR:   mode = SEEK_CUR; break;
                  case MSPACK_SYS_SEEK_END:   mode = SEEK_END; break;
                  default: return -1;
            }
            #if HAVE_FSEEKO
                  return fseeko(this->fh, offset, mode);
            #else
                  return fseek(this->fh, offset, mode);
            #endif
      }
      return -1;
}

static off_t cabx_tell(struct mspack_file *file)
{
      struct mspack_file_p *this = (struct mspack_file_p *) file;
      #if HAVE_FSEEKO
            return (this && this->regular_file) ? (off_t) ftello(this->fh) : 0;
      #else
            return (this && this->regular_file) ? (off_t) ftell(this->fh) : 0;
      #endif
}

static void cabx_msg(struct mspack_file *file, char *format, ...)
{
  va_list ap;
  if (file) {
    fprintf(stderr, "%s: ", ((struct mspack_file_p *) file)->name);
  }
  va_start(ap, format);
  vfprintf(stderr, format, ap);
  va_end(ap);
  fputc((int) '\n', stderr);
  fflush(stderr);
}

static void *cabx_alloc(struct mspack_system *this, size_t bytes)
{
      return malloc(bytes);
}

static void cabx_free(void *buffer)
{
  free(buffer);
}

static void cabx_copy(void *src, void *dest, size_t bytes)
{
      memcpy(dest, src, bytes);
}

Generated by  Doxygen 1.6.0   Back to index