#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 fprintf(stderr, _("Opening file %s, Input at %p\n"), no.to_charp(), this); #endif if (GetHandle() < 0) { 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 std::cerr << _("Opening file in archive, position ") << results.ptr << std::endl; #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 std::cerr << _("Wrap-opening ") << fname << std::endl; #endif if (fname[0] != '/') { bool t; t = Archive::inarchive(fname); if (t) { #ifdef DEBUG std::cerr << _("Trying to open the file in archive, since it seems to be here\n"); #endif return Archive::open(fname, results); } } results->name = ""; return open(fname.to_charp(), O_RDONLY); } 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) throw (GeneralException) : name(fname), archive(fname), type(atype) { char buffer[1024]; int len; size_t size; FileTree * p = &filetree, * t; String ifname; switch(type) { 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 std::cerr << _("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; } bool Archive::inarchive(const String & fname) { Archive * p; for (p = header; p; p = p->next) { #ifdef DEBUG std::cerr << _("Looking for file `") << fname << _("' in archive ") << p->name << std::endl; #endif if (p->inarchivein(fname)) { #ifdef DEBUG std::cerr << _("File `") << fname << _("' found in archive ") << p->name << std::endl; #endif return true; } } return false; } int Archive::open(const String & fname, Input::openresults_t * results) throw (GeneralException) { 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.")); } 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 std::cerr << _("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; }