diff -r 04b2ef9b1ef8 -r e5b9ccd39972 ZipArchive.st --- a/ZipArchive.st Wed Dec 05 15:38:32 2012 +0100 +++ b/ZipArchive.st Sat Dec 08 21:05:28 2012 +0100 @@ -3156,14 +3156,11 @@ entries "return a collection of fileName entries" - |names| - - names := OrderedCollection new. - - self zipMembersDo:[:zipd | - names add:(zipd fileName) - ]. - ^ names + ^ zipMembersByName keys +! + +file + ^ file ! fileSize @@ -3176,20 +3173,13 @@ members "return a collection of members" - |members| - - members := OrderedCollection new. - - self zipMembersDo:[:zipd | - members add:zipd - ]. - ^ members + ^ zipMembersByName values. ! membersMatching:aFileMatchPattern "return a collection of members which match aFileMatchPattern" - ^ self members select:[:m | aFileMatchPattern match:m fileName] + ^ self zipMembersByName select:[:m | aFileMatchPattern match:m fileName] ! name @@ -3198,6 +3188,12 @@ ^ archiveName ! +numberOfEntries + "return the number of entries in the archive" + + ^ zipMembersByName size +! + pathName "FileStream compatibility: answer the name of the underlying file - a String" @@ -3211,6 +3207,10 @@ size ^self fileSize +! + +zipMembersByName + ^ zipMembersByName ! ! !ZipArchive methodsFor:'error raising'! @@ -3259,31 +3259,30 @@ archiveName := filename name. mode := readOrWriteMode. + self openFile. mode ~~ #write ifTrue:[ |mustCloseFile| - mustCloseFile := true. - - self openFile. [ + mustCloseFile := true. self readDirectory. mustCloseFile := false. mode == #append ifTrue:[ - members := self entries collect:[:eachEntryName | self findMember:eachEntryName] thenSelect:[:eachEntry | eachEntry notNil]. + members := self zipMembersByName values. members isEmptyOrNil ifTrue:[^ self]. - maxStartPosition := (members collect:[:eachMember | eachMember fileStart]) max. - lastMember := members detect:[:eachMember | eachMember fileStart = maxStartPosition]. - - file position0Based:(startOfArchive + lastMember fileStart + lastMember compressedSize). + maxStartPosition := members maxApplying:[:eachMember | self dataStartOf:eachMember]. + lastMember := members detect:[:eachMember | eachMember dataStart = maxStartPosition]. + + file position0Based:(startOfArchive + lastMember dataStart + lastMember compressedSize). mode := #write. ]. ] ensure:[ mustCloseFile ifTrue:[self closeFile]. ]. - ] ifFalse:[ - self openFile. + ] ifFalse:[ + zipMembersByName := Dictionary new. ]. "Modified: / 31-08-2010 / 12:39:25 / sr" @@ -3294,6 +3293,7 @@ "initialize the archive to read from aPositionableStream. Obsolete - backward compatibility." + self obsoleteMethodWarning. ^ self readingFrom:aPositionableStream ! @@ -3345,6 +3345,7 @@ ] ifFalse:[ archiveName := 'internal stream'. ]. + zipMembersByName := Dictionary new. ! ! !ZipArchive methodsFor:'private'! @@ -3378,6 +3379,57 @@ ] ! +dataStartOf:zipEntry + "fetch the absolute start address of the data of a given zipEntry. + Note: extra field and extra field length may be different from that in + the central directory entry. Sow e have to fetch the local header." + + |dataStart fileHeaderStart fileNameLength extraFieldLength| + + dataStart := zipEntry dataStart. + dataStart notNil ifTrue:[ + ^ dataStart. + ]. + + fileHeaderStart := zipEntry relativeLocalHeaderOffset + startOfArchive. + (fileHeaderStart + 30) > endOfArchive ifTrue: [ + ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry end is out of the archive bounds'. + ]. + + "Now read the fileHeader: + 0 local file header signature 4 bytes (0x04034b50) + 4 version needed to extract 2 bytes + 6 general purpose bit flag 2 bytes + 8 compression method 2 bytes + 10 last mod file time 2 bytes + 12 last mod file date 2 bytes + 14 crc-32 4 bytes + 18 compressed size 4 bytes + 22 uncompressed size 4 bytes + 26 file name length (x) 2 bytes + 28 extra field length (y) 2 bytes + fixd size total len: 30 + 30 file name (variable size) + 30+x extra field (variable size) + 30+x+y data + Note: extra field and extra field length may be different from that in + the central directory entry!! + " + + file position0Based:fileHeaderStart+26. + fileNameLength := file nextUnsignedShortMSB:false. + extraFieldLength := file nextUnsignedShortMSB:false. + + dataStart := fileHeaderStart + 30 + fileNameLength + extraFieldLength. + + (dataStart + (zipEntry compressedSize)) > endOfArchive ifTrue: [ + ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry end is out of the archive bounds'. + ]. + zipEntry dataStart:dataStart. + + ^ dataStart +! + openFile |fn| @@ -3615,18 +3667,6 @@ "Modified: / 19-11-2010 / 16:23:36 / cg" ! -addMember - "add a zipMember" - - |zmemb | - - self addMember:(zmemb := ZipMember new). - ^ zmemb. - - "Created: / 29.3.1998 / 18:22:25 / cg" - "Modified: / 9.9.1998 / 20:33:32 / cg" -! - addMember:zmemb "add a zipMember" @@ -3636,6 +3676,14 @@ lastEntry next:zmemb. ]. lastEntry := zmemb. + (zipMembersByName includesKey:zmemb fileName) ifTrue:[ + "ignore duplicate entries for backward compatibility. + Argh: expecco once added wrong duplicates to the end of ets files. + The first entry is valid." + Transcript showCR:'Duplicate entry in ZIP (ignored): ', zmemb fileName. + ] ifFalse:[ + zipMembersByName at:zmemb fileName put:zmemb. + ]. ^ zmemb. "Modified: / 30.3.1998 / 17:13:20 / cg" @@ -3669,20 +3717,7 @@ findMember:name "find a zipMember by name" -"/ zipMembersByName isNil ifTrue:[ -"/ zipMembersByName := Dictionary new. -"/ self zipMembersDo:[:zipd | -"/ zipMembersByName at:(zipd fileName) put:zipd. -"/ ]. -"/ ]. -"/ ^ zipMembersByName at:name ifAbsent:nil. - - self zipMembersDo:[:zipd | - (zipd fileName = name) ifTrue:[^ zipd]. - ]. - ^ nil - - "Modified: / 18-11-2010 / 20:23:35 / cg" + ^ zipMembersByName at:name ifAbsent:[]. ! findMemberAllowForMissingTrailingSlash: name @@ -3755,12 +3790,15 @@ centralDirectory readFrom:file. "/ set file position to start of central directory - (pos0 - (centralDirectory centralDirectorySize)) < startOfArchive ifTrue: [ + (pos0 - centralDirectory centralDirectoryStartOffset - centralDirectory centralDirectorySize) < startOfArchive ifTrue: [ ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - central directory start is out of the archive bounds'. ]. + startOfArchive := pos0 - centralDirectory centralDirectoryStartOffset - centralDirectory centralDirectorySize. file position0Based:(pos0 - (centralDirectory centralDirectorySize)). + zipMembersByName := Dictionary new:centralDirectory centralDirectoryTotalNoOfEntries. + "/ read central directory entries 1 to:(centralDirectory centralDirectoryTotalNoOfEntries) do:[:i | |zipd filename_length centralFileHeaderSignature relative_offset_local_header @@ -3776,8 +3814,7 @@ ^ self. ]. - zipd := ZipMember new. - zipd readCentralDirectoryEntryFrom:file. + zipd := ZipMember new readCentralDirectoryEntryFrom:file. self addMember:zipd. ]. @@ -3870,21 +3907,13 @@ !ZipArchive methodsFor:'queries'! isValidPath: anArchivePathName - ^ self members - contains:[:aMember | - |fn| - - fn := aMember fileName. - ((fn startsWith:anArchivePathName,'/') or:[(fn = anArchivePathName)]) + self zipMembersByName + keysDo:[:eachMemberName | + ((eachMemberName startsWith:anArchivePathName,'/') + or:[eachMemberName = anArchivePathName]) ifTrue:[^ true] ]. - "/ cg: wrong - what about (isValidPath:'foo'), if there is a file named 'foobar' ?!! -"/ self members do: [:aMember| -"/ (aMember fileName startsWith:anArchivePathName) ifTrue:[ -"/ ^ true -"/ ]. -"/ ]. -"/ ^ false + ^ false. ! ! !ZipArchive methodsFor:'reading'! @@ -4038,7 +4067,7 @@ ! withPositionAndMemberFor:fileName do:aBlock - |zmemb | + |zmemb dataStart| (file isNil or:[mode ~~ #read]) ifTrue:[ ^ self error: 'ZipArchive not open for reading ...'. @@ -4046,15 +4075,9 @@ zmemb := self findMember:fileName. zmemb isNil ifTrue:[^ nil]. - (zmemb fileStart + startOfArchive) > endOfArchive ifTrue: [ - ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry start is out of the archive bounds'. - ]. - - (zmemb fileStart + startOfArchive + (zmemb compressedSize)) > endOfArchive ifTrue: [ - ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry end is out of the archive bounds'. - ]. - - aBlock value:zmemb value:(zmemb fileStart + startOfArchive) + + dataStart := self dataStartOf:zmemb. + aBlock value:zmemb value:dataStart. "Created: / 21-11-2010 / 11:51:41 / cg" ! ! @@ -4064,7 +4087,7 @@ readStreamFor:nameOfFileInArchive "open a stream on archive contents identified by nameOfFileInArchive" - |zipEntry| + |zipEntry dataStart| (file isNil or:[mode ~~ #read]) ifTrue:[ ^ OpenError raiseRequestWith:nameOfFileInArchive errorString:'ZipArchive not open for reading ...'. @@ -4075,15 +4098,8 @@ ^ OpenError raiseRequestWith:nameOfFileInArchive errorString:'ZipArchive member does not exist: '. ]. - (zipEntry fileStart + startOfArchive) > endOfArchive ifTrue: [ - ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry start is out of the archive bounds'. - ]. - - (zipEntry fileStart + startOfArchive + (zipEntry compressedSize)) > endOfArchive ifTrue: [ - ^ ZipFileFormatErrorSignal raiseRequestErrorString:' - zipEntry end is out of the archive bounds'. - ]. - - file position0Based:(zipEntry fileStart + startOfArchive). + dataStart := self dataStartOf:zipEntry. + file position0Based:dataStart. ^ (ZipReadStream zipFileStream:file zipEntry:zipEntry) zipArchive:self. @@ -4131,6 +4147,7 @@ + self obsoleteMethodWarning. ^ self addFile:aDirectoryName withContents:nil compressMethod:COMPRESSION_STORED asDirectory:true. "Modified: / 19-11-2010 / 15:38:59 / cg" @@ -4166,8 +4183,6 @@ ]. zipEntry := ZipMember new default. - self addMember:zipEntry. - theZipFileName := self validZipFileNameFrom:aFileName. zipEntry fileName: theZipFileName. @@ -4232,6 +4247,8 @@ zipEntry uncompressedSize: unCompressedDataSize. zipEntry rewriteCrcAndSizeTo:file. + self addMember:zipEntry. + file setToEnd. "Modified: / 19-11-2010 / 15:39:32 / cg" @@ -4289,7 +4306,6 @@ theCompressMethod := COMPRESSION_STORED ]. zipEntry := ZipMember new default. - self addMember:zipEntry. theZipFileName := self validZipFileNameFrom:aFileName. zipEntry fileName:theZipFileName. zipEntry fileNameLength:theZipFileName size. @@ -4349,6 +4365,7 @@ theCompressedData notNil ifTrue:[ file nextPutBytes:zipEntry compressedSize from:theCompressedData. ]. + self addMember:zipEntry. "Created: / 18-11-2010 / 19:31:10 / cg" "Modified: / 19-11-2010 / 17:47:01 / cg" @@ -4370,7 +4387,7 @@ |zipEntry curTime curDate theZipFileName theCompressMethod| - (file isNil or: [mode ~~ #write]) ifTrue: [ + (file isNil or:[mode ~~ #write]) ifTrue: [ ^ self error: 'ZipArchive not open for writing ...'. ]. @@ -4384,8 +4401,6 @@ ]. zipEntry := ZipMember new default. - self addMember:zipEntry. - theZipFileName := self validZipFileNameFrom:nameOfFileInArchive. zipEntry fileName: theZipFileName. @@ -4406,6 +4421,7 @@ file setToEnd. zipEntry writeTo:file. + self addMember:zipEntry. ^ (ZipWriteStream zipFileStream:file zipEntry:zipEntry) zipArchive:self. @@ -4644,13 +4660,8 @@ ! dataStart - "tell the file offset, where tha data of this zip entry starts" - dataStart isNil ifTrue: [ - dataStart := relativeLocalHeaderOffset - + "C_SIZEOFLOCALHEADER" 30 - + fileNameLength - + extraFieldLength. - ]. + "tell the file offset, where the data of this zip entry starts" + ^ dataStart "Created: / 29.3.1998 / 18:28:40 / cg" ! @@ -4846,13 +4857,6 @@ !ZipArchive::ZipMember methodsFor:'queries'! -fileStart - ^ self dataStart - "/ ^ relative_offset_local_header + ZipArchive LREC_SIZE + 4 + name size - - "Created: / 29.3.1998 / 19:10:57 / cg" -! - isDirectory ^ ((externalFileAttributes ? 0) bitTest:EXTERNALFILEATTRIBUTES_ISDIRECTORY) @@ -4874,15 +4878,15 @@ lastModFileTime := aStream nextUnsignedShortMSB:false. lastModFileDate := aStream nextUnsignedShortMSB:false. crc32 := aStream nextUnsignedLongMSB: false. - compressedSize := aStream nextLongMSB:false. - uncompressedSize := aStream nextLongMSB:false. + compressedSize := aStream nextUnsignedLongMSB:false. + uncompressedSize := aStream nextUnsignedLongMSB:false. fileNameLength := aStream nextUnsignedShortMSB:false. extraFieldLength := aStream nextUnsignedShortMSB:false. fileCommentLength := aStream nextUnsignedShortMSB:false. diskNumberStart := aStream nextUnsignedShortMSB:false. internalFileAttributes := aStream nextUnsignedShortMSB:false. - externalFileAttributes := aStream nextLongMSB:false. - relativeLocalHeaderOffset := aStream nextLongMSB:false. + externalFileAttributes := aStream nextUnsignedLongMSB:false. + relativeLocalHeaderOffset := aStream nextUnsignedLongMSB:false. "/ (aStream position + fileNameLength) > endOfArchive ifTrue: [ "/ ^ ZipArchive zipFileFormatErrorSignal raiseRequestErrorString:' - central directory entry out of archive bounds'. @@ -4894,7 +4898,7 @@ "/ (aStream position + extraFieldLength) > endOfArchive ifTrue: [ "/ ^ ZipArchive zipFileFormatErrorSignal raiseRequestErrorString:' - central directory entry out of archive bounds'. "/ ]. - extraField := String new:extraFieldLength. + extraField := ByteArray new:extraFieldLength. aStream nextBytes:extraFieldLength into:extraField. ]. @@ -5152,11 +5156,11 @@ !ZipArchive class methodsFor:'documentation'! version - ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.100 2012-12-05 14:38:32 stefan Exp $' + ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.101 2012-12-08 20:05:28 stefan Exp $' ! version_CVS - ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.100 2012-12-05 14:38:32 stefan Exp $' + ^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.101 2012-12-08 20:05:28 stefan Exp $' ! ! ZipArchive initialize!