ZipArchive.st
author Claus Gittinger <cg@exept.de>
Mon, 30 Mar 1998 13:27:18 +0200
changeset 622 6d06b9f7d7ed
child 624 251f10395e0b
permissions -rw-r--r--
*** empty log message ***

'From Smalltalk/X, Version:3.4.3 on 29-mar-1998 at 9:59:17 pm'                  !

Object subclass:#ZipArchive
	instanceVariableNames:'file archiveName firstEntry lastEntry'
	classVariableNames:'ECREC_SIZE LREC_SIZE CREC_SIZE SIZE_CENTRAL_DIRECTORY
		TOTAL_ENTRIES_CENTRAL_DIR C_COMPRESSED_SIZE
		C_RELATIVE_OFFSET_LOCAL_HEADER C_FILENAME_LENGTH
		C_UNCOMPRESSED_SIZE ZipFileFormatErrorSignal COMPR_STORED
		COMPR_SHRUNK COMPR_REDUCED1 COMPR_REDUCED2 COMPR_REDUCED3
		COMPR_REDUCED4 COMPR_IMPLODED COMPR_TOKENIZED COMPR_DEFLATED'
	poolDictionaries:''
	category:'System-Support-FileFormats'
!

Object subclass:#ZipMember
	instanceVariableNames:'next relative_offset_local_header compressed_size
		uncompressed_size name crc32 compression_method'
	classVariableNames:''
	poolDictionaries:''
	privateIn:ZipArchive
!

!ZipArchive class methodsFor:'documentation'!

history

    "Created: / 29.3.1998 / 17:45:14 / cg"
    "Created: #oldFileNamed: / 29.3.1998 / 17:46:09 / cg"
    "Created: #newFileNamed: / 29.3.1998 / 17:46:16 / cg"
    "Created: #name: / 29.3.1998 / 17:46:47 / cg"
    "Created: #name:mode: / 29.3.1998 / 17:47:40 / cg"
    "Modified: #name:mode: / 29.3.1998 / 17:48:05 / cg"
    "Created: #readDirectory / 29.3.1998 / 17:59:21 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:01:54 / cg"
    "Created: #initialize / 29.3.1998 / 18:02:52 / cg"
    "Modified: #initialize / 29.3.1998 / 18:03:51 / cg"
    "Modified: #initialize / 29.3.1998 / 18:04:19 / cg"
    "Modified: #initialize / 29.3.1998 / 18:05:44 / cg"
    "Modified: #initialize / 29.3.1998 / 18:06:00 / cg"
    "Modified: #initialize / 29.3.1998 / 18:06:20 / cg"
    "Created: #readu2 / 29.3.1998 / 18:08:17 / cg"
    "Modified: #readu2 / 29.3.1998 / 18:09:05 / cg"
    "Created: #read4 / 29.3.1998 / 18:10:25 / cg"
    "Modified: #readu2 / 29.3.1998 / 18:10:53 / cg"
    "Modified: #read4 / 29.3.1998 / 18:11:03 / cg"
    "Created: #addMember / 29.3.1998 / 18:22:26 / cg"
    "Modified: #addMember / 29.3.1998 / 18:22:47 / cg"
    "Modified: #name:mode: / 29.3.1998 / 18:23:26 / cg"
    "Modified: #initialize / 29.3.1998 / 18:24:37 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:25:05 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:25:14 / cg"
    "Deleted: #name: / 29.3.1998 / 18:25:45 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:26:38 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:27:05 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:33:22 / cg"
    "Modified: #readDirectory / 29.3.1998 / 18:34:14 / cg"
    "Created: #extract: / 29.3.1998 / 19:09:51 / cg"
    "Created: #LREC_SIZE / 29.3.1998 / 19:11:21 / cg"
    "Created: #findMember: / 29.3.1998 / 19:13:21 / cg"
    "Modified: #findMember: / 29.3.1998 / 19:13:55 / cg"
    "Created: #entries / 29.3.1998 / 19:14:32 / cg"
    "Created: #zipMembersDo: / 29.3.1998 / 19:15:16 / cg"
    "Modified: #findMember: / 29.3.1998 / 19:15:39 / cg"
    "Modified: #entries / 29.3.1998 / 19:16:21 / cg"
    "Modified: #name:mode: / 29.3.1998 / 19:16:35 / cg"
    "Modified: #name:mode: / 29.3.1998 / 19:57:01 / cg"
    "Modified: #readDirectory / 29.3.1998 / 20:02:00 / cg"
    "Modified: #readDirectory / 29.3.1998 / 20:02:16 / cg"
    "Modified: #entries / 29.3.1998 / 20:08:39 / cg"
    "Modified: #extract: / 29.3.1998 / 20:08:49 / cg"
    "Created: #members / 29.3.1998 / 20:09:28 / cg"
    "Modified: #members / 29.3.1998 / 20:10:23 / cg"
    "Modified: #extract: / 29.3.1998 / 20:12:13 / cg"
    "Created: #decode:method: / 29.3.1998 / 20:14:46 / cg"
    "Modified: #initialize / 29.3.1998 / 20:17:19 / cg"
    "Modified: #decode:method: / 29.3.1998 / 20:20:21 / cg"
! !

!ZipArchive class methodsFor:'instance creation'!

newFileNamed:name
    ^ self new name:name mode:#write

    "Created: / 29.3.1998 / 17:46:16 / cg"
!

oldFileNamed:name
    ^ self new name:name mode:#read

    "Created: / 29.3.1998 / 17:46:09 / cg"
! !

!ZipArchive class methodsFor:'class initialization'!

initialize
    ECREC_SIZE := 18.
    LREC_SIZE := 26.
    CREC_SIZE := 42.

    TOTAL_ENTRIES_CENTRAL_DIR := 10.
    SIZE_CENTRAL_DIRECTORY := 12.

    C_COMPRESSED_SIZE := 16.
    C_UNCOMPRESSED_SIZE := 20.
    C_FILENAME_LENGTH := 24.
    C_RELATIVE_OFFSET_LOCAL_HEADER := 38.

    "/ compression methods 
    COMPR_STORED          :=  0.    
    COMPR_SHRUNK          :=  1.
    COMPR_REDUCED1        :=  2.
    COMPR_REDUCED2        :=  3.
    COMPR_REDUCED3        :=  4.
    COMPR_REDUCED4        :=  5.
    COMPR_IMPLODED        :=  6.
    COMPR_TOKENIZED       :=  7.
    COMPR_DEFLATED        :=  8.

    ZipFileFormatErrorSignal := Signal new.

    "
     self initialize
    "

    "Modified: / 29.3.1998 / 20:17:18 / cg"
! !

!ZipArchive class methodsFor:'constants'!

LREC_SIZE
    ^ LREC_SIZE

    "Created: / 29.3.1998 / 19:11:20 / cg"
! !

!ZipArchive methodsFor:'accessing'!

entries
    "return a collection of fileName entries"

    |names|

    names := OrderedCollection new.

    self zipMembersDo:[:zipd |
        names add:(zipd name)
    ].
    ^ names

    "
     (ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') entries
    "

    "Modified: / 29.3.1998 / 20:08:38 / cg"
!

extract:fileName
    |zmemb rawContents|

    zmemb := self findMember:fileName.
    file position:(zmemb fileStart + 1).
    rawContents := file nextBytes:(zmemb compressed_size).
    ^ self decode:rawContents method:(zmemb compression_method)

    "
     (ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') extract:'java/io/UTFDataFormatException.class'
    "

    "Modified: / 29.3.1998 / 20:12:12 / cg"
!

members
    "return a collection of members"

    |members|

    members := OrderedCollection new.

    self zipMembersDo:[:zipd |
        members add:zipd
    ].
    ^ members

    "
     (ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') members
    "

    "Created: / 29.3.1998 / 20:09:27 / cg"
    "Modified: / 29.3.1998 / 20:10:21 / cg"
! !

!ZipArchive methodsFor:'private'!

name:nm mode:mode
    archiveName := nm.
    mode == #read ifTrue:[
        file := nm asFilename readStream.
        self readDirectory.
    ] ifFalse:[
        file := nm asFilename writeStream
    ]

    "
     ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip'
     (ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') entries
     (ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') extract:'java/io/UTFDataFormatException.class'
    "

    "Modified: / 29.3.1998 / 19:57:00 / cg"
! !

!ZipArchive methodsFor:'private - decompression'!

decode:rawBytes method:compressionMethod
    compressionMethod == COMPR_STORED ifTrue:[
        "/ uncompressed
        ^ rawBytes
    ].

    compressionMethod == COMPR_SHRUNK ifTrue:[
        self error:'unsupported compression method: SHRUNK'.
        ^ nil
    ].
    compressionMethod == COMPR_REDUCED1 ifTrue:[
        self error:'unsupported compression method: REDUCED1'.
        ^ nil
    ].
    compressionMethod == COMPR_REDUCED2 ifTrue:[
        self error:'unsupported compression method: REDUCED2'.
        ^ nil
    ].
    compressionMethod == COMPR_REDUCED3 ifTrue:[
        self error:'unsupported compression method: REDUCED3'.
        ^ nil
    ].
    compressionMethod == COMPR_REDUCED4 ifTrue:[
        self error:'unsupported compression method: REDUCED4'.
        ^ nil
    ].
    compressionMethod == COMPR_IMPLODED ifTrue:[
        self error:'unsupported compression method: IMPLODED'.
        ^ nil
    ].
    compressionMethod == COMPR_TOKENIZED ifTrue:[
        self error:'unsupported compression method: TOKENIZED'.
        ^ nil
    ].
    compressionMethod == COMPR_DEFLATED ifTrue:[
        self error:'unsupported compression method: DEFLATED'.
        ^ nil
    ].

    self error:'unsupported compression method'.
    ^ nil

    "Created: / 29.3.1998 / 20:14:45 / cg"
    "Modified: / 29.3.1998 / 20:20:20 / cg"
! !

!ZipArchive methodsFor:'private - directory stuff'!

addMember
    |zmemb |

    zmemb := ZipMember new.
    (firstEntry == nil) ifTrue:[
        firstEntry := zmemb
    ] ifFalse:[ 
        lastEntry next:zmemb.
    ].
    lastEntry := zmemb.
    ^ zmemb.

    "Created: / 29.3.1998 / 18:22:25 / cg"
    "Modified: / 29.3.1998 / 18:22:47 / cg"
!

findMember:name
    self zipMembersDo:[:zipd |
        (zipd name = name) ifTrue:[^ zipd].
    ].
    ^ nil

    "Modified: / 29.3.1998 / 19:15:38 / cg"
!

readDirectory
    |size count_in dir_size|

    size := file fileSize.
    (size == 0) ifTrue:[
        count_in := 0.
        ^ self
    ].

    (size < (ECREC_SIZE+4)) ifTrue:[
        ^ ZipFileFormatErrorSignal raiseWith:'zipfile too short'.
    ].

    file position:(size - (ECREC_SIZE+4) + 1).
    ((file next ~~ $P)
    or:[file next ~~ $K
    or:[file next ~~ (Character value:8r005)
    or:[file next ~~ (Character value:8r006)]]]) ifTrue:[
        ^ ZipFileFormatErrorSignal raiseWith:'not a valid zipfile'.
    ].

    file skip: (TOTAL_ENTRIES_CENTRAL_DIR - 4).

    count_in := self readu2.     "/ Get TOTAL_ENTRIES_CENTRAL_DIR
    dir_size := self read4.      "/ Get SIZE_CENTRAL_DIRECTORY
    file position:(size - (dir_size + ECREC_SIZE+4) + 1).

    0 to:(count_in-1) do:[:i |
        |zipd filename_length|

        zipd := self addMember.
        file skip:(4+C_COMPRESSED_SIZE-4-2-2-2).
        zipd compression_method:(self readu2)."/ Get compression method
        self readu2.                         "/ skip last_mod_file_time
        self readu2.                         "/ skip last_mod_file_date
        zipd crc32:(self read4).             "/ Get crc32
        zipd compressed_size:(self read4).   "/ Get C_COMPRESSED_SIZE
        zipd uncompressed_size:(self read4). "/ Get C_UNCOMPRESSED_SIZE
        filename_length := self readu2.      "/ Get C_FILENAME_LENGTH
        file skip:(C_RELATIVE_OFFSET_LOCAL_HEADER-(C_FILENAME_LENGTH+2)).
        zipd relative_offset_local_header:(self read4).
        zipd name:(String new:filename_length).
        file nextBytes:filename_length into:(zipd name).
    ]

    "Modified: / 29.3.1998 / 20:02:15 / cg"
!

zipMembersDo:aBlock
    |zipd|

    zipd := firstEntry.
    [zipd notNil] whileTrue:[
        aBlock value:zipd.
        zipd := zipd next
    ].

    "Modified: / 29.3.1998 / 19:13:54 / cg"
    "Created: / 29.3.1998 / 19:15:15 / cg"
! !

!ZipArchive methodsFor:'private - io'!

read4
    ^ file nextLongMSB:false

    "Created: / 29.3.1998 / 18:10:25 / cg"
    "Modified: / 29.3.1998 / 18:11:03 / cg"
!

readu2
    ^ file nextUnsignedShortMSB:false

    "Modified: / 29.3.1998 / 18:10:53 / cg"
! !

!ZipArchive::ZipMember methodsFor:'accessing'!

compressed_size
    "return the value of the instance variable 'compressed_size' (automatically generated)"

    ^ compressed_size

    "Created: / 29.3.1998 / 18:28:03 / cg"
!

compressed_size:something
    "set the value of the instance variable 'compressed_size' (automatically generated)"

    compressed_size := something.

    "Created: / 29.3.1998 / 18:28:03 / cg"
!

compression_method
    "return the value of the instance variable 'compression_method' (automatically generated)"

    ^ compression_method

    "Created: / 29.3.1998 / 20:02:57 / cg"
!

compression_method:something
    "set the value of the instance variable 'compression_method' (automatically generated)"

    compression_method := something.

    "Created: / 29.3.1998 / 20:02:57 / cg"
!

crc32
    "return the value of the instance variable 'crc32' (automatically generated)"

    ^ crc32

    "Created: / 29.3.1998 / 20:03:00 / cg"
!

crc32:something
    "set the value of the instance variable 'crc32' (automatically generated)"

    crc32 := something.

    "Created: / 29.3.1998 / 20:03:00 / cg"
!

name
    "return the value of the instance variable 'name' (automatically generated)"

    ^ name

    "Created: / 29.3.1998 / 18:29:22 / cg"
!

name:something
    "set the value of the instance variable 'name' (automatically generated)"

    name := something.

    "Created: / 29.3.1998 / 18:29:22 / cg"
!

next
    "return the value of the instance variable 'next' (automatically generated)"

    ^ next

    "Created: / 29.3.1998 / 18:29:42 / cg"
!

next:something
    "set the value of the instance variable 'next' (automatically generated)"

    next := something.

    "Created: / 29.3.1998 / 18:29:42 / cg"
!

relative_offset_local_header
    "return the value of the instance variable 'relative_offset_local_header' (automatically generated)"

    ^ relative_offset_local_header

    "Created: / 29.3.1998 / 18:28:40 / cg"
!

relative_offset_local_header:something
    "set the value of the instance variable 'relative_offset_local_header' (automatically generated)"

    relative_offset_local_header := something.

    "Created: / 29.3.1998 / 18:28:40 / cg"
!

uncompressed_size
    "return the value of the instance variable 'uncompressed_size' (automatically generated)"

    ^ uncompressed_size

    "Created: / 29.3.1998 / 18:28:21 / cg"
!

uncompressed_size:something
    "set the value of the instance variable 'uncompressed_size' (automatically generated)"

    uncompressed_size := something.

    "Created: / 29.3.1998 / 18:28:21 / cg"
! !

!ZipArchive::ZipMember methodsFor:'printing & storing'!

displayString
    ^ 'ZipMember(' , name , ')'

    "Created: / 29.3.1998 / 20:10:07 / cg"
! !

!ZipArchive::ZipMember methodsFor:'queries'!

fileStart
    ^ relative_offset_local_header + ZipArchive LREC_SIZE + 4 + name size

    "Created: / 29.3.1998 / 19:10:57 / cg"
! !

ZipArchive initialize!