#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 #define BUILTIN_SIG 0x5141504e 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 { // size = results.size; seek(results.ptr, SEEK_SET); read(&size, 4); 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); } } 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; archive.read(&size, 4); 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 << "inarchivein: reste = `" << reste << "' - name = `" << name << "'\n"; 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; } 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; 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; }