"ts" <decoux / moulon.inra.fr> wrote in message
news:200103130555.f2D5tsr17121 / orsay1.moulon.inra.fr...
> >>>>> "P" == Paul C <paul_c / passtheaardvark.com> writes:
>
> P>             zipLocalEntry = ZipLocalEntry_new ( ptr_entry ) ;
>
>  What is missing is the source of ZipLocalEntry_new, to see how you create
>  your object.
>
>
> Guy Decoux

For some reason my news account doesnt seem to like me posting attachments
(or at least they dont show up at my end) and I have therefore attached the
source code for the offending module below. If anyone would like me to email
as an attachment pse let me know.

Apologies to anyone who ends up getting this multiple times

========= Original Message =========

Guy,

I am attaching the source code for the module. To compile it you need to
install the zipios++ (zipios.sourceforge.com) and run extconf.rb
with --zipios-(include|lib) set appropriately (extconf.rb is a bit of a hack
in order to detect the c++ libs properly).

I have added some instrumentation to the ZipLocalEntry_new - the debug trace
with this added is below.

Thanks, Paul

=========== DEBUG TRACE ===========

# irb --prompt xmp

require 'Zip'
    ==>true

z = Zip::ZipFile.new "test.zip"
    ==>#<Zip::ZipFile:0x816c4ec @name="test.zip">

z.entries { |e| print e.path, "\n" }   # Get Zip Directory
    (zip.cpp:279) MSG: Entering Function #ZipLocalEntry_new
    (zip.cpp:280) PTR: ConstEntryPointer ->135928464
    (zip.cpp:284) PTR: zipLocalEntry ->135469952
    (zip.cpp:289) VALUE: rbZipLocalEntry -> #<Zip::ZipEntry:0x815f738
@path="extconf.rb">
    (zip.cpp:290) MSG: Leaving Function #ZipLocalEntry_new
extconf.rb
    (zip.cpp:279) MSG: Entering Function #ZipLocalEntry_new
    (zip.cpp:280) PTR: ConstEntryPointer ->135928468
    (zip.cpp:284) PTR: zipLocalEntry ->135470080
    (zip.cpp:289) VALUE: rbZipLocalEntry -> #<Zip::ZipEntry:0x815f558
@path="Makefile">
    (zip.cpp:290) MSG: Leaving Function #ZipLocalEntry_new
Makefile
    ==>#<Zip::ZipFile:0x816c4ec @name="test.zip">

z.entry "extconf.rb"                   # OK
    (zip.cpp:403) MSG: Entering Function #ZipFile_entry
    (zip.cpp:404) VALUE: self -> #<Zip::ZipFile:0x816c4ec @name="test.zip">
    (zip.cpp:408) PTR: ZipFile ->135501760
    (zip.cpp:418) MSG: GetEntry OK
    (zip.cpp:420) PTR: ConstEntryPointer ->135871104
    (zip.cpp:279) MSG: Entering Function #ZipLocalEntry_new
    (zip.cpp:280) PTR: ConstEntryPointer ->135871104
    (zip.cpp:284) PTR: zipLocalEntry ->135489024
    (zip.cpp:289) VALUE: rbZipLocalEntry -> #<Zip::ZipEntry:0x81545cc
@path="extconf.rb">
    (zip.cpp:290) MSG: Leaving Function #ZipLocalEntry_new
    (zip.cpp:422) VALUE: zipLocalEntry -> #<Zip::ZipEntry:0x81545cc
@path="extconf.rb">
    (zip.cpp:423) MSG: Leaving Function #ZipFile_entry
    ==>#<Zip::ZipEntry:0x81545cc @path="extconf.rb">

z.entry "zzzzzzz"                      # Invalid Entry
    (zip.cpp:403) MSG: Entering Function #ZipFile_entry
    (zip.cpp:404) VALUE: self -> #<Zip::ZipFile:0x816c4ec @name="test.zip">
    (zip.cpp:408) PTR: ZipFile ->135501760
    (zip.cpp:413) MSG: GetEntry Failed
RuntimeError: Entry not found
(irb):10:in `entry'
(irb):10:in `irb_binding'

z.entry "extconf.rb"                   # Should be OK
    (zip.cpp:403) MSG: Entering Function #ZipFile_entry
    (zip.cpp:404) VALUE: self -> #<Zip::ZipFile:0x816c4ec @name="test.zip">
    (zip.cpp:408) PTR: ZipFile ->135501760
    (zip.cpp:418) MSG: GetEntry OK
    (zip.cpp:420) PTR: ConstEntryPointer ->136008400
    (zip.cpp:279) MSG: Entering Function #ZipLocalEntry_new
    (zip.cpp:280) PTR: ConstEntryPointer ->136008400
    (zip.cpp:284) PTR: zipLocalEntry ->135126848
    (zip.cpp:289) VALUE: rbZipLocalEntry -> #<Zip::ZipEntry:0x814d2f4
@path="extconf.rb">
    (zip.cpp:290) MSG: Leaving Function #ZipLocalEntry_new
    (zip.cpp:422) VALUE: zipLocalEntry -> #<Zip::ZipEntry:0x814d2f4
@path="extconf.rb">
    (zip.cpp:423) MSG: Leaving Function #ZipFile_entry
(irb):12: [BUG] Segmentation fault
ruby 1.6.3 (2001-03-07) [i386-netbsd1.5]
Abort (core dumped)



====== extconf.rb =====
require 'mkmf'

CPP.gsub! /g?cc/, "c++"
LINK.gsub! /g?cc/, "c++"

dir_config ( "zipios" ) ;

if ( have_header ( "zipios++/zipfile.h" ) and
     have_library ( "gcc", "__pure_virtual" ) and
     have_library ( "zipios", "FListScannerwrap" ) and
     have_library ( "z", "inflate" ) and
     have_library ( "stdc++", "cin" ) )

    $defs[-1].tr! ( "++", "" )

    create_makefile ( "Zip" )
end

====== zip.cpp =====
//
// zip.cpp
//
// Ruby extension to use the Zipios++ Zip file reader library
// (http://zipios.sourceforge.com)
//
// Module
//
//      Zip - Module Namespace
//
// Classes
//
//      Zip::ZipFile - Zip File
//      Zip::ZipEntry - Zip File Entry
//
// Methods
//
// Zip::ZipFile
// ============
//
//      Zip::ZipFile#new (fileName, [fileOffset=0]) -> ZipFile
//
//          Constructor - open zip file
//
//      Zip::ZipFile#openEmbeddedZipFile (fileName) -> ZipFile
//
//          Constructor - open zIp file appended to another file
//
//          File must have Zip file offset encoded in last 4 bytes
//          in Zip file (little endian byte order) eg. using
//          'appendzip' program from zipios++ distribution
//
//      ZipFile#inspect -> String
//
//      ZipFile#name -> String
//
//          Return Zip file name
//
//      ZipFile#items -> Number
//
//          Returns number of items in ZipFile
//
//      ZipFile#entry -> ZipEntry List
//
//          Return ZipEntry list (contents) for ZipFile
//          In block context call block once for each entry
//
//      ZipFile#entries ( pathString ) -> ZipEntry
//
//          Return ZipEntry for given path
//          Raises RuntimeError is entry not found
//
//      ZipFile#getData ( ZipEntry | pathString ) -> String
//
//          Returns uncompressed data for ZipEntry or pathString
//          Raises RuntimeError is entry not found
//
// Zip::ZipEntry
// =============
//
// Note: There is no direct sconstructor for Zip::ZipEntry - must
// created through ZipFile#entry or ZipFile#entries
//
//      ZipEntry#inspect -> String
//
//      (The following ZipEntry methods are largely accessors for the
//      ZipEntry file attributes)
//
//      ZipEntry#comment -> String
//      ZipEntry#compressed_size -> Number
//      ZipEntry#crc -> Number (this doesnt look right??)
//      ZipEntry#size -> Number
//      ZipEntry#time -> Number (format not defined - see Zipios docs)
//      ZipEntry#isValid? -> Boolean
//      ZipEntry#isDirectory? -> Boolean
//      ZipEntry#path
//      ZipEntry#filename
//
// BUGS/TODO
//
//      - Fix ZipEntry#entry coredump
//      - Utility to create embedded Zipfiles
//      - Convert ZipEntry#time to Ruby date
//      - ZipStream support (code removed until I work out how to fix)
//      - Use Ruby IO object in ZipFile#new (may need complete rewrite)
//
//      On reflection it might have been easier to write simple C
//      extension supporting the Zip file structure and reuse the Ruby
//      Zlib module for decompression
//
// LICENSE
//
//      The zipios++ library is released under the GNU LGPL
//      (http://www.gnu.org/copyleft/lesser.html)
//
//      Ruby is releaed under a combined GPL/Artistic lisence
//      (http://www.ruby-lang.org/en/LICENSE.txt)
//
//      This code is freely distributable under whatever the combined
//      restrictions of the two above licenses might be
//
// Author: Paul Charvavarti (paul_c / passtheaardvark.com)
//
// $Id: zip.cpp,v 1.1.1.1 2001/03/13 17:43:52 paulc Exp $
//
// $Log: zip.cpp,v $
// Revision 1.1.1.1  2001/03/13 17:43:52  paulc
// Initial Import
//
//


extern "C" {
#include "ruby.h"
}

#include "zipios++/zipfile.h"
#include "zipios++/zipinputstream.h"
#include "zipios++/ziphead.h"

//
// C++ object wrapper macros stolen from SWIG
//

#define VALUEFUNC(f) ((VALUE (*)(...))f)
#define VOIDFUNC(f) ((void (*)(...))f)

#define Wrap_Class(klass,mark,free,ptr) \
    ((ptr)?Data_Wrap_Struct(klass,VOIDFUNC(mark),VOIDFUNC(free),ptr):Qnil)

#define Get_Class(val,rtype,ctype,ptr) {\
    if (NIL_P(val)) ptr = NULL;\
    else {\
        if (!rb_obj_is_kind_of(val, rtype))\
            rb_raise(rb_eTypeError, "Wrong argument type");\
        Data_Get_Struct(val, ctype, ptr);\
        if (!ptr) rb_raise(rb_eRuntimeError, "Class already released");\
    }\
}

#define Wrap_ZipFile(klass,ptr) \
    Wrap_Class(klass,0,free_ZipFile,ptr)
#define Get_ZipFile(val,ptr) \
    Get_Class(val,cZipFile,ZipFile,ptr)
#define Wrap_ZipLocalEntry(klass,ptr) \
    Wrap_Class(klass,0,free_ZipLocalEntry,ptr)
#define Get_ZipLocalEntry(val,ptr) \
    Get_Class(val,cZipLocalEntry,ZipLocalEntry,ptr)

//
// Debug macros - compile with -DDEBUG to enable
//

#define VALUE2STR(value) STR2CSTR(rb_funcall(value,rb_intern("to_s"),0))

#ifdef DEBUG
#define DEBUG_MSG(msg) printf ( "    (%s:%d) MSG: %s\n", \
                __FILE__, __LINE__, msg ) ;
#define DEBUG_NUM(msg,num) printf ( "    (%s:%d) NUM: %s ->%d\n", \
                __FILE__, __LINE__, msg, num ) ;
#define DEBUG_STR(msg,str) printf ( "    (%s:%d) NUM: %s ->%s\n", \
                __FILE__, __LINE__, msg, str) ;
#define DEBUG_PTR(msg,ptr) printf ( "    (%s:%d) PTR: %s ->%d\n", \
                __FILE__, __LINE__, msg,ptr ) ;
#define DEBUG_VALUE(msg,value) printf ( "    (%s:%d) VALUE: %s -> %s\n", \
                __FILE__, __LINE__, msg, \
                VALUE2STR(rb_funcall(value,rb_intern("inspect"),0)))
#define DEBUG_METHOD(msg,value,method) printf ( "    (%s:%d) METHOD: %s ->
%s\n", \
                __FILE__, __LINE__, msg, \
                VALUE2STR(rb_funcall(value,rb_intern(#method),0)))
#else
#define DEBUG_MSG(msg) ;
#define DEBUG_NUM(msg,num) ;
#define DEBUG_NUM(msg,str) ;
#define DEBUG_PTR(msg,ptr) ;
#define DEBUG_VALUE(msg,value) ;
#define DEBUG_METHOD(msg,value,method) ;
#endif

//
// C++ Namespace
//

using namespace zipios ;

//
// Function prototypes
//

static VALUE ZipLocalEntry_inspect ( VALUE self ) ;
static VALUE ZipLocalEntry_getProperties ( VALUE rbZipLocalEntry ) ;
VALUE ZipLocalEntry_new ( ConstEntryPointer *entry ) ;

static VALUE getDataFromInputStream ( istream *is, int length ) ;
static VALUE ZipFile_getData ( VALUE self, VALUE name ) ;
static VALUE ZipFile_entry ( VALUE self, VALUE name ) ;
static VALUE ZipFile_entries ( VALUE self ) ;
static VALUE ZipFile_inspect ( VALUE self ) ;
VALUE ZipFile_embedded ( VALUE self, VALUE filename ) ;
VALUE ZipFile_new ( int argc, VALUE *argv, VALUE self ) ;

//
// Global Types
//


VALUE mZip;
VALUE cZipFile;
VALUE cZipLocalEntry;

//
// Object 'free' functions used by Data_Wrap_Struct
//

void free_ZipFile(ZipFile *arg) { delete arg ; }
void free_ZipLocalEntry(ZipLocalEntry *arg) { delete arg ; }

//
// ZipEntry#inspect
//

static VALUE ZipLocalEntry_inspect ( VALUE self ) {

    char *buffer ;

    buffer = ALLOCA_N ( char, 1024 ) ;

    snprintf ( buffer, 1024, "#<Zip::ZipEntry:0x%lx @path=\"%s\">",
                self, STR2CSTR(rb_iv_get(self, "@path") ))  ;

    return rb_str_new2(buffer) ;
}

//
// ZipEntry#getProperties
//
// Extract properties from ZipLocalEntry abd write to instance vars
// Method not exposed ro Runy (called by ZipLocalEntry_new)
//

static VALUE ZipLocalEntry_getProperties ( VALUE rbZipLocalEntry ) {

    ZipLocalEntry *zipLocalEntry ;
    Get_ZipLocalEntry ( rbZipLocalEntry, zipLocalEntry ) ;

    rb_iv_set ( rbZipLocalEntry, "@comment",
            rb_str_new2(zipLocalEntry->getComment().c_str()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@compressed_size",
            INT2NUM(zipLocalEntry->getCompressedSize()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@crc",
            INT2NUM(zipLocalEntry->getCrc()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@size",
            INT2NUM(zipLocalEntry->getSize()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@time",
            INT2NUM(zipLocalEntry->getTime()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@isValid",
            zipLocalEntry->isValid() ? Qtrue : Qfalse ) ;
    rb_iv_set ( rbZipLocalEntry, "@isDirectory",
            zipLocalEntry->isDirectory() ? Qtrue : Qfalse ) ;
    rb_iv_set ( rbZipLocalEntry, "@path",
            rb_str_new2(zipLocalEntry->getName().c_str()) ) ;
    rb_iv_set ( rbZipLocalEntry, "@filename",
            rb_str_new2(zipLocalEntry->getFileName().c_str()) ) ;

    return rbZipLocalEntry ;
}

//
// ZipEntry constructor
//
// Passed ConstEntryPointer to C++ ZipLocalEntry object
//

VALUE ZipLocalEntry_new ( ConstEntryPointer *entry ) {

    VALUE rbZipLocalEntry = Qnil ;
    ZipLocalEntry *zipLocalEntry ;

    DEBUG_MSG("Entering Function #ZipLocalEntry_new") ;
    DEBUG_PTR("ConstEntryPointer",entry) ;

    zipLocalEntry = (ZipLocalEntry *) (*entry)->clone() ;

    DEBUG_PTR("zipLocalEntry",zipLocalEntry) ;

    rbZipLocalEntry =  Wrap_ZipLocalEntry ( cZipLocalEntry, zipLocalEntry )
;
    ZipLocalEntry_getProperties ( rbZipLocalEntry ) ;

    DEBUG_VALUE("rbZipLocalEntry",rbZipLocalEntry) ;
    DEBUG_MSG("Leaving Function #ZipLocalEntry_new") ;

    return rbZipLocalEntry ;
}

//
// Extract ZipEntry contents to Ruby string
//

static VALUE getDataFromInputStream ( istream *is, int length ) {

    VALUE data ;
    char *buffer ;

    DEBUG_MSG("Entering Function getDataFromInputStream") ;
    DEBUG_PTR("Istream",is) ;
    DEBUG_NUM("Length",length) ;


    if ( (buffer = (char *) calloc(length,sizeof(char))) == NULL ) {
        rb_raise ( rb_eRuntimeError, "could not allocate buffery memory" ) ;
    }

    try {
        is->read ( buffer,length ) ;
    }
    catch ( exception &excp ) {
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }

    data = rb_str_new ( buffer, length ) ;

    DEBUG_METHOD("Got Buffer Length",data,length) ;
    DEBUG_MSG("Leaving Function getDataFromInputStream") ;

    return data ;
}

//
// ZipFile#GetData
//
// Extract uncompressed data for entry 'name' - 'name' can either be a
// ZipEntry value or an explicit path
//
// Calls getDataFromInputStream to extract data
//

static VALUE ZipFile_getData ( VALUE self, VALUE entry ) {

    VALUE rbZipEntry ;
    ZipFile *zf ;
    istream *is ;
    ZipLocalEntry *zipentry ;
    string s_name ;

    DEBUG_MSG("Entering Function #ZipFile_getData") ;
    DEBUG_VALUE("self",self);
    DEBUG_VALUE("entry",entry);

    Get_ZipFile ( self, zf ) ;

    DEBUG_PTR("ZipFile",zf) ;

    try {
        if ( TYPE(entry) == T_STRING ) {
            rbZipEntry = ZipFile_entry ( self, entry) ;
            s_name = STR2CSTR(entry) ;
        }
        else if ( rb_obj_is_kind_of(entry,cZipLocalEntry) ) {
            rbZipEntry = entry ;
            s_name = STR2CSTR(rb_funcall(entry,rb_intern("path"),0)) ;
        }
        else {
            rb_raise ( rb_eTypeError, "Must be String or ZipEntry" ) ;
        }

        DEBUG_PTR("RbZipEntry",rbZipEntry) ;

        Get_ZipLocalEntry ( rbZipEntry, zipentry ) ;

        DEBUG_PTR("ZipEntry",zipentry) ;

        if ( zipentry->isDirectory() ) {
            rb_raise ( rb_eRuntimeError, "Entry is directory" ) ;
        }
        if ( ( is = zf->getInputStream(s_name)) == 0 ) {
            rb_raise ( rb_eRuntimeError, "Could not get istream" ) ;
        }

        DEBUG_PTR("Istream",is) ;

        return getDataFromInputStream ( is, zipentry->getSize() ) ;
    }
    catch ( exception &excp ) {
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }
}

//
// ZipFile#entry
//
// Creates ZipEntry object for given entry name
//

static VALUE ZipFile_entry ( VALUE self, VALUE name ) {

    VALUE zipLocalEntry = Qnil ;
    ConstEntryPointer entry ;
    ConstEntryPointer *ptr_entry ;
    ZipFile *zf ;

    const string sname = STR2CSTR(name) ;

    DEBUG_MSG("Entering Function #ZipFile_entry") ;
    DEBUG_VALUE("self",self) ;

    Get_ZipFile ( self, zf ) ;

    DEBUG_PTR("ZipFile",zf) ;

    try {
        if ( (entry = zf->getEntry(sname)) == 0 ) {

            DEBUG_MSG("GetEntry Failed") ;
            rb_raise ( rb_eRuntimeError, "Entry not found" ) ;
        }
        else {

            DEBUG_MSG("GetEntry OK") ;
            ptr_entry = new ConstEntryPointer ( entry ) ;
            DEBUG_PTR("ConstEntryPointer",ptr_entry) ;
            zipLocalEntry = ZipLocalEntry_new ( ptr_entry ) ;
            DEBUG_VALUE("zipLocalEntry",zipLocalEntry) ;
            DEBUG_MSG("Leaving Function #ZipFile_entry") ;

            return zipLocalEntry ;
        }
    }
    catch ( exception &excp ) {

        DEBUG_MSG("Exception") ;
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }
}

//
// ZipFile#entries
//
// Creates either list of ZipEntry objects (ZipFile contents) or
// in block context calls block once for each entry passing
// ZipEntry object
//

static VALUE ZipFile_entries ( VALUE self ) {

    VALUE entries_ary ;
    vector< ConstEntryPointer > entries ;
    vector< ConstEntryPointer >::iterator it ;

    ZipFile *zf ;
    Get_ZipFile ( self, zf ) ;

    try {
        entries = zf->entries() ;
    }
    catch ( exception &excp ) {
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }

    if ( rb_block_given_p() ) {
        for( it = entries.begin() ; it != entries.end() ; it++) {
            rb_yield ( ZipLocalEntry_new(it) ) ;
        }
        return self ;
    }
    else {
        entries_ary = rb_ary_new () ;
        for( it = entries.begin() ; it != entries.end() ; it++) {
            rb_ary_push ( entries_ary, ZipLocalEntry_new(it) ) ;
        }
        return entries_ary ;
    }
}

//
// ZipFile#size
//
// Returns number of elements in Zipfile
//
//
// static VALUE ZipFile_size ( VALUE self ) {
//
//     ZipFile *zf ;
//     Get_ZipFile ( self, zf ) ;
//     return INT2FIX(zf->size()) ;
// }
//
//
// ZipFile#getName
//
// Returns archive name
//
//
// static VALUE ZipFile_getName ( VALUE self ) {
//
//     ZipFile *zf ;
//     Get_ZipFile ( self, zf ) ;
//     return rb_str_new2(zf->getName().c_str()) ;
// }


//
// ZipFile#inspect
//

static VALUE ZipFile_inspect ( VALUE self ) {

    char *buffer ;

    buffer = ALLOCA_N ( char, 1024 ) ;

    snprintf ( buffer, 1024, "#<Zip::ZipFile:0x%lx @name=\"%s\">",
                self, STR2CSTR(rb_iv_get(self, "@name") ))  ;

    return rb_str_new2(buffer) ;
}

//
// ZipFile#openEmbeddedZipFile
//
// Opens embedded zip file generated according to format in
// zipio++ package (offset stored in last 4 bytes of file)
//

VALUE ZipFile_embedded ( VALUE self, VALUE filename ) {

    ZipFile *zipFile, embeddedZipFile ;
    VALUE rbZipFile = Qnil ;
    VALUE stat ;

    stat = rb_eval_string ( "File" ) ;
    if ( rb_funcall(stat,rb_intern("readable?"),1,filename) == Qfalse ) {
        rb_raise ( rb_eRuntimeError, "Could not open file \"%s\"",
                    STR2CSTR(filename) ) ;
    }

    try {
        embeddedZipFile = ZipFile::openEmbeddedZipFile(STR2CSTR(filename)) ;
        zipFile = new ZipFile ( embeddedZipFile ) ;
    }
    catch ( exception &excp ) {
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }

    rbZipFile = Wrap_ZipFile ( self, zipFile ) ;

    return rbZipFile ;
}

//
// ZipFile#new (constructor)
//
//

VALUE ZipFile_new ( int argc, VALUE *argv, VALUE self ) {

    ZipFile zipFile ;
    ZipFile *zipFilePtr ;
    VALUE rbZipFile = Qnil ;
    VALUE stat = Qnil ;
    VALUE filename = Qnil ;
    VALUE offset = Qnil ;
    int c_offset = 0 ;

    rb_scan_args ( argc, argv, "11", &filename, &offset ) ;

    c_offset = ( offset == Qnil ) ? 0 : NUM2INT(offset) ;

    // We have to check the file exists befoew we pass on to the zipios
    // constuctor as it does not generate an expection on error

    stat = rb_eval_string ( "File" ) ;  // Must be a better way to do this

    if ( rb_funcall(stat,rb_intern("readable?"),1,filename) == Qfalse ) {
        rb_raise ( rb_eRuntimeError, "Could not open file \"%s\"",
                    STR2CSTR(filename) ) ;
    }

    try {
        zipFile = ZipFile (STR2CSTR(filename), c_offset) ;
    }
    catch ( exception &excp ) {
        rb_raise ( rb_eRuntimeError, excp.what() ) ;
    }

    // Allocate object on heap once local object successfully created
    // (Seems to prevents memory leak in exception handler)

    zipFilePtr = new ZipFile( zipFile ) ;

    rbZipFile = Wrap_ZipFile ( self, zipFilePtr ) ;
    rb_iv_set ( rbZipFile, "@name",
rb_str_new2(zipFilePtr->getName().c_str()) ) ;
    rb_iv_set ( rbZipFile, "@items", INT2FIX(zipFilePtr->size()) ) ;

    return rbZipFile ;
}

//
// Define methids to Ruby
//

extern "C"
void Init_Zip() {

    mZip = rb_define_module ( "Zip" ) ;

    // ZipFile

    cZipFile = rb_define_class_under ( mZip, "ZipFile",  rb_cObject ) ;

    rb_define_singleton_method ( cZipFile, "new", ZipFile_new, -1 ) ;
    rb_define_singleton_method ( cZipFile, "openEmbeddedZipFile",
ZipFile_embedded, 1 ) ;

    rb_define_method ( cZipFile, "inspect", ZipFile_inspect, 0 ) ;
    rb_define_method ( cZipFile, "entries", ZipFile_entries, 0 ) ;
    rb_define_method ( cZipFile, "entry", ZipFile_entry, 1 ) ;
    rb_define_method ( cZipFile, "getData", ZipFile_getData, 1 ) ;

    rb_define_attr ( cZipFile, "name", 1, 0 ) ;
    rb_define_attr ( cZipFile, "items", 1, 0 ) ;

    // ZipEntry

    cZipLocalEntry = rb_define_class_under ( mZip, "ZipEntry",  rb_cObject )
;

    rb_define_method ( cZipLocalEntry, "inspect", ZipLocalEntry_inspect, 0 )
;

    rb_define_attr ( cZipLocalEntry, "comment", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "compressed_size", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "crc", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "size", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "time", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "isValid?", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "isDirectory?", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "path", 1, 0 ) ;
    rb_define_attr ( cZipLocalEntry, "filename", 1, 0 ) ;

}