diff options
author | Reid Spencer <rspencer@reidspencer.com> | 2004-11-06 08:51:45 +0000 |
---|---|---|
committer | Reid Spencer <rspencer@reidspencer.com> | 2004-11-06 08:51:45 +0000 |
commit | 362cbf0d747154f2617f2cabe20187235dcaba60 (patch) | |
tree | 8a9eece69846f269562360e75f8f19c8612ab107 | |
parent | 07adb2836b8aa7a3872e33c285958f5937662b50 (diff) | |
download | external_llvm-362cbf0d747154f2617f2cabe20187235dcaba60.zip external_llvm-362cbf0d747154f2617f2cabe20187235dcaba60.tar.gz external_llvm-362cbf0d747154f2617f2cabe20187235dcaba60.tar.bz2 |
First kinda/sorta working version of the Archive library. Reading is not
yet supported but writing works. Way too early to review this. More to come
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@17499 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Archive/Archive.cpp | 24 | ||||
-rw-r--r-- | lib/Archive/ArchiveInternals.h | 158 | ||||
-rw-r--r-- | lib/Archive/ArchiveWriter.cpp | 284 | ||||
-rw-r--r-- | lib/Bytecode/Archive/Archive.cpp | 24 | ||||
-rw-r--r-- | lib/Bytecode/Archive/ArchiveInternals.h | 158 | ||||
-rw-r--r-- | lib/Bytecode/Archive/ArchiveWriter.cpp | 284 |
6 files changed, 932 insertions, 0 deletions
diff --git a/lib/Archive/Archive.cpp b/lib/Archive/Archive.cpp new file mode 100644 index 0000000..e9fcd2e --- /dev/null +++ b/lib/Archive/Archive.cpp @@ -0,0 +1,24 @@ +//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up standard unix archive files (.a) containing LLVM bytecode. +// +//===----------------------------------------------------------------------===// + +#include "ArchiveInternals.h" + +using namespace llvm; + +Archive::Archive() { +} + +Archive::~Archive() { +} + +// vim: sw=2 ai diff --git a/lib/Archive/ArchiveInternals.h b/lib/Archive/ArchiveInternals.h new file mode 100644 index 0000000..dde8358 --- /dev/null +++ b/lib/Archive/ArchiveInternals.h @@ -0,0 +1,158 @@ +//===-- lib/Bytecode/ArchiveInternals.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Internal implementation header for LLVM Archive files. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_BYTECODE_ARCHIVEINTERNALS_H +#define LIB_BYTECODE_ARCHIVEINTERNALS_H + +#include "llvm/Bytecode/Archive.h" +#include "llvm/System/TimeValue.h" + +#define ARFILE_MAGIC "!<arch>\n" ///< magic string +#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1) ///< length of magic string +#define ARFILE_SYMTAB_NAME "/" ///< name of symtab entry +#define ARFILE_STRTAB_NAME "//" ///< name of strtab entry +#define ARFILE_PAD '\n' ///< inter-file align padding + +namespace llvm { + + /// The ArchiveMemberHeader structure is used internally for bytecode archives. + /// The header precedes each file member in the archive. This structure is + /// defined using character arrays for direct and correct interpretation + /// regardless of the endianess of the machine that produced it. + /// @brief Archive File Member Header + class ArchiveMemberHeader { + public: + void init() { + memset(name,' ',16); + memset(date,' ',12); + memset(uid,' ',6); + memset(gid,' ',6); + memset(mode,' ',8); + memset(size,' ',10); + fmag[0] = '`'; + fmag[1] = '\n'; + } + void setDate( int secondsSinceEpoch = 0 ) { + if (secondsSinceEpoch == 0) { + sys::TimeValue tv = sys::TimeValue::now(); + uint64_t secs; uint32_t nanos; + tv.GetTimespecTime(secs,nanos); + secondsSinceEpoch = (int) secs; + } + char buffer[20]; + sprintf(buffer,"%d", secondsSinceEpoch); + memcpy(date,buffer,strlen(buffer)); + } + + void setSize(size_t sz) { + char buffer[20]; + sprintf(buffer, "%u", (unsigned)sz); + memcpy(size,buffer,strlen(buffer)); + } + + void setMode(int m) { + char buffer[20]; + sprintf(buffer, "%o", m); + memcpy(mode,buffer,strlen(buffer)); + } + + void setUid(unsigned u) { + char buffer[20]; + sprintf(buffer, "%u", u); + memcpy(uid,buffer,strlen(buffer)); + } + + void setGid(unsigned g) { + char buffer[20]; + sprintf(buffer, "%u", g); + memcpy(gid,buffer,strlen(buffer)); + } + + bool setName(const std::string& nm) { + if (nm.length() > 0 && nm.length() <= 16) { + memcpy(name,nm.c_str(),nm.length()); + for (int i = nm.length()+1; i < 16; i++ ) name[i] = ' '; + return true; + } + return false; + } + + private: + char name[16]; ///< Name of the file member. The filename is terminated with '/' + ///< and blanks. The empty name (/ and 15 blanks) is for the + ///< symbol table. The special name "//" and 15 blanks is for + ///< the string table, used for long file names. It must be + ///< first in the archive. + char date[12]; ///< File date, decimal seconds since Epoch + char uid[6]; ///< user id in ASCII decimal + char gid[6]; ///< group id in ASCII decimal + char mode[8]; ///< file mode in ASCII octal + char size[10]; ///< file size in ASCII decimal + char fmag[2]; ///< Always contains ARFILE_MAGIC_TERMINATOR + + }; + + /// The ArchiveInternals class is used to hold the content of the archive + /// while it is in memory. It also provides the bulk of the implementation for + /// the llvm:Archive class's interface. + class Archive::ArchiveInternals { + /// @name Types + /// @{ + public: + typedef std::vector<std::string> StrTab; + + /// This structure holds information for one member in the archive. It is + /// used temporarily while the contents of the archive are being + /// determined. + struct MemberInfo { + MemberInfo() {} + sys::Path path; + std::string name; + sys::Path::StatusInfo status; + StrTab symbols; + unsigned offset; + }; + + /// @} + /// @name Methods + /// @{ + public: + /// @brief Add a file member to the archive. + void addFileMember( + const sys::Path& path, ///< The path to the file to be added + const std::string& name, ///< The name for the member + const StrTab* syms = 0 ///< The symbol table of the member + ); + + /// @brief Write the accumulated archive information to an archive file + void writeArchive(); + void writeMember(const MemberInfo& member,std::ofstream& ARFile); + void writeSymbolTable(std::ofstream& ARFile); + void writeInteger(int num, std::ofstream& ARFile); + + /// @} + /// @name Data + /// @{ + private: + friend class Archive; ///< Parent class is a friend + sys::Path fname; ///< Path to the archive file + std::vector<MemberInfo> members; ///< Info about member files + Archive::SymTab* symtab; ///< User's symbol table + + /// @} + }; +} + +#endif + +// vim: sw=2 ai diff --git a/lib/Archive/ArchiveWriter.cpp b/lib/Archive/ArchiveWriter.cpp new file mode 100644 index 0000000..e3e2df4 --- /dev/null +++ b/lib/Archive/ArchiveWriter.cpp @@ -0,0 +1,284 @@ +//===-- ArchiveWriter.cpp - LLVM archive writing --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencerand is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up standard unix archive files (.a) containing LLVM bytecode. +// +//===----------------------------------------------------------------------===// + +#include "ArchiveInternals.h" +#include "llvm/Module.h" +#include "llvm/Bytecode/Reader.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/System/MappedFile.h" +#include <fstream> +#include <iostream> + +using namespace llvm; + +Archive* +Archive::CreateEmpty(const sys::Path& Filename) { + Archive* result = new Archive; + Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals; + impl->fname = Filename; + return result; +} + +Archive* +Archive::CreateFromFiles( + const sys::Path& Filename, + const PathList& Files, + const std::string& StripName +) { + Archive* result = new Archive; + Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals; + impl->fname = Filename; + + try { + size_t strip_len = StripName.length(); + for (PathList::const_iterator P = Files.begin(), E = Files.end(); P != E ;++P) + { + if (P->readable()) { + std::string name(P->get()); + if (strip_len > 0 && StripName == name.substr(0,strip_len)) { + name.erase(0,strip_len); + } + if (P->isBytecodeFile()) { + std::vector<std::string> syms; + if (!GetBytecodeSymbols(*P, syms)) + throw std::string("Can not get symbols from: ") + P->get(); + impl->addFileMember(*P, name, &syms); + } else { + impl->addFileMember(*P, name); + } + } + else + throw std::string("Can not read: ") + P->get(); + } + + // Now that we've collected everything, write the archive + impl->writeArchive(); + + } catch(...) { + delete impl; + result->impl = 0; + delete result; + throw; + } + + return result; +} + +void +Archive::ArchiveInternals::addFileMember( + const sys::Path& filePath, + const std::string& memberName, + const StrTab* symbols +) { + MemberInfo info; + info.path = filePath; + info.name = memberName; + filePath.getStatusInfo(info.status); + if (symbols) + info.symbols = *symbols; + info.offset = 0; + members.push_back(info); +} + +void +Archive::ArchiveInternals::writeInteger(int num, std::ofstream& ARFile) { + char buff[4]; + buff[0] = (num >> 24) & 255; + buff[1] = (num >> 16) & 255; + buff[2] = (num >> 8) & 255; + buff[3] = num & 255; + ARFile.write(buff, sizeof(buff)); +} + +void +Archive::ArchiveInternals::writeSymbolTable( std::ofstream& ARFile ) { + + // Compute the number of symbols in the symbol table and the + // total byte size of the string pool. While we're traversing, + // build the string pool for supporting long file names. Also, + // build the table of file offsets for the symbol table and + // the + typedef std::map<std::string,unsigned> SymbolMap; + StrTab stringPool; + SymbolMap symbolTable; + std::vector<unsigned> fileOffsets; + std::string symTabStrings; + unsigned fileOffset = 0; + unsigned spOffset = 0; + unsigned numSymbols = 0; + unsigned numSymBytes = 0; + for (unsigned i = 0; i < members.size(); i++ ) { + MemberInfo& mi = members[i]; + StrTab& syms = mi.symbols; + size_t numSym = syms.size(); + numSymbols += numSym; + for (unsigned j = 0; j < numSym; j++ ) { + numSymBytes += syms[j].size() + 1; + symbolTable[syms[i]] = i; + } + if (mi.name.length() > 15 || std::string::npos != mi.name.find('/')) { + stringPool.push_back(mi.name + "/\n"); + mi.name = std::string("/") + utostr(spOffset); + spOffset += mi.name.length() + 2; + } else if (mi.name[mi.name.length()-1] != '/') { + mi.name += "/"; + } + fileOffsets.push_back(fileOffset); + fileOffset += sizeof(ArchiveMemberHeader) + mi.status.fileSize; + } + + + // Compute the size of the symbol table file member + unsigned symTabSize = 0; + if (numSymbols != 0) + symTabSize = + sizeof(ArchiveMemberHeader) + // Size of the file header + 4 + // Size of "number of entries" + (4 * numSymbols) + // Size of member file indices + numSymBytes; // Size of the string table + + // Compute the size of the string pool + unsigned strPoolSize = 0; + if (spOffset != 0 ) + strPoolSize = + sizeof(ArchiveMemberHeader) + // Size of the file header + spOffset; // Number of bytes in the string pool + + // Compute the byte index offset created by symbol table and string pool + unsigned firstFileOffset = symTabSize + strPoolSize; + + // Create header for symbol table. This must be first if there is + // a symbol table and must have a special name. + if ( symTabSize > 0 ) { + ArchiveMemberHeader Hdr; + Hdr.init(); + + // Name of symbol table is '/ ' but "" is passed in + // because the setName method always terminates with a / + Hdr.setName(ARFILE_SYMTAB_NAME); + Hdr.setDate(); + Hdr.setSize(symTabSize - sizeof(ArchiveMemberHeader)); + Hdr.setMode(0); + Hdr.setUid(0); + Hdr.setGid(0); + + // Write header to archive file + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + // Write the number of entries in the symbol table + this->writeInteger(numSymbols, ARFile); + + // Write the file offset indices for each symbol and build the + // symbol table string pool + std::string symTabStrPool; + symTabStrPool.reserve(256 * 1024); // Reserve 256KBytes for symbols + for (SymbolMap::iterator I = symbolTable.begin(), E = symbolTable.end(); + I != E; ++I ) { + this->writeInteger(firstFileOffset + fileOffsets[I->second], ARFile); + symTabStrPool += I->first; + symTabStrPool += "\0"; + } + + // Write the symbol table's string pool + ARFile.write(symTabStrPool.data(), symTabStrPool.size()); + } + + //============== DONE WITH SYMBOL TABLE + + if (strPoolSize > 0) { + // Initialize the header for the string pool + ArchiveMemberHeader Hdr; + Hdr.init(); + Hdr.setName(ARFILE_STRTAB_NAME); + Hdr.setDate(); + Hdr.setSize(spOffset); + Hdr.setMode(0); + Hdr.setUid(0); + Hdr.setGid(0); + + // Write the string pool header + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + // Write the string pool + for (unsigned i = 0; i < stringPool.size(); i++) { + ARFile.write(stringPool[i].data(), stringPool[i].size()); + } + } +} + +void +Archive::ArchiveInternals::writeMember( + const MemberInfo& member, + std::ofstream& ARFile +) { + + // Map the file into memory. We do this early for two reasons. First, + // if there's any kind of error, we want to know about it. Second, we + // want to ensure we're using the most recent size for this file. + sys::MappedFile mFile(member.path); + mFile.map(); + + // Header for the archive member + ArchiveMemberHeader Hdr; + Hdr.init(); + + // Set the name. If its longer than 15 chars, it will have already + // been reduced by the writeSymbolTable. + Hdr.setName(member.name); + + // Set the other header members + Hdr.setSize( mFile.size() ); + Hdr.setMode( member.status.mode); + Hdr.setUid ( member.status.user); + Hdr.setGid ( member.status.group); + Hdr.setDate( member.status.modTime.ToPosixTime() ); + + // Write header to archive file + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + //write to archive file + ARFile.write(mFile.charBase(),mFile.size()); + + mFile.unmap(); +} + +void +Archive::ArchiveInternals::writeArchive() { + + // Create archive file for output. + std::ofstream ArchiveFile(fname.get().c_str()); + + // Check for errors opening or creating archive file. + if ( !ArchiveFile.is_open() || ArchiveFile.bad() ) { + throw std::string("Error opening archive file: ") + fname.get(); + } + + // Write magic string to archive. + ArchiveFile << ARFILE_MAGIC; + + // Write the symbol table and string pool + writeSymbolTable(ArchiveFile); + + //Loop over all member files, and add to the archive. + for ( unsigned i = 0; i < members.size(); ++i) { + if(ArchiveFile.tellp() % 2 != 0) + ArchiveFile << ARFILE_PAD; + writeMember(members[i],ArchiveFile); + } + + //Close archive file. + ArchiveFile.close(); +} + +// vim: sw=2 ai diff --git a/lib/Bytecode/Archive/Archive.cpp b/lib/Bytecode/Archive/Archive.cpp new file mode 100644 index 0000000..e9fcd2e --- /dev/null +++ b/lib/Bytecode/Archive/Archive.cpp @@ -0,0 +1,24 @@ +//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up standard unix archive files (.a) containing LLVM bytecode. +// +//===----------------------------------------------------------------------===// + +#include "ArchiveInternals.h" + +using namespace llvm; + +Archive::Archive() { +} + +Archive::~Archive() { +} + +// vim: sw=2 ai diff --git a/lib/Bytecode/Archive/ArchiveInternals.h b/lib/Bytecode/Archive/ArchiveInternals.h new file mode 100644 index 0000000..dde8358 --- /dev/null +++ b/lib/Bytecode/Archive/ArchiveInternals.h @@ -0,0 +1,158 @@ +//===-- lib/Bytecode/ArchiveInternals.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Internal implementation header for LLVM Archive files. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_BYTECODE_ARCHIVEINTERNALS_H +#define LIB_BYTECODE_ARCHIVEINTERNALS_H + +#include "llvm/Bytecode/Archive.h" +#include "llvm/System/TimeValue.h" + +#define ARFILE_MAGIC "!<arch>\n" ///< magic string +#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1) ///< length of magic string +#define ARFILE_SYMTAB_NAME "/" ///< name of symtab entry +#define ARFILE_STRTAB_NAME "//" ///< name of strtab entry +#define ARFILE_PAD '\n' ///< inter-file align padding + +namespace llvm { + + /// The ArchiveMemberHeader structure is used internally for bytecode archives. + /// The header precedes each file member in the archive. This structure is + /// defined using character arrays for direct and correct interpretation + /// regardless of the endianess of the machine that produced it. + /// @brief Archive File Member Header + class ArchiveMemberHeader { + public: + void init() { + memset(name,' ',16); + memset(date,' ',12); + memset(uid,' ',6); + memset(gid,' ',6); + memset(mode,' ',8); + memset(size,' ',10); + fmag[0] = '`'; + fmag[1] = '\n'; + } + void setDate( int secondsSinceEpoch = 0 ) { + if (secondsSinceEpoch == 0) { + sys::TimeValue tv = sys::TimeValue::now(); + uint64_t secs; uint32_t nanos; + tv.GetTimespecTime(secs,nanos); + secondsSinceEpoch = (int) secs; + } + char buffer[20]; + sprintf(buffer,"%d", secondsSinceEpoch); + memcpy(date,buffer,strlen(buffer)); + } + + void setSize(size_t sz) { + char buffer[20]; + sprintf(buffer, "%u", (unsigned)sz); + memcpy(size,buffer,strlen(buffer)); + } + + void setMode(int m) { + char buffer[20]; + sprintf(buffer, "%o", m); + memcpy(mode,buffer,strlen(buffer)); + } + + void setUid(unsigned u) { + char buffer[20]; + sprintf(buffer, "%u", u); + memcpy(uid,buffer,strlen(buffer)); + } + + void setGid(unsigned g) { + char buffer[20]; + sprintf(buffer, "%u", g); + memcpy(gid,buffer,strlen(buffer)); + } + + bool setName(const std::string& nm) { + if (nm.length() > 0 && nm.length() <= 16) { + memcpy(name,nm.c_str(),nm.length()); + for (int i = nm.length()+1; i < 16; i++ ) name[i] = ' '; + return true; + } + return false; + } + + private: + char name[16]; ///< Name of the file member. The filename is terminated with '/' + ///< and blanks. The empty name (/ and 15 blanks) is for the + ///< symbol table. The special name "//" and 15 blanks is for + ///< the string table, used for long file names. It must be + ///< first in the archive. + char date[12]; ///< File date, decimal seconds since Epoch + char uid[6]; ///< user id in ASCII decimal + char gid[6]; ///< group id in ASCII decimal + char mode[8]; ///< file mode in ASCII octal + char size[10]; ///< file size in ASCII decimal + char fmag[2]; ///< Always contains ARFILE_MAGIC_TERMINATOR + + }; + + /// The ArchiveInternals class is used to hold the content of the archive + /// while it is in memory. It also provides the bulk of the implementation for + /// the llvm:Archive class's interface. + class Archive::ArchiveInternals { + /// @name Types + /// @{ + public: + typedef std::vector<std::string> StrTab; + + /// This structure holds information for one member in the archive. It is + /// used temporarily while the contents of the archive are being + /// determined. + struct MemberInfo { + MemberInfo() {} + sys::Path path; + std::string name; + sys::Path::StatusInfo status; + StrTab symbols; + unsigned offset; + }; + + /// @} + /// @name Methods + /// @{ + public: + /// @brief Add a file member to the archive. + void addFileMember( + const sys::Path& path, ///< The path to the file to be added + const std::string& name, ///< The name for the member + const StrTab* syms = 0 ///< The symbol table of the member + ); + + /// @brief Write the accumulated archive information to an archive file + void writeArchive(); + void writeMember(const MemberInfo& member,std::ofstream& ARFile); + void writeSymbolTable(std::ofstream& ARFile); + void writeInteger(int num, std::ofstream& ARFile); + + /// @} + /// @name Data + /// @{ + private: + friend class Archive; ///< Parent class is a friend + sys::Path fname; ///< Path to the archive file + std::vector<MemberInfo> members; ///< Info about member files + Archive::SymTab* symtab; ///< User's symbol table + + /// @} + }; +} + +#endif + +// vim: sw=2 ai diff --git a/lib/Bytecode/Archive/ArchiveWriter.cpp b/lib/Bytecode/Archive/ArchiveWriter.cpp new file mode 100644 index 0000000..e3e2df4 --- /dev/null +++ b/lib/Bytecode/Archive/ArchiveWriter.cpp @@ -0,0 +1,284 @@ +//===-- ArchiveWriter.cpp - LLVM archive writing --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencerand is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Builds up standard unix archive files (.a) containing LLVM bytecode. +// +//===----------------------------------------------------------------------===// + +#include "ArchiveInternals.h" +#include "llvm/Module.h" +#include "llvm/Bytecode/Reader.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/System/MappedFile.h" +#include <fstream> +#include <iostream> + +using namespace llvm; + +Archive* +Archive::CreateEmpty(const sys::Path& Filename) { + Archive* result = new Archive; + Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals; + impl->fname = Filename; + return result; +} + +Archive* +Archive::CreateFromFiles( + const sys::Path& Filename, + const PathList& Files, + const std::string& StripName +) { + Archive* result = new Archive; + Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals; + impl->fname = Filename; + + try { + size_t strip_len = StripName.length(); + for (PathList::const_iterator P = Files.begin(), E = Files.end(); P != E ;++P) + { + if (P->readable()) { + std::string name(P->get()); + if (strip_len > 0 && StripName == name.substr(0,strip_len)) { + name.erase(0,strip_len); + } + if (P->isBytecodeFile()) { + std::vector<std::string> syms; + if (!GetBytecodeSymbols(*P, syms)) + throw std::string("Can not get symbols from: ") + P->get(); + impl->addFileMember(*P, name, &syms); + } else { + impl->addFileMember(*P, name); + } + } + else + throw std::string("Can not read: ") + P->get(); + } + + // Now that we've collected everything, write the archive + impl->writeArchive(); + + } catch(...) { + delete impl; + result->impl = 0; + delete result; + throw; + } + + return result; +} + +void +Archive::ArchiveInternals::addFileMember( + const sys::Path& filePath, + const std::string& memberName, + const StrTab* symbols +) { + MemberInfo info; + info.path = filePath; + info.name = memberName; + filePath.getStatusInfo(info.status); + if (symbols) + info.symbols = *symbols; + info.offset = 0; + members.push_back(info); +} + +void +Archive::ArchiveInternals::writeInteger(int num, std::ofstream& ARFile) { + char buff[4]; + buff[0] = (num >> 24) & 255; + buff[1] = (num >> 16) & 255; + buff[2] = (num >> 8) & 255; + buff[3] = num & 255; + ARFile.write(buff, sizeof(buff)); +} + +void +Archive::ArchiveInternals::writeSymbolTable( std::ofstream& ARFile ) { + + // Compute the number of symbols in the symbol table and the + // total byte size of the string pool. While we're traversing, + // build the string pool for supporting long file names. Also, + // build the table of file offsets for the symbol table and + // the + typedef std::map<std::string,unsigned> SymbolMap; + StrTab stringPool; + SymbolMap symbolTable; + std::vector<unsigned> fileOffsets; + std::string symTabStrings; + unsigned fileOffset = 0; + unsigned spOffset = 0; + unsigned numSymbols = 0; + unsigned numSymBytes = 0; + for (unsigned i = 0; i < members.size(); i++ ) { + MemberInfo& mi = members[i]; + StrTab& syms = mi.symbols; + size_t numSym = syms.size(); + numSymbols += numSym; + for (unsigned j = 0; j < numSym; j++ ) { + numSymBytes += syms[j].size() + 1; + symbolTable[syms[i]] = i; + } + if (mi.name.length() > 15 || std::string::npos != mi.name.find('/')) { + stringPool.push_back(mi.name + "/\n"); + mi.name = std::string("/") + utostr(spOffset); + spOffset += mi.name.length() + 2; + } else if (mi.name[mi.name.length()-1] != '/') { + mi.name += "/"; + } + fileOffsets.push_back(fileOffset); + fileOffset += sizeof(ArchiveMemberHeader) + mi.status.fileSize; + } + + + // Compute the size of the symbol table file member + unsigned symTabSize = 0; + if (numSymbols != 0) + symTabSize = + sizeof(ArchiveMemberHeader) + // Size of the file header + 4 + // Size of "number of entries" + (4 * numSymbols) + // Size of member file indices + numSymBytes; // Size of the string table + + // Compute the size of the string pool + unsigned strPoolSize = 0; + if (spOffset != 0 ) + strPoolSize = + sizeof(ArchiveMemberHeader) + // Size of the file header + spOffset; // Number of bytes in the string pool + + // Compute the byte index offset created by symbol table and string pool + unsigned firstFileOffset = symTabSize + strPoolSize; + + // Create header for symbol table. This must be first if there is + // a symbol table and must have a special name. + if ( symTabSize > 0 ) { + ArchiveMemberHeader Hdr; + Hdr.init(); + + // Name of symbol table is '/ ' but "" is passed in + // because the setName method always terminates with a / + Hdr.setName(ARFILE_SYMTAB_NAME); + Hdr.setDate(); + Hdr.setSize(symTabSize - sizeof(ArchiveMemberHeader)); + Hdr.setMode(0); + Hdr.setUid(0); + Hdr.setGid(0); + + // Write header to archive file + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + // Write the number of entries in the symbol table + this->writeInteger(numSymbols, ARFile); + + // Write the file offset indices for each symbol and build the + // symbol table string pool + std::string symTabStrPool; + symTabStrPool.reserve(256 * 1024); // Reserve 256KBytes for symbols + for (SymbolMap::iterator I = symbolTable.begin(), E = symbolTable.end(); + I != E; ++I ) { + this->writeInteger(firstFileOffset + fileOffsets[I->second], ARFile); + symTabStrPool += I->first; + symTabStrPool += "\0"; + } + + // Write the symbol table's string pool + ARFile.write(symTabStrPool.data(), symTabStrPool.size()); + } + + //============== DONE WITH SYMBOL TABLE + + if (strPoolSize > 0) { + // Initialize the header for the string pool + ArchiveMemberHeader Hdr; + Hdr.init(); + Hdr.setName(ARFILE_STRTAB_NAME); + Hdr.setDate(); + Hdr.setSize(spOffset); + Hdr.setMode(0); + Hdr.setUid(0); + Hdr.setGid(0); + + // Write the string pool header + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + // Write the string pool + for (unsigned i = 0; i < stringPool.size(); i++) { + ARFile.write(stringPool[i].data(), stringPool[i].size()); + } + } +} + +void +Archive::ArchiveInternals::writeMember( + const MemberInfo& member, + std::ofstream& ARFile +) { + + // Map the file into memory. We do this early for two reasons. First, + // if there's any kind of error, we want to know about it. Second, we + // want to ensure we're using the most recent size for this file. + sys::MappedFile mFile(member.path); + mFile.map(); + + // Header for the archive member + ArchiveMemberHeader Hdr; + Hdr.init(); + + // Set the name. If its longer than 15 chars, it will have already + // been reduced by the writeSymbolTable. + Hdr.setName(member.name); + + // Set the other header members + Hdr.setSize( mFile.size() ); + Hdr.setMode( member.status.mode); + Hdr.setUid ( member.status.user); + Hdr.setGid ( member.status.group); + Hdr.setDate( member.status.modTime.ToPosixTime() ); + + // Write header to archive file + ARFile.write((char*)&Hdr, sizeof(Hdr)); + + //write to archive file + ARFile.write(mFile.charBase(),mFile.size()); + + mFile.unmap(); +} + +void +Archive::ArchiveInternals::writeArchive() { + + // Create archive file for output. + std::ofstream ArchiveFile(fname.get().c_str()); + + // Check for errors opening or creating archive file. + if ( !ArchiveFile.is_open() || ArchiveFile.bad() ) { + throw std::string("Error opening archive file: ") + fname.get(); + } + + // Write magic string to archive. + ArchiveFile << ARFILE_MAGIC; + + // Write the symbol table and string pool + writeSymbolTable(ArchiveFile); + + //Loop over all member files, and add to the archive. + for ( unsigned i = 0; i < members.size(); ++i) { + if(ArchiveFile.tellp() % 2 != 0) + ArchiveFile << ARFILE_PAD; + writeMember(members[i],ArchiveFile); + } + + //Close archive file. + ArchiveFile.close(); +} + +// vim: sw=2 ai |