// $Id: zip.cpp,v 1.22 2002/07/30 16:30:03 ericb Exp $ // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://ibm.com/developerworks/opensource/jikes. // Copyright (C) 1996, 1998, 1999, 2000, 2001 International Business // Machines Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // #include "zip.h" #include "control.h" #include "symbol.h" #ifdef HAVE_JIKES_NAMESPACE namespace Jikes { // Open namespace Jikes block #endif //************************************************************ // // The ZipFile methods follow // //************************************************************ #ifdef UNIX_FILE_SYSTEM int (*ZipFile::uncompress_file[10]) (FILE *, char *, long) = { UncompressFile0, UncompressFile1, UncompressFile2, UncompressFile3, UncompressFile4, UncompressFile5, UncompressFile6, UncompressFile7, UncompressFile8, UncompressFile9 }; inline u1 ZipFile::GetU1() { return getc(zipfile); } inline void ZipFile::Skip(u4 length) { for (u4 i = 0; i < length; i++) getc(zipfile); } #elif defined(WIN32_FILE_SYSTEM) // ! UNIX_FILE_SYSTEM int (*ZipFile::uncompress_file[10]) (char *, char *, long) = { UncompressFile0, UncompressFile1, UncompressFile2, UncompressFile3, UncompressFile4, UncompressFile5, UncompressFile6, UncompressFile7, UncompressFile8, UncompressFile9 }; inline u1 ZipFile::GetU1() { return *file_buffer++; } inline void ZipFile::Skip(u4 length) { file_buffer += length; } #endif // WIN32_FILE_SYSTEM inline u2 ZipFile::GetU2() { u4 val = GetU1(); val |= (((u4) GetU1()) << 8); return val; } inline u4 ZipFile::GetU4() { u4 val = GetU1(); val |= (((u4) GetU1()) << 8); val |= (((u4) GetU1()) << 16); val |= (((u4) GetU1()) << 24); return val; } ZipFile::ZipFile(FileSymbol *file_symbol) : buffer(NULL) { Zip *zip = file_symbol -> Zipfile(); assert(zip -> IsValid()); #ifdef UNIX_FILE_SYSTEM zipfile = zip -> zipfile; int rc = fseek(zipfile, file_symbol -> offset, SEEK_SET); assert(rc == 0); #elif defined(WIN32_FILE_SYSTEM) file_buffer = &zip -> zipbuffer[file_symbol -> offset]; #endif Skip(8); // u4 magic = GetU4(); // u2 version_needed_to_extract = GetU2(); // u2 general_purpose_bits = GetU2(); u2 compression_method = GetU2(); Skip(16); // u2 time = GetU2(); // u2 date = GetU2(); // u4 crc32 = GetU4(); // u4 compressed_size = GetU4(); // u4 uncompressed_size = GetU4(); u2 filename_length = GetU2(); u2 extra_field_length = GetU2(); Skip(filename_length + extra_field_length); #ifdef UNIX_FILE_SYSTEM this -> buffer = new char[file_symbol -> uncompressed_size]; if (! uncompress_file[compression_method < 9 ? compression_method : 9](zipfile, this -> buffer, file_symbol -> uncompressed_size)) { delete [] this -> buffer; this -> buffer = NULL; } #elif defined(WIN32_FILE_SYSTEM) if (compression_method > 0) { this -> buffer = new char[file_symbol -> uncompressed_size]; if (! uncompress_file[compression_method < 9 ? compression_method : 9](file_buffer, this -> buffer, file_symbol -> uncompressed_size)) { delete [] this -> buffer; this -> buffer = NULL; this -> file_buffer = NULL; } } #endif } ZipFile::~ZipFile() { delete [] buffer; } //******************************************************************** // // The Zip methods follow: // //******************************************************************** inline u1 Zip::GetU1() { return *buffer_ptr++; } inline u2 Zip::GetU2() { u4 val = GetU1(); val |= (((u4) GetU1()) << 8); return val; } inline u4 Zip::GetU4() { u4 val = GetU1(); val |= (((u4) GetU1()) << 8); val |= (((u4) GetU1()) << 16); val |= (((u4) GetU1()) << 24); return val; } inline void Zip::Skip(u4 length) { buffer_ptr += length; } inline DirectorySymbol *Zip::ProcessSubdirectoryEntries(DirectorySymbol *directory_symbol, char *name, int name_length) { wchar_t *directory_name = new wchar_t[name_length]; for (int start = 0, end; start < name_length; start = end + 1) { end = start; for (int i = 0; end < name_length && name[end] != U_SLASH; i++, end++) directory_name[i] = name[end]; NameSymbol *name_symbol = control.FindOrInsertName(directory_name, end - start); DirectorySymbol *subdirectory_symbol = directory_symbol -> FindDirectorySymbol(name_symbol); if (! subdirectory_symbol) subdirectory_symbol = directory_symbol -> InsertDirectorySymbol(name_symbol, false); directory_symbol = subdirectory_symbol; } delete [] directory_name; return directory_symbol; } inline NameSymbol *Zip::ProcessFilename(char *name, int name_length) { wchar_t *input_filename = new wchar_t[name_length]; for (int i = 0; i < name_length; i++) input_filename[i] = name[i]; NameSymbol *name_symbol = control.FindOrInsertName(input_filename, name_length); delete [] input_filename; return name_symbol; } inline void Zip::ProcessDirectoryEntry() { Skip(8); // u2 version_made_by = GetU2(); // u2 version_needed_to_extract = GetU2(); // u2 general_purpose_bits = GetU2(); // u2 compression_method = GetU2(); u2 last_mod_file_time = GetU2(); u2 last_mod_file_date = GetU2(); Skip(4); // u4 crc32 = GetU4(); Skip(4); // u4 compressed_size = GetU4(); u4 uncompressed_size = GetU4(); u2 file_name_length = GetU2(); u2 extra_field_length = GetU2(); u2 file_comment_length = GetU2(); Skip(8); // u2 disk_number_start = GetU2(); // u2 internal_file_attributes = GetU2(); // u4 external_file_attributes = GetU4(); u4 relative_offset_of_local_header = GetU4(); u4 date_time = ((u4) last_mod_file_date) << 16 | last_mod_file_time; char *name = buffer_ptr; Skip(file_name_length + extra_field_length + file_comment_length); // // Note that we need to process all subdirectory entries // that appear in the zip file, and not just the ones that // contain java and class files. Recall that in java the // dot notation is used in specifying a package. Therefore, // in processing a qualified-name that represents a package, // we need to recognize each name as a subpackage. E.g., // when processing "java.lang", we need to recognize "java" // as a package before looking for "lang"... // start at the "." directory. DirectorySymbol *directory_symbol = root_directory; // -1 to remove last '/' if (name[file_name_length - 1] == U_SLASH) ProcessSubdirectoryEntries(directory_symbol, name, file_name_length - 1); else { bool java_file = (file_name_length >= FileSymbol::java_suffix_length && FileSymbol::IsJavaSuffix(&name[file_name_length - FileSymbol::java_suffix_length])), class_file = (file_name_length >= FileSymbol::class_suffix_length && FileSymbol::IsClassSuffix(&name[file_name_length - FileSymbol::class_suffix_length])); if (java_file || class_file) { int name_length = file_name_length - (java_file ? FileSymbol::java_suffix_length : FileSymbol::class_suffix_length); int i; for (i = name_length - 1; i >= 0 && name[i] != U_SLASH; i--) ; if (i > 0) // directory specified? directory_symbol = ProcessSubdirectoryEntries(directory_symbol, name, i); NameSymbol *name_symbol = ProcessFilename(&name[i + 1], name_length - (i + 1)); // // Search for a file of that name in the directory. // If one is not found, then insert ... Otherwise, // either a class file of that name was previously // processed and now we found a java file with the // same name or vice-versa... In that case keep // (or replace with) the file with the most recent // date stamp. // FileSymbol *file_symbol = directory_symbol -> FindFileSymbol(name_symbol); if (! file_symbol) { file_symbol = directory_symbol -> InsertFileSymbol(name_symbol); file_symbol -> directory_symbol = directory_symbol; if (java_file) file_symbol -> SetJava(); else file_symbol -> SetClassOnly(); file_symbol -> uncompressed_size = uncompressed_size; file_symbol -> offset = relative_offset_of_local_header; file_symbol -> date_time = date_time; } else if (file_symbol -> date_time < date_time) { if (java_file) file_symbol -> SetJava(); else file_symbol -> SetClass(); file_symbol -> uncompressed_size = uncompressed_size; file_symbol -> offset = relative_offset_of_local_header; file_symbol -> date_time = date_time; } } } } Zip::Zip(Control &control_, char *zipfile_name) : control(control_), magic(0), zipbuffer(NULL) { #ifdef UNIX_FILE_SYSTEM zipfile = SystemFopen(zipfile_name, "rb"); if (zipfile) { int rc = fseek(zipfile, -END_SIZE, SEEK_END); if (rc == 0) { zipbuffer = new char[END_SIZE]; buffer_ptr = zipbuffer; SystemFread(buffer_ptr, sizeof(char), END_SIZE, zipfile); magic = GetU4(); } } #elif defined(WIN32_FILE_SYSTEM) zipfile = CreateFile(zipfile_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (zipfile != INVALID_HANDLE_VALUE) { mapfile = CreateFileMapping(zipfile, NULL, PAGE_READONLY, 0, 0, NULL); zipbuffer = (mapfile == INVALID_HANDLE_VALUE ? NULL : (char *) MapViewOfFile(mapfile, FILE_MAP_READ, 0, 0, 0) ); if (zipbuffer) { buffer_ptr = &zipbuffer[GetFileSize(zipfile, NULL) - END_SIZE]; magic = GetU4(); } } #endif // The following was posted to the dev list, but was just // too good to not put in here, the next person to have to // deal with this crap will appreciate it. -=Chris // // From: Mo DeJong // // Ode to a zip file: // // I can't read it forwards // I can't read it backwards // I must know where to begin // so I need to look in the middle // to find the middle, I must know the end // but I don't know where that is, so I guess // // ------------------------------------------------- // This may or may not be a valid zip file. The zip file might have // a file comment so we can't be sure where the END header is located. // We check for the LOC header at byte 0 to make sure this is a valid // zip file and then scan over the file backwards in search of the // END header. if (zipbuffer != NULL && ! IsValid()) { u4 sig = 0; #ifdef UNIX_FILE_SYSTEM int res = fseek(zipfile, 0, SEEK_SET); assert(res == 0); char *tmpbuffer = new char[LOC_SIZE]; buffer_ptr = tmpbuffer; SystemFread(buffer_ptr, sizeof(char), LOC_SIZE, zipfile); sig = GetU4(); delete [] tmpbuffer; buffer_ptr = NULL; if (sig == LOC_SIG) { int block_size = 8192; tmpbuffer = new char[block_size]; char *holdbuffer = new char[8]; char *index_ptr; res = fseek(zipfile, 0, SEEK_END); assert(res == 0); long zip_file_size = ftell(zipfile); int num_loops = zip_file_size / block_size; magic = 0; for (; magic == 0 && num_loops >= 0 ; num_loops--) { if ((ftell(zipfile) - block_size) < 0) { block_size = ftell(zipfile); res = fseek(zipfile, 0L, SEEK_SET); } else { res = fseek(zipfile, -block_size, SEEK_CUR); } assert(res == 0); SystemFread(tmpbuffer, sizeof(char), block_size, zipfile); res = fseek(zipfile, -block_size, SEEK_CUR); // undo fread assert(res == 0); for (index_ptr = tmpbuffer + block_size - 1; index_ptr >= tmpbuffer; index_ptr--) { if (*index_ptr == 'P') { // Check for header signature that spans buffer int span = (tmpbuffer + block_size) - index_ptr; if (span < 4) { memmove(holdbuffer+span, holdbuffer, 3); memmove(holdbuffer, index_ptr, span); buffer_ptr = holdbuffer; } else { buffer_ptr = index_ptr; } sig = GetU4(); if (sig == END_SIG) { // Found the END header, put it in zipbuffer. buffer_ptr = zipbuffer; fseek(zipfile, block_size-span, SEEK_CUR); SystemFread(buffer_ptr, sizeof(char), END_SIZE, zipfile); magic = GetU4(); break; } } } // Copy first 3 bytes into holdbuffer in case sig spans holdbuffer[0] = tmpbuffer[0]; holdbuffer[1] = tmpbuffer[1]; holdbuffer[2] = tmpbuffer[2]; } delete [] tmpbuffer; delete [] holdbuffer; } #elif defined(WIN32_FILE_SYSTEM) buffer_ptr = &zipbuffer[0]; sig = GetU4(); if (sig == LOC_SIG) { buffer_ptr = &zipbuffer[GetFileSize(zipfile, NULL) - END_SIZE]; for ( ; buffer_ptr >= zipbuffer; buffer_ptr--) { if (*buffer_ptr == 'P') { sig = GetU4(); if (sig == END_SIG) { magic = sig; break; } else buffer_ptr -= 4; } } } #endif } ReadDirectory(); } Zip::~Zip() { #ifdef UNIX_FILE_SYSTEM delete [] zipbuffer; if (zipfile) fclose(zipfile); #elif defined(WIN32_FILE_SYSTEM) if (zipfile != INVALID_HANDLE_VALUE) { if (mapfile != INVALID_HANDLE_VALUE) { if (zipbuffer) UnmapViewOfFile(zipbuffer); CloseHandle(mapfile); } CloseHandle(zipfile); } #endif delete root_directory; } // // Upon successful termination of this function, IsValid() should yield true. // Each CEN header would have been read so the magic number would get reset // when the END header is again read. // void Zip::ReadDirectory() { // Not a sourcepath (since we don't read java files from zip files) root_directory = new DirectorySymbol(control.dot_name_symbol, NULL, false); if (IsValid()) { Skip(8); // u2 number_of_this_disk = GetU2(); // u2 number_of_the_disk_with_the_star = GetU2(); // u2 start_of_the_central_directory = GetU2(); // u2 total_number_of_entries_in_the_ = GetU2(); u4 central_directory_size = GetU4(); #ifdef UNIX_FILE_SYSTEM u4 central_directory_offset = GetU4(); Skip(2); // u2 comment_length = GetU2(); int rc = fseek(zipfile, central_directory_offset, SEEK_SET); assert(rc == 0); delete [] zipbuffer; zipbuffer = new char[central_directory_size + END_SIZE]; buffer_ptr = zipbuffer; SystemFread(buffer_ptr, sizeof(char), central_directory_size + END_SIZE, zipfile); #elif defined(WIN32_FILE_SYSTEM) Skip(6); // u4 central_directory_offset = GetU4(); // u2 comment_length = GetU2(); buffer_ptr -= END_SIZE + central_directory_size; #endif for (magic = GetU4(); magic == CEN_SIG; magic = GetU4()) ProcessDirectoryEntry(); } } #ifdef HAVE_JIKES_NAMESPACE } // Close namespace Jikes block #endif