/* * Baltisot * Copyright (C) 1999-2003 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: Input.cc,v 1.37 2003-12-04 04:09:02 pixel Exp $ */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_UNISTD_H #include #else #include #endif #include "Input.h" #include "Exceptions.h" #include "gettext.h" #ifndef S_ISREG #define S_ISREG(x) 1 #endif #ifdef WORDS_BIGENDIAN #define BUILTIN_SIG 0x4e504151 #else #define BUILTIN_SIG 0x5141504e #endif /* NPAQ file format ================ off|siz|description ---+---+----------- 0 | 4 | Magic: NPAQ 4 | X | Index 4+X| Y | Files Index: It's composed of entries. Each entries have the same format: off|siz|description ---+---+----------- 0 | 1 | name size = n 1 | n | file name 1+n| 4 | file size 5+n| 1 | type --> 0 = file / 1 = directory If it's a directory, it means the following entries belongs to this directory. If n = 0, there is nothing else afterward (no filesize nor type) and it means the end of the current directory. The end of the root directory means the end of the index. Files: Each file are gzipped. They are all concatenated after the index. They all start with a four bytes length which correspond to the unpacked size. The size in the index corresponds to the size of the gzipped result plus those 4 bytes. */ static Input::openresults_t gresults; Input::Input(const String & no) throw (GeneralException) : Handle(no.strlen() ? wrapopen(no, &gresults) : dup(0)), n(no) { #ifdef DEBUG printm(M_BARE, String(_("Opening file")) + no + _("Input at %p\n"), this); #endif if (GetHandle() < 0) { printm(M_BARE, "Got handle: %i\n", GetHandle()); throw IOGeneral(String(_("Error opening file ")) + no + _(" for reading: ") + strerror(errno)); } results = gresults; fromarchive = false; if (results.name == "") { struct stat s; fstat(GetHandle(), &s); date_modif = s.st_mtime; if (S_ISREG(s.st_mode)) { size = seek(0, SEEK_END); seek(0, SEEK_SET); } } else { #ifdef DEBUG printm(M_INFO, String(_("Opening file in archive, position ")) + results.ptr + "\n"); #endif size = results.size; seek(results.ptr, SEEK_SET); size = readU32(); date_modif = 0; SetZ(); fromarchive = true; } } Input::Input(const Input & i) : Handle(i), n(i.n), size(i.size), date_modif(i.date_modif) { } bool Input::CanWrite() const { return 0; } bool Input::CanRead() const { return 1; } bool Input::CanSeek() const { struct stat s; fstat(GetHandle(), &s); return S_ISREG(s.st_mode); } String Input::GetName() const { return n; } ssize_t Input::GetSize() const { return size; } time_t Input::GetModif() const { return date_modif; } off_t Input::seek(off_t offset, int whence) throw (GeneralException) { if (!fromarchive) { if ((itell = lseek(GetHandle(), offset, whence)) < 0) { throw IOGeneral(String(_("Error seeking file ")) + GetName() + _(": ") + strerror(errno)); } #ifdef PARANOID_SEEK if (itell != lseek(GetHandle(), 0, SEEK_CUR)) { throw IOGeneral(String(_("Error seeking file ")) + GetName() + _(": the position does not match")); } #endif return itell; } else { return Handle::seek(offset, whence); } } int Input::wrapopen(const String & fname, openresults_t * results) { #ifdef DEBUG printm(M_INFO, _("Wrap-opening ") + fname + "\n"); #endif if (fname[0] != '/') { Archive * t; t = Archive::inarchive(fname); if (t) { #ifdef DEBUG printm(M_BARE, _("Trying to open the file in archive, since it seems to be here\n")); #endif #ifdef _WIN32 hFile = t->GetHandle(); #endif return t->open(fname, results); } } results->name = ""; #ifndef _WIN32 return open(fname.to_charp(), O_RDONLY); #else hFile = CreateFile(fname.to_charp(), GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); return _open_osfhandle((INT_PTR) hFile, O_RDONLY | O_BINARY); #endif } void Input::SetZ(int l) throw(GeneralException) { if (!fromarchive) Handle::SetZ(l); } Stdin_t::Stdin_t() { } bool Stdin_t::CanSeek() const { return 0; } String Stdin_t::GetName() const { return "Stdin"; } #ifdef HOOK_STDS Stdin_t Stdin; #endif Archive * Archive::header = 0; Archive::Archive(const String & fname, int atype) : name(fname), archive(new Input(fname)), type(atype) { create(); } Archive::Archive(Handle * hand, int atype) : name(hand->GetName()), archive(hand), type(atype) { create(); } #ifdef _MSC_VER #pragma pack(1) #endif struct PEsection_t { char name[8]; Uint32 VSize, VAdd, SizeOf, Pointer, PTRelocs, PTLNs; Uint16 NR, NLN; Uint32 Chars; } PACKED; void Archive::create() throw (GeneralException) { char buffer[1024]; int len; Uint32 sig, ptr, maxptr = 0, startptr; Uint16 sections; PEsection_t PEsection; size_t size; FileTree * p = &filetree, * t; String ifname; int i; switch(type) { case ARCHIVE_EXECUTABLE: startptr = archive->tell(); sig = archive->readU32(); if ((sig & 0xffff) == 0x5a4d) { /* MZ */ archive->seek(56, SEEK_CUR); sig = archive->readU32(); archive->seek(sig - 64, SEEK_CUR); sig = archive->readU32(); if (sig != 0x4550) throw GeneralException(_("Archive: file is not a PE.")); archive->seek(2, SEEK_CUR); sections = archive->readU16(); archive->seek(240, SEEK_CUR); for (i = 0; i < sections; i++) { archive->read(&PEsection, sizeof(PEsection)); printm(M_INFO, "Section: %s\n", PEsection.name); ptr = PEsection.Pointer + PEsection.SizeOf; if (ptr > maxptr) maxptr = ptr; } archive->seek(startptr - archive->tell() + maxptr, SEEK_CUR); } case ARCHIVE_BUILTIN: archive->read(buffer, 4); if (*((Uint32 *)buffer) != BUILTIN_SIG) throw GeneralException(_("Archive: not in built-in format.")); while (p) { archive->read(buffer, 1); len = *buffer; if (len) { archive->read(buffer, len); buffer[len] = 0; ifname = buffer; size = archive->readU32(); archive->read(buffer, 1); #ifdef DEBUG printm(M_BARE, _("Adding file `") + ifname + _("' to node `") + p->name + "'\n"); #endif t = new FileTree(ifname, size, buffer[0], p); if (buffer[0]) p = t; } else { p = p->Father(); } } filetree.compute_ptrs(archive->tell()); break; default: throw GeneralException(_("Archive: unsupported archive format.")); } next = header; prev = 0; header = this; if (next) next->prev = this; } Archive::~Archive() { if (prev) prev->next = next; if (next) next->prev = prev; if (header == this) header = next; delete archive; } Archive * Archive::inarchive(const String & fname) { Archive * p; for (p = header; p; p = p->next) { #ifdef DEBUG printm(M_BARE, _("Looking for file `") + fname + _("' in archive ") + p->name + "\n"); #endif if (p->inarchivein(fname)) { #ifdef DEBUG printm(M_BARE, _("File `") + fname + _("' found in archive ") + p->name + "\n"); #endif return p; } } return 0; } int Archive::open(const String & fname, Input::openresults_t * results) { #if 0 Archive * p; for (p = header; p; p = p->next) { bool t; t = p->inarchive(fname); if (t) return p->openin(fname, results); } throw IOGeneral(_("File `") + fname + _("' not found in archive collection.")); #endif return openin(fname, results); } Handle * Archive::GetHandle() { return archive; } bool Archive::inarchivein(const String & fname) { Archive::FileTree * p = filetree.Child(); ssize_t pos; String name = fname; String reste; while((name != "") && p) { pos = name.strchr('/'); if (pos >= 0) { reste = name.extract(0, pos - 1); name = name.extract(pos + 1); } else { reste = name; name = ""; } #ifdef DEBUG printm(M_BARE, _("Checking against node `") + p->name + "'\n"); #endif while (p) { if (p->name == reste) { if (name != "") p = p->Child(); break; } else { p = p->Next(); } } } return p != 0; } int Archive::openin(const String & fname, Input::openresults_t * results) throw (GeneralException) { Archive::FileTree * p = filetree.Child(); ssize_t pos; String name = fname; String reste; while((name != "") && p) { pos = name.strchr('/'); if (pos >= 0) { reste = name.extract(0, pos - 1); name = name.extract(pos + 1); } else { reste = name; name = ""; } while (p) { if (p->name == reste) { if (name != "") p = p->Child(); break; } else { p = p->Next(); } } } if (!p) throw IOGeneral(_("File `") + fname + _("' not in archive ") + this->name); if (p->Child()) throw IOGeneral(_("File `") + fname + _("' in archive ") + this->name + _(" is a directory - can't open.")); results->name = p->name; results->ptr = p->ptr; results->size = p->size; results->type = p->type; return archive->Dup(); } Archive::FileTree::FileTree(const String & fname, size_t fsize, int ftype, Archive::FileTree * fFather) : name(fname), type(ftype), size(fsize), next(0), prev(0), father(fFather), child(0) { if (father) { if (father->child) { FileTree * p; for (p = father->child; p->next; p = p->next); p->next = this; prev = p; } else { #ifdef DEBUG std::cerr << _("Adding `") << fname << _("' as first child of node `") << father->name << "'\n"; #endif father->child = this; } } } Archive::FileTree::~FileTree() { if (child) delete child; if (next) next->prev = prev; if (prev) prev->next = next; if (father) { if (father->child == this) father->child = next; father->touched(); } } void Archive::FileTree::touched() { if (father) father->touched(); else compute_ptrs(ptr); } int Archive::FileTree::compute_ptrs(size_t cptr) { ptr = cptr; #ifdef DEBUG std::cerr << _("Computed pointer for `") << name << "' = " << ptr << std::endl; if (child) std::cerr << _("Node has child\n"); else std::cerr << _("Node is ") << size << _(" bytes large.\n"); #endif if (child) { FileTree * p; size = 0; for (p = child; p; p = p->next) { size = p->compute_ptrs(ptr + size) - ptr; } } return size + ptr; } Archive::FileTree * Archive::FileTree::Father() { return father; } Archive::FileTree * Archive::FileTree::Child() { return child; } Archive::FileTree * Archive::FileTree::Next() { return next; } Archive::FileTree * Archive::FileTree::Prev() { return prev; }