X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fstlplus%2Fsubsystems%2Flibrary_manager.cpp;fp=src%2Fstlplus%2Fsubsystems%2Flibrary_manager.cpp;h=0daf1be4c0adb0adf28829e6344936e657d59873;hb=6b0a0d0efafe34d48ab344fca3b479553bd4e62c;hp=0000000000000000000000000000000000000000;hpb=85783316365181491a3e3c0c63659972477cebba;p=chaz%2Fyoink diff --git a/src/stlplus/subsystems/library_manager.cpp b/src/stlplus/subsystems/library_manager.cpp new file mode 100644 index 0000000..0daf1be --- /dev/null +++ b/src/stlplus/subsystems/library_manager.cpp @@ -0,0 +1,2332 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "library_manager.hpp" +#include "file_system.hpp" +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +static const char* LibraryNameExtension = "lmn"; +static const char* HeaderExtension = "lmh"; + +//////////////////////////////////////////////////////////////////////////////// +// local operations +//////////////////////////////////////////////////////////////////////////////// + +typedef std::map lm_callback_map; + +static std::string lowercase(const std::string& val) +{ + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = tolower(text[i]); + return text; +} + +// Context file and library operations +// These must be readable/writeable without a library data structure present +// so that the name of the library is known before creation + +static bool read_context(const std::string& path, const std::string& owner, std::string& name, bool& writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + name = ""; + writable = false; + if (!stlplus::file_exists(spec)) return false; + std::ifstream input(spec.c_str()); + input >> name >> writable; + return !input.fail(); +} + +static bool write_context(const std::string& path, const std::string& owner, const std::string& name, bool writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + std::ofstream output(spec.c_str()); + output << name << " " << writable << std::endl; + return !output.fail(); +} + +static bool create_library(const std::string& path, const std::string& owner, const std::string& name, bool writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + if (stlplus::is_present(path) && !stlplus::is_folder(path)) return false; + if (!stlplus::folder_exists(path) && !stlplus::folder_create(path)) return false; + return write_context(path, owner, name, writable); +} + +static bool erase_library(const std::string& path, const std::string& owner) +{ + // check that it is a library before deleting it! + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + if (!stlplus::file_exists(spec)) return false; + return stlplus::folder_delete(path, true); +} + +// dependency checking + +typedef std::map,stlplus::lm_dependencies> visited_map; + +static stlplus::lm_dependencies& out_of_date_check (visited_map& visited, + const stlplus::library_manager* manager, + const std::string& library, + const stlplus::lm_unit_name& name) +{ + // the visited field contains an entry if a unit has been visited - it also contains the reason for out-of-date-ness + // this is initially set empty (up to date) since the unit has to be added + // before it is checked just in case there is a recursive loop + // the dependencies field is filled in if the unit is subsequently found to be out of date + std::pair full_name = std::make_pair(library,name); + // first check whether this unit has already been visited - this should in + // principle never happen because the same test is performed before trying + // to recurse, so consider this to be paranoid-mode programming + visited_map::iterator found = visited.find(full_name); + if (found != visited.end()) + return found->second; + // now add this unit to prevent recursion loops later. This entry is added + // to when out-of-date dependencies are found and is also the return value + // of the function + visited[full_name] = stlplus::lm_dependencies(); + // now find the unit + const stlplus::lm_unit_ptr unit = manager->find(library,name); + if (!unit) + { + // if a unit is missing it fails with a dependency on itself - this is a + // bit of a work-around, but this again is paranoid-mode programming since + // the calling function (this function calling itself recursively) should + // check the unit's existence before recursing + visited[full_name].unit_add(stlplus::lm_unit_dependency(library,name)); + } + else + { + // we're onto the real checks now - first get the datestamp of this unit: + // all dependencies must be older than this + time_t unit_modified = unit->modified(); + // check dependency on source file if there is one + if (unit->source_file_present()) + { + std::string source_file = unit->source_file().path_full(unit->library_path()); + time_t source_modified = stlplus::file_modified(source_file); + if (source_modified == 0 || source_modified > unit_modified) + { + visited[full_name].set_source_file(unit->source_file()); + } + } + // now check the other file dependencies + for (unsigned i = 0; i < unit->file_size(); i++) + { + // a file dependency is a dependency on a file outside of the library system + const stlplus::lm_file_dependency& dependency = unit->file_dependency(i); + std::string file_full = dependency.path_full(unit->library_path()); + time_t source_modified = stlplus::file_modified(file_full); + if (source_modified == 0 || source_modified > unit_modified) + { + visited[full_name].file_add(dependency); + } + } + // now check and recurse on unit dependencies + for (unsigned j = 0; j < unit->unit_size(); j++) + { + const stlplus::lm_unit_dependency& dependency = unit->unit_dependency(j); + std::pair other_name = std::make_pair(dependency.library(), dependency.unit_name()); + // perform the datestamp checking at this level and only recurse if this does not detect an error + const stlplus::lm_unit_ptr other_unit = manager->find(other_name.first, other_name.second); + if (!other_unit) + { + visited[full_name].unit_add(dependency); + } + else + { + time_t other_modified = other_unit->modified(); + if (other_modified == 0 || other_modified > unit_modified) + { + visited[full_name].unit_add(dependency); + } + else + { + // prevent recursion before doing it + visited_map::iterator other_found = visited.find(other_name); + if (other_found != visited.end()) + { + // if the unit was found to be out of date on the previous visit, add it to the failed dependencies + if (!other_found->second.empty()) + { + visited[full_name].unit_add(dependency); + } + } + else + { + // the unit hasn't been visited before, so recurse on it now + stlplus::lm_dependencies other_dependencies = + out_of_date_check(visited, manager, other_name.first, other_name.second); + if (!other_dependencies.empty()) + { + visited[full_name].unit_add(dependency); + } + } + } + } + } + } + return visited[full_name]; +} + +//////////////////////////////////////////////////////////////////////////////// +// class lm_unit_name + +stlplus::lm_unit_name::lm_unit_name(const std::string& name, const std::string& type) : + m_name(name), m_type(type) +{ +} + +stlplus::lm_unit_name::~lm_unit_name(void) +{ +} + +const std::string& stlplus::lm_unit_name::name(void) const +{ + return m_name; +} + +void stlplus::lm_unit_name::set_name(const std::string& name) +{ + m_name = name; +} + +void stlplus::lm_unit_name::lowercase(void) +{ + m_name = ::lowercase(m_name); +} + +const std::string& stlplus::lm_unit_name::type(void) const +{ + return m_type; +} + +void stlplus::lm_unit_name::set_type(const std::string& type) +{ + m_type = type; +} + +bool stlplus::lm_unit_name::write(std::ostream& context) const +{ + context << m_name << " " << m_type; + return !context.fail(); +} + +bool stlplus::lm_unit_name::read(std::istream& context) +{ + context >> m_name >> m_type; + return !context.fail(); +} + +std::string stlplus::lm_unit_name::to_string(void) const +{ + return m_name + ":" + m_type; +} + +bool stlplus::lm_unit_name::print(std::ostream& str) const +{ + str << to_string(); + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_name& name) +{ + name.print(str); + return str; +} + +bool stlplus::operator == (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) +{ + return l.name() == r.name() && l.type() == r.type(); +} + +bool stlplus::operator < (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) +{ + // sort by name then type + return (l.name() != r.name()) ? l.name() < r.name() : l.type() < r.type(); +} + +//////////////////////////////////////////////////////////////////////////////// +// dependencies + +// file dependencies + +stlplus::lm_file_dependency::lm_file_dependency(void) : m_line(0), m_column(0) +{ +} + +stlplus::lm_file_dependency::lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line, unsigned column) +{ + set_path(library_path, path); + set_line(line); + set_column(column); +} + +stlplus::lm_file_dependency::~lm_file_dependency(void) +{ +} + +const std::string& stlplus::lm_file_dependency::path(void) const +{ + return m_path; +} + +std::string stlplus::lm_file_dependency::path_full(const std::string& library_path) const +{ + return filespec_to_path(library_path, m_path); +} + +void stlplus::lm_file_dependency::set_path(const std::string& library_path, const std::string& path) +{ + m_path = filespec_to_relative_path(library_path, path); +} + +unsigned stlplus::lm_file_dependency::line(void) const +{ + return m_line; +} + +void stlplus::lm_file_dependency::set_line(unsigned line) +{ + m_line = line; +} + +unsigned stlplus::lm_file_dependency::column(void) const +{ + return m_column; +} + +void stlplus::lm_file_dependency::set_column(unsigned column) +{ + m_column = column; +} + +bool stlplus::lm_file_dependency::write(std::ostream& context) const +{ + context << m_path << " " << m_line << " " << m_column; + return !context.fail(); +} + +bool stlplus::lm_file_dependency::read(std::istream& context) +{ + context >> m_path >> m_line >> m_column; + return !context.fail(); +} + +bool stlplus::lm_file_dependency::print(std::ostream& str) const +{ + str << "file: " << m_path; + if (m_line != 0) + str << ":" << m_line << ":" << m_column; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_file_dependency& dependency) +{ + dependency.print(str); + return str; +} + +// unit dependency + +stlplus::lm_unit_dependency::lm_unit_dependency(void) +{ +} + +stlplus::lm_unit_dependency::lm_unit_dependency(const std::string& library, const lm_unit_name& name) : + m_library(library), m_name(name) +{ +} + +stlplus::lm_unit_dependency::~lm_unit_dependency(void) +{ +} + +const std::string& stlplus::lm_unit_dependency::library(void) const +{ + return m_library; +} + +void stlplus::lm_unit_dependency::set_library(const std::string& library) +{ + m_library = library; +} + +const stlplus::lm_unit_name& stlplus::lm_unit_dependency::unit_name(void) const +{ + return m_name; +} + +void stlplus::lm_unit_dependency::set_unit_name(const lm_unit_name& unit_name) +{ + m_name = unit_name; +} + +const std::string& stlplus::lm_unit_dependency::name(void) const +{ + return m_name.name(); +} + +void stlplus::lm_unit_dependency::set_name(const std::string& name) +{ + m_name.set_name(name); +} + +const std::string& stlplus::lm_unit_dependency::type(void) const +{ + return m_name.type(); +} + +void stlplus::lm_unit_dependency::set_type(const std::string& type) +{ + m_name.set_type(type); +} + +bool stlplus::lm_unit_dependency::write(std::ostream& context) const +{ + context << m_library; + m_name.write(context); + return !context.fail(); +} + +bool stlplus::lm_unit_dependency::read(std::istream& context) +{ + context >> m_library; + m_name.read(context);; + return !context.fail(); +} + +bool stlplus::lm_unit_dependency::print(std::ostream& str) const +{ + str << "unit: " << m_library << "." << m_name; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_dependency& dependency) +{ + dependency.print(str); + return str; +} + +// dependencies + +stlplus::lm_dependencies::lm_dependencies(void) : m_source(0) +{ +} + +stlplus::lm_dependencies::lm_dependencies(const lm_dependencies& r) : m_source(0) +{ + *this = r; +} + +stlplus::lm_dependencies& stlplus::lm_dependencies::operator=(const stlplus::lm_dependencies& r) +{ + if (m_source) + delete m_source; + m_source = 0; + if (r.m_source) + m_source = new lm_file_dependency(*r.m_source); + m_files = r.m_files; + m_units = r.m_units; + return *this; +} + +stlplus::lm_dependencies::~lm_dependencies(void) +{ + if (m_source) + delete m_source; +} + +void stlplus::lm_dependencies::set_source_file(const lm_file_dependency& source) +{ + if (m_source) + delete m_source; + m_source = new lm_file_dependency(source); +} + +bool stlplus::lm_dependencies::source_file_present(void) const +{ + return (m_source != 0); +} + +const stlplus::lm_file_dependency& stlplus::lm_dependencies::source_file(void) const +{ + return *m_source; +} + +unsigned stlplus::lm_dependencies::file_add(const lm_file_dependency& dependency) +{ + m_files.push_back(dependency); + return m_files.size()-1; +} + +unsigned stlplus::lm_dependencies::file_size(void) const +{ + return m_files.size(); +} + +const stlplus::lm_file_dependency& stlplus::lm_dependencies::file_dependency(unsigned i) const +{ + return m_files[i]; +} + +void stlplus::lm_dependencies::file_erase(unsigned i) +{ + m_files.erase(m_files.begin()+i); +} + +unsigned stlplus::lm_dependencies::unit_add(const lm_unit_dependency& dependency) +{ + m_units.push_back(dependency); + return m_units.size()-1; +} + +unsigned stlplus::lm_dependencies::unit_size(void) const +{ + return m_units.size(); +} + +const stlplus::lm_unit_dependency& stlplus::lm_dependencies::unit_dependency(unsigned i) const +{ + return m_units[i]; +} + +void stlplus::lm_dependencies::unit_erase(unsigned i) +{ + m_units.erase(m_units.begin()+i); +} + +void stlplus::lm_dependencies::clear(void) +{ + if (m_source) + delete m_source; + m_source = 0; + m_files.clear(); + m_units.clear(); +} + +bool stlplus::lm_dependencies::empty(void) const +{ + return (m_source == 0) && m_files.empty() && m_units.empty(); +} + +bool stlplus::lm_dependencies::write(std::ostream& context) const +{ + context << (m_source ? true : false) << " "; + if (m_source) m_source->write(context); + context << std::endl; + context << m_files.size() << std::endl; + for (unsigned i = 0; i < m_files.size(); i++) + { + m_files[i].write(context); + context << std::endl; + } + context << m_units.size() << std::endl; + for (unsigned j = 0; j < m_units.size(); j++) + { + m_units[j].write(context); + context << std::endl; + } + return !context.fail(); +} + +bool stlplus::lm_dependencies::read(std::istream& context) +{ + clear(); + bool source_present = false; + context >> source_present; + if (source_present) + { + m_source = new lm_file_dependency(); + m_source->read(context); + } + unsigned files_size = 0; + context >> files_size; + for (unsigned i = 0; i < files_size; i++) + { + m_files.push_back(lm_file_dependency()); + m_files.back().read(context); + } + unsigned units_size = 0; + context >> units_size; + for (unsigned j = 0; j < units_size; j++) + { + m_units.push_back(lm_unit_dependency()); + m_units.back().read(context); + } + return !context.fail(); +} + +bool stlplus::lm_dependencies::print(std::ostream& str) const +{ + if (m_source) + str << " source file: " << *m_source << std::endl; + for (unsigned i = 0; i < m_files.size(); i++) + str << " " << m_files[i] << std::endl; + for (unsigned j = 0; j < m_units.size(); j++) + str << " " << m_units[j] << std::endl; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_dependencies& dependencies) +{ + dependencies.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// lm_unit +//////////////////////////////////////////////////////////////////////////////// + +stlplus::lm_unit::lm_unit(const lm_unit_name& name, lm_library* library) : + m_name(name), m_header_modified(false), m_loaded(false), m_marked(false), m_library(library), m_error(false) +{ + read_header(); +} + +stlplus::lm_unit::~lm_unit(void) +{ + write_header(); +} + +//////////////////////////////////////// +// Header data + +const stlplus::lm_unit_name& stlplus::lm_unit::unit_name(void) const +{ + return m_name; +} + +const std::string& stlplus::lm_unit::name(void) const +{ + return m_name.name(); +} + +const std::string& stlplus::lm_unit::type(void) const +{ + return m_name.type(); +} + +// dependencies + +// source file dependency + +void stlplus::lm_unit::set_source_file(const lm_file_dependency& dependency) +{ + m_header_modified = true; + m_dependencies.set_source_file(dependency); +} + +bool stlplus::lm_unit::source_file_present(void) const +{ + return m_dependencies.source_file_present(); +} + +const stlplus::lm_file_dependency& stlplus::lm_unit::source_file(void) const +{ + return m_dependencies.source_file(); +} + +// other file dependencies + +unsigned stlplus::lm_unit::file_add(const lm_file_dependency& dependency) +{ + m_header_modified = true; + return m_dependencies.file_add(dependency); +} + +unsigned stlplus::lm_unit::file_size(void) const +{ + return m_dependencies.file_size(); +} + +const stlplus::lm_file_dependency& stlplus::lm_unit::file_dependency(unsigned i) const +{ + return m_dependencies.file_dependency(i); +} + +void stlplus::lm_unit::file_erase(unsigned i) +{ + m_header_modified = true; + m_dependencies.file_erase(i); +} + +// unit dependencies + +unsigned stlplus::lm_unit::unit_add(const lm_unit_dependency& dependency) +{ + m_header_modified = true; + return m_dependencies.unit_add(dependency); +} + +unsigned stlplus::lm_unit::unit_size(void) const +{ + return m_dependencies.unit_size(); +} + +const stlplus::lm_unit_dependency& stlplus::lm_unit::unit_dependency(unsigned i) const +{ + return m_dependencies.unit_dependency(i); +} + +void stlplus::lm_unit::unit_erase(unsigned i) +{ + m_header_modified = true; + m_dependencies.unit_erase(i); +} + +const stlplus::lm_dependencies& stlplus::lm_unit::dependencies(void) const +{ + return m_dependencies; +} + +void stlplus::lm_unit::set_dependencies(const lm_dependencies& dependencies) +{ + m_header_modified = true; + m_dependencies = dependencies; +} + +void stlplus::lm_unit::clear_dependencies(void) +{ + m_header_modified = true; + m_dependencies.clear(); +} + +bool stlplus::lm_unit::empty_dependencies(void) const +{ + return m_dependencies.empty(); +} + +// dependency checking + +bool stlplus::lm_unit::out_of_date(void) const +{ + return m_library->out_of_date(m_name); +} + +bool stlplus::lm_unit::up_to_date(void) const +{ + return m_library->up_to_date(m_name); +} + +stlplus::lm_dependencies stlplus::lm_unit::out_of_date_reason(void) const +{ + return m_library->out_of_date_reason(m_name); +} + +// supplementary data + +const std::string& stlplus::lm_unit::supplementary_data(void) const +{ + return m_supplement; +} + +void stlplus::lm_unit::set_supplementary_data(const std::string& data) +{ + m_supplement = data; + m_header_modified = true; +} + +//////////////////////////////////////// +// unit data management + +bool stlplus::lm_unit::load(void) +{ + if (out_of_date()) return false; + if (m_loaded) return true; + // get the user data for this type + lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); + if (callback == m_library->m_manager->m_callbacks.end()) return false; + void* data = callback->second.m_type_data; + bool result = read(filename(), data); + m_loaded = true; + return result; +} + +bool stlplus::lm_unit::save(void) +{ + if (!m_marked) return true; + if (!m_loaded) return false; + // get the user data for this type + lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); + if (callback == m_library->m_manager->m_callbacks.end()) return false; + void* data = callback->second.m_type_data; + bool result = write(filename(), data); + if (result) m_marked = false; + return result; +} + +bool stlplus::lm_unit::loaded(void) const +{ + return m_loaded; +} + +void stlplus::lm_unit::mark(void) +{ + m_marked = true; +} + +time_t stlplus::lm_unit::modified(void) const +{ + return file_modified(filename()); +} + +//////////////////////////////////////// +// containing library manager details + +const stlplus::lm_library* stlplus::lm_unit::library(void) const +{ + return m_library; +} + +stlplus::lm_library* stlplus::lm_unit::library(void) +{ + return m_library; +} + +const std::string& stlplus::lm_unit::library_name(void) const +{ + return m_library->name(); +} + +const std::string& stlplus::lm_unit::library_path(void) const +{ + return m_library->path(); +} + +// error handling - these apply to the last read/write operation + +bool stlplus::lm_unit::error(void) const +{ + return m_error; +} + +// functions that customise subclasses of this superclass + +bool stlplus::lm_unit::read(const std::string& filespec, void* type_data) +{ + std::ifstream input(filespec.c_str()); + bool result = read(input, type_data); + if (input.fail()) + { + result = false; + m_error = true; + } + return result; +} + +bool stlplus::lm_unit::read(std::istream&, void*) +{ + return false; +} + +bool stlplus::lm_unit::write(const std::string& filespec, void* type_data) +{ + std::ofstream output(filespec.c_str()); + bool result = write(output, type_data); + if (output.fail()) + { + result = false; + m_error = true; + } + return result; +} + +bool stlplus::lm_unit::write(std::ostream&, void*) +{ + return false; +} + +bool stlplus::lm_unit::purge(void) +{ + return true; +} + +stlplus::lm_unit* stlplus::lm_unit::clone(void) const +{ + return new lm_unit(*this); +} + +bool stlplus::lm_unit::print(std::ostream& str) const +{ + str << m_name << " " << (m_loaded ? "loaded" : "") << " " << (m_marked ? "needs saving" : ""); + return !str.fail(); +} + +bool stlplus::lm_unit::print_long(std::ostream& str) const +{ + str << "header:" << std::endl; + str << " name: " << m_name.name() << std::endl; + str << " type: " << library()->manager()->description(m_name.type()) << std::endl; + str << " dependencies:" << std::endl; + // Note: I've inlined this rather than call the above-defined print for dependencies + // This is so that I can use the library manager to look up the descriptions of unit types + // I can also convert paths so that they are relative to the current directory rather than the library + // print the source file dependency if present + if (m_dependencies.source_file_present()) + { + str << " source file: "; + str << filespec_to_relative_path(m_dependencies.source_file().path_full(library()->path())); + if (m_dependencies.source_file().line() != 0) + str << ":" << m_dependencies.source_file().line() << ":" << m_dependencies.source_file().column(); + str << std::endl; + } + // now print other file dependencies + // convert these to relative paths too + for (unsigned f = 0; f < m_dependencies.file_size(); f++) + { + str << " file: "; + str << filespec_to_relative_path(m_dependencies.file_dependency(f).path_full(library()->path())); + if (m_dependencies.file_dependency(f).line() != 0) + str << ":" << m_dependencies.file_dependency(f).line() << ":" << m_dependencies.file_dependency(f).column(); + str << std::endl; + } + // now print unit dependencies + // convert unit types to their descriptive strings + for (unsigned u = 0; u < m_dependencies.unit_size(); u++) + { + str << " " << library()->manager()->description(m_dependencies.unit_dependency(u).type()) << ": "; + str << m_dependencies.unit_dependency(u).library() << "." << m_dependencies.unit_dependency(u).name(); + str << std::endl; + } + if (!m_supplement.empty()) + { + str << " supplementary data: " << m_supplement << std::endl; + } + return !str.fail(); +} + +// header file management + +std::string stlplus::lm_unit::filename(void) const +{ + return stlplus::create_filespec(library_path(), m_name.name(), m_name.type()); +} + +std::string stlplus::lm_unit::header_filename(void) const +{ + return stlplus::create_filespec(library_path(), m_name.name(), m_name.type() + std::string(HeaderExtension)); +} + +bool stlplus::lm_unit::read_header(void) +{ + if (file_exists(header_filename())) + { + std::ifstream input(header_filename().c_str()); + m_dependencies.read(input); + input.get(); + std::getline(input, m_supplement); + } + m_header_modified = false; + return true; +} + +bool stlplus::lm_unit::write_header(void) +{ + if (!m_header_modified) return true; + std::ofstream output(header_filename().c_str()); + m_dependencies.write(output); + output << m_supplement << std::endl; + m_header_modified = false; + return true; +} + +// print diagnostics + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit& u) +{ + u.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// lm_library +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// constructors/destructors +// copy operations only legal on unopened libraries + +stlplus::lm_library::lm_library(library_manager* manager) : m_writable(false), m_manager(manager) +{ +} + +stlplus::lm_library::lm_library(const lm_library& library) : m_writable(library.m_writable), m_manager(library.m_manager) +{ +} + +stlplus::lm_library& stlplus::lm_library::operator = (const stlplus::lm_library& library) +{ + m_writable = library.m_writable; + m_manager = library.m_manager; + return *this; +} + +stlplus::lm_library::~lm_library(void) +{ + close(); +} + +const stlplus::library_manager* stlplus::lm_library::manager(void) const +{ + return m_manager; +} + +stlplus::library_manager* stlplus::lm_library::manager(void) +{ + return m_manager; +} + +////////////////////////////////////////////////////////////////////////////// +// initialisers + +bool stlplus::lm_library::create(const std::string& name, const std::string& path, bool writable) +{ + close(); + if (!create_library(path, m_manager->m_owner, name, writable)) return false; + return open(path); +} + +bool stlplus::lm_library::open(const std::string& path) +{ + close(); + if (!read_context(path, m_manager->m_owner, m_name, m_writable)) return false; + // convert path to full path on load + m_path = folder_to_path(path); + return load_types(); +} + +////////////////////////////////////////////////////////////////////////////// +// management of types + +bool stlplus::lm_library::load_type(const std::string& type) +{ + lm_callback_map::iterator callback = m_manager->m_callbacks.find(type); + if (callback == m_manager->m_callbacks.end()) return false; + // a null callback means create a dummy unit from the baseclass only + lm_create_callback fn = callback->second.m_callback; + void* data = callback->second.m_type_data; + // for each file in the library folder that matches the type of the create callback, create an unloaded unit + std::vector files = folder_wildcard(m_path, stlplus::create_filename("*", type), false, true); + for (unsigned i = 0; i < files.size(); i++) + { + // key by unit name - lowercase name if case-insensitive + lm_unit_name uname(basename_part(files[i]),type); + if (!m_manager->m_unit_case) uname.lowercase(); + lm_unit_ptr unit; + // if there's a callback, use it to create the subclass, else create the + // superclass which only contains header information + if (fn) + unit.set(fn(uname,this,data)); + else + unit.set(new lm_unit(uname,this)); + m_units[uname] = unit; + } + return true; +} + +bool stlplus::lm_library::load_types(void) +{ + bool result = true; + for (lm_callback_map::const_iterator i = m_manager->m_callbacks.begin(); i != m_manager->m_callbacks.end(); i++) + result &= load_type(i->first); + return result; +} + +bool stlplus::lm_library::remove_type(const std::string& type) +{ + bool result = true; + std::vector units = names(type); + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + lm_unit_name name(*i, type); + std::map::iterator ni = local_find(name); + if (ni != m_units.end()) + m_units.erase(ni); + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// whole library operations + +bool stlplus::lm_library::load(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->load(); + return result; +} + +bool stlplus::lm_library::save(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->save(); + return result; +} + +bool stlplus::lm_library::purge(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->purge(); + return result; +} + +bool stlplus::lm_library::close(void) +{ + bool result = save(); + m_units.clear(); + m_path = ""; + m_writable = false; + return result; +} + +bool stlplus::lm_library::erase(void) +{ + // preserve the path because close destroys it + std::string path = m_path; + return close() && erase_library(path, m_manager->m_owner); +} + +const std::string& stlplus::lm_library::name(void) const +{ + return m_name; +} + +const std::string& stlplus::lm_library::path(void) const +{ + return m_path; +} + +////////////////////////////////////////////////////////////////////////////// +// managing read/write status + +bool stlplus::lm_library::set_read_write(bool writable) +{ + if (m_writable == writable) return true; + if (os_read_only()) return false; + m_writable = writable; + if (!write_context(m_path, m_manager->m_owner, m_name, m_writable)) + read_context(m_path, m_manager->m_owner, m_name, m_writable); + return m_writable == writable; +} + +bool stlplus::lm_library::set_writable(void) +{ + return set_read_write(true); +} + +bool stlplus::lm_library::set_read_only(void) +{ + return set_read_write(false); +} + +bool stlplus::lm_library::writable(void) const +{ + return os_writable() && lm_writable(); +} + +bool stlplus::lm_library::read_only(void) const +{ + return os_read_only() || lm_read_only(); +} + +bool stlplus::lm_library::os_writable(void) const +{ + return folder_writable(path()); +} + +bool stlplus::lm_library::os_read_only(void) const +{ + return !folder_writable(path()); +} + +bool stlplus::lm_library::lm_writable(void) const +{ + return m_writable; +} + +bool stlplus::lm_library::lm_read_only(void) const +{ + return !m_writable; +} + +////////////////////////////////////////////////////////////////////////////// +// unit management + +std::map::iterator stlplus::lm_library::local_find(const lm_unit_name& name) +{ + // implement the case-sensitivity + lm_unit_name local = name; + if (!m_manager->m_unit_case) local.lowercase(); + return m_units.find(local); +} + +std::map::const_iterator stlplus::lm_library::local_find(const lm_unit_name& name) const +{ + // implement the case-sensitivity + lm_unit_name local = name; + if (!m_manager->m_unit_case) local.set_name(lowercase(local.name())); + return m_units.find(local); +} + +bool stlplus::lm_library::exists(const lm_unit_name& name) const +{ + return find(name).present(); +} + +stlplus::lm_unit_ptr stlplus::lm_library::create(const stlplus::lm_unit_name& name) +{ + if (read_only()) return lm_unit_ptr(); + // preserve the unit's name, but use a lowercase key in case-insensitive mode + lm_unit_name uname = name; + if (!m_manager->m_unit_case) uname.lowercase(); + // remove any existing unit with the same name + erase(uname); + // use the callbacks to create a new unit + lm_callback_map::iterator callback = m_manager->m_callbacks.find(name.type()); + if (callback == m_manager->m_callbacks.end()) return lm_unit_ptr(); + lm_unit_ptr new_unit; + new_unit.set(callback->second.m_callback(name,this,callback->second.m_type_data)); + new_unit->m_loaded = true; + // add it to the library manager + m_units[uname] = new_unit; + return m_units[uname]; +} + +bool stlplus::lm_library::loaded(const lm_unit_name& name) const +{ + lm_unit_ptr unit = find(name); + return unit && unit->loaded(); +} + +bool stlplus::lm_library::load(const lm_unit_name& name) +{ + lm_unit_ptr unit = find(name); + if (!unit) return false; + return unit->load(); +} + +bool stlplus::lm_library::purge(const lm_unit_name& name) +{ + lm_unit_ptr unit = find(name); + if (!unit) return false; + bool result = save(name); + result &= unit->purge(); + unit->m_loaded = false; + return result; +} + +bool stlplus::lm_library::save(const lm_unit_name& name) +{ + if (read_only()) return false; + lm_unit_ptr unit = find(name); + if (!unit) return false; + return unit->save(); +} + +bool stlplus::lm_library::erase(const lm_unit_name& name) +{ + if (read_only()) return false; + std::map::iterator i = local_find(name); + if (i == m_units.end()) return false; + std::string spec = i->second->filename(); + std::string header_spec = i->second->header_filename(); + m_units.erase(i); + file_delete(spec); + file_delete(header_spec); + return true; +} + +bool stlplus::lm_library::mark(const lm_unit_name& name) +{ + if (read_only()) return false; + lm_unit_ptr unit = find(name); + if (!unit) return false; + unit->mark(); + return true; +} + +time_t stlplus::lm_library::modified(const lm_unit_name& name) const +{ + lm_unit_ptr unit = find(name); + return unit ? unit->modified() : 0; +} + +bool stlplus::lm_library::erase_by_source(const std::string& source_file) +{ + if (read_only()) return false; + if (source_file.empty()) return false; + bool result = false; + std::string source_file_full = filespec_to_path(source_file); + // erase by unit name so that I don't have to deal with an iterator to a changing map + std::vector units = names(); + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + lm_unit_ptr unit = find(*i); + if (unit && unit->source_file_present()) + { + std::string file_full = unit->source_file().path_full(unit->library_path()); + if (file_full == source_file_full) + { + erase(*i); + result = true; + } + } + } + return result; +} + +const stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) const +{ + std::map::const_iterator i = local_find(name); + if (i == m_units.end()) return lm_unit_ptr(); + return i->second; +} + +stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) +{ + std::map::iterator i = local_find(name); + if (i == m_units.end()) return lm_unit_ptr(); + return i->second; +} + +std::vector stlplus::lm_library::names(void) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + result.push_back(i->second->unit_name()); + return result; +} + +std::vector stlplus::lm_library::names(const std::string& type) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + if (i->first.type() == type) + result.push_back(i->second->name()); + return result; +} + +std::vector stlplus::lm_library::handles(void) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + result.push_back(i->second); + return result; +} + +std::vector stlplus::lm_library::handles(const std::string& type) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + if (i->first.type() == type) + result.push_back(i->second); + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// dependency checking + +bool stlplus::lm_library::out_of_date(const stlplus::lm_unit_name& name) const +{ + return m_manager->out_of_date(m_name, name); +} + +bool stlplus::lm_library::up_to_date(const stlplus::lm_unit_name& name) const +{ + return m_manager->up_to_date(m_name, name); +} + +stlplus::lm_dependencies stlplus::lm_library::out_of_date_reason(const stlplus::lm_unit_name& unit) const +{ + return m_manager->out_of_date_reason(m_name, unit); +} + +std::pair stlplus::lm_library::tidy(void) +{ + std::pair result = std::make_pair(true,0); + // erase every unit that is out of date + // this will potentially make other units out of date, so keep erasing until + // everything is up to date or an error occurs + for (;;) + { + std::vector units = names(); + unsigned initial_count = result.second; + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + if (out_of_date(*i)) + { + if (!erase(*i)) + result.first = false; + else + result.second++; + } + } + if (!result.first) break; + if (result.second == initial_count) break; + } + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// do-everything print routine! + +bool stlplus::lm_library::pretty_print(std::ostream& str, + bool print_units, + const std::string& type) const +{ + // print the library information + if (this == m_manager->work()) + str << "->> "; + else + str << " "; + str << name() << " in directory " << folder_to_relative_path(path()); + if (read_only()) str << " (locked)"; + str << std::endl; + // select the units + if (print_units) + { + // separate into a block per unit kind + for (lm_callback_map::const_iterator j = m_manager->m_callbacks.begin(); j != m_manager->m_callbacks.end(); j++) + { + // select the requested unit kind + if (type.empty() || type == j->first) + { + // get all the units of this kind + std::vector unit_names = names(j->first); + if (!unit_names.empty()) + { + str << " " << j->second.m_description << std::endl; + for (unsigned k = 0; k < unit_names.size(); k++) + { + lm_dependencies reason = out_of_date_reason(stlplus::lm_unit_name(unit_names[k],j->first)); + str << " - " << unit_names[k]; + if (!reason.empty()) str << " (out of date)"; + str << std::endl; + if (!reason.empty()) + { + if (reason.source_file_present()) + { + const lm_file_dependency& file = reason.source_file(); + str << " * source file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; + } + for (unsigned i1 = 0; i1 < reason.file_size(); i1++) + { + const lm_file_dependency& file = reason.file_dependency(i1); + str << " * file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; + } + for (unsigned i2 = 0; i2 < reason.unit_size(); i2++) + { + const lm_unit_dependency& file = reason.unit_dependency(i2); + lm_callback_map::const_iterator entry = m_manager->m_callbacks.find(file.type()); + str << " * " << entry->second.m_description << " " << file.library() << "." << file.name() << " has changed" << std::endl; + } + } + } + } + } + } + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// diagnostic print routines + +bool stlplus::lm_library::print(std::ostream& str) const +{ + str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + i->second->print(str); + return !str.fail(); +} + +bool stlplus::lm_library::print_long(std::ostream& str) const +{ + str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + i->second->print_long(str); + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_library& lib) +{ + lib.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// library manager +//////////////////////////////////////////////////////////////////////////////// + +// static functions allow you to test whether a directory is a library before opening it +// you can also find the library's name without opening it + +bool stlplus::library_manager::is_library(const std::string& path, const std::string& owner) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + return file_exists(spec); +} + +std::string stlplus::library_manager::library_name(const std::string& path, const std::string& owner) +{ + std::string name; + bool writable = false; + if (!read_context(path, owner, name, writable)) + return std::string(); + return name; +} + +bool stlplus::library_manager::is_library(const std::string& path) +{ + return is_library(path, m_owner); +} + +std::string stlplus::library_manager::library_name(const std::string& path) +{ + return library_name(path, m_owner); +} + +////////////////////////////////////////////////////////////////////////////// +// tructors + +stlplus::library_manager::library_manager(const std::string& owner, bool library_case, bool unit_case) : + m_owner(owner), m_ini_files(0), m_library_case(library_case), m_unit_case(unit_case) +{ +} + +stlplus::library_manager::~library_manager(void) +{ + close(); +} + +////////////////////////////////////////////////////////////////////////////// +// case sensitivity + +bool stlplus::library_manager::library_case(void) const +{ + return m_library_case; +} + +void stlplus::library_manager::set_library_case(bool library_case) +{ + m_library_case = library_case; +} + +bool stlplus::library_manager::unit_case(void) const +{ + return m_unit_case; +} + +void stlplus::library_manager::set_unit_case(bool unit_case) +{ + m_unit_case = unit_case; +} + +//////////////////////////////////////////////////////////////////////////////// +// type handling + +bool stlplus::library_manager::add_type(const std::string& type, + const std::string& description, + lm_create_callback fn, + void* type_data) +{ + bool result = true; + m_callbacks[type] = lm_callback_entry(fn, description, type_data); + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->load_type(type); + return result; +} + +bool stlplus::library_manager::remove_type(const std::string& type) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->remove_type(type); + m_callbacks.erase(type); + return result; +} + +std::vector stlplus::library_manager::types(void) const +{ + std::vector result; + for (lm_callback_map::const_iterator i = m_callbacks.begin(); i != m_callbacks.end(); i++) + result.push_back(i->first); + return result; +} + +std::string stlplus::library_manager::description(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return std::string(); + return found->second.m_description; +} + +stlplus::lm_create_callback stlplus::library_manager::callback(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return 0; + return found->second.m_callback; +} + +void* stlplus::library_manager::type_data(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return 0; + return found->second.m_type_data; +} + +////////////////////////////////////////////////////////////////////////////// +// mapping file handling + +void stlplus::library_manager::set_mapping_file(const std::string& mapping_file) +{ + m_mapping_file = mapping_file; +} + +bool stlplus::library_manager::load_mappings(const std::string& mapping_file) +{ + m_mapping_file = mapping_file; + if (!file_exists(mapping_file)) + { + return false; + } + std::ifstream input(mapping_file.c_str()); + if (input.fail()) + return false; + // each line of the map file is a path to a library + // the first line is the work library - may be empty + // mappings are saved as paths relative to the mapping file and converted to full paths on load + bool result = true; + unsigned line = 1; + for (std::string path = ""; std::getline(input,path); line++) + { + if (path.empty()) continue; + std::string full_path = folder_to_path(folder_part(m_mapping_file), path); + if (!is_library(full_path)) + { + result = false; + } + else + { + lm_library* lib = open(full_path); + if (!lib) + result = false; + else if (line == 1) + setwork(lib->name()); + } + } + return result; +} + +std::string stlplus::library_manager::mapping_file() +{ + return m_mapping_file; +} + +bool stlplus::library_manager::set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_name) +{ + if (!ini_files) return false; + bool result = true; + m_ini_files = ini_files; + m_ini_section = library_section; + m_ini_work = work_name; + // now load the existing library mappings if present - in any case create the sections + // Note: a library mapping is saved as a path relative to the ini file - on load convert it to a path relative to the current directory + if (m_ini_files->section_exists(m_ini_section)) + { + std::vector library_names = m_ini_files->variable_names(m_ini_section); + std::string work_name; + for (unsigned i = 0; i < library_names.size(); i++) + { + std::string library_name = library_names[i]; + // if the variable name is the work name, then this is a mapping to an existing library name + // if it is null, then it is masking a global library definition so ignore it + // otherwise it is a mapping to a directory containing the library + if (library_name.empty()) + { + } + else if (library_name == m_ini_work) + { + work_name = m_ini_files->variable_value(m_ini_section, library_name); + } + else + { + std::string value = m_ini_files->variable_value(m_ini_section, library_name); + std::string filename = m_ini_files->variable_filename(m_ini_section, library_name); + // get the path to the ini file defining this library, strip off the ini filename to get the folder + // then combine this with the library path from that ini file to the library to get a full path to the library + // whew! + std::string full_path = folder_to_path(folder_part(filename),value); + if (!is_library(full_path)) + result = false; + else + { + lm_library* lib = open(full_path); + if (!lib) + result = false; + } + } + } + // work must be set after all the libraries have been opened because it is + // illegal to set work to a library that doesn't already exist in the + // library manager + if (work_name.empty()) + unsetwork(); + else + result &= setwork(work_name); + } + return result; +} + +stlplus::ini_manager* stlplus::library_manager::get_ini_manager(void) const +{ + return m_ini_files; +} + +bool stlplus::library_manager::save_mappings (void) +{ + bool result = true; + // save to mapping file or ini manager or both + if (!m_mapping_file.empty()) + { + if (m_libraries.size() == 0) + { + // if the file would be empty, delete it + if (!file_delete(m_mapping_file)) + result = false; + } + else + { + std::ofstream output(m_mapping_file.c_str()); + if (output.fail()) + { + result = false; + } + else + { + // each line of the map file is a path to a library + // the first line is the work library + // mappings are saved as relative paths to the mapping file and converted to full paths on load + if (!work_name().empty()) + output << folder_to_relative_path(folder_part(m_mapping_file), path(work_name())); + output << std::endl; + std::vector libraries = names(); + for (unsigned i = 0; i < libraries.size(); ++i) + { + if (libraries[i] != work_name()) + output << folder_to_relative_path(folder_part(m_mapping_file), path(libraries[i])) << std::endl; + } + if (output.fail()) + result = false; + } + } + } + if (m_ini_files) + { + // this is somewhat tricky! + // first remove all local mappings + // then need to compare the surviving library mappings with the contents of the library manager + // if there's a library in the ini files not in the library manager, mask it with an empty local declaration + // if there's a library in the ini files with the same mapping as the library manager, do nothing + // if there's a library in the ini files with a different mapping write that library mapping + // if there's a mapping missing from the ini files, write it + // finally write the work mapping if there is one + // clear all local mappings + // TODO - rework this so that the ini files only change if the mappings change + m_ini_files->clear_section(m_ini_section); + m_ini_files->add_comment(m_ini_section, "generated automatically by the library manager"); + // look for globally defined library mappings that need to be overridden in the local ini file + std::vector ini_names = m_ini_files->variable_names(m_ini_section); + for (unsigned i = 0; i < ini_names.size(); i++) + { + std::string ini_name = ini_names[i]; + // check for a global library that needs to be locally masked + if (!exists(ini_name)) + m_ini_files->add_variable(m_ini_section, ini_name, ""); + else + { + // check for a library that is locally remapped + std::string value = m_ini_files->variable_value(m_ini_section, ini_name); + std::string filename = m_ini_files->variable_filename(m_ini_section, ini_name); + std::string full_ini_path = folder_to_path(folder_part(filename), value); + std::string full_lib_path = folder_to_path(path(ini_name)); + if (full_ini_path != full_lib_path) + { + // write the path relative to the ini file + std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); + m_ini_files->add_variable(m_ini_section, ini_name, relative_path); + } + } + } + // now scan the library for mappings that aren't yet in the ini file + std::vector lib_names = names(); + for (unsigned j = 0; j < lib_names.size(); j++) + { + std::string lib_name = lib_names[j]; + if (std::find(ini_names.begin(), ini_names.end(), lib_name) == ini_names.end()) + { + // write the path relative to the ini file + std::string full_lib_path = folder_to_path(path(lib_name)); + std::string filename = m_ini_files->variable_filename(m_ini_section, lib_name); + std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); + m_ini_files->add_variable(m_ini_section, lib_name, relative_path); + } + } + // write the work library - also write a blank value if work is not set but is defined in another library + if (!work_name().empty()) + m_ini_files->add_variable(m_ini_section, m_ini_work, work_name()); + else if (m_ini_files->variable_exists(m_ini_section, m_ini_work)) + m_ini_files->add_variable(m_ini_section, m_ini_work, ""); + m_ini_files->add_blank(m_ini_section); + // remove the section from the ini file manager if there's nothing in it + if (m_ini_files->empty_section(m_ini_section)) + m_ini_files->erase_section(m_ini_section); + if (!m_ini_files->save()) + result = false; + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// library management + +bool stlplus::library_manager::exists(const std::string& name) const +{ + return find(name) != 0; +} + +stlplus::lm_library* stlplus::library_manager::create(const std::string& name, const std::string& path, bool writable) +{ + if (!create_library(path, m_owner, name, writable)) return 0; + return open(path); +} + +stlplus::lm_library* stlplus::library_manager::open(const std::string& path) +{ + std::string name; + bool writable = false; + if (!read_context(path, m_owner, name, writable)) return 0; + // remove any pre-existing library with the same name + close(name); + // add the library to the manager and open it + m_libraries.push_back(lm_library(this)); + if (!m_libraries.back().open(folder_to_path(path))) + { + // remove the library in the event of an error + m_libraries.erase(--m_libraries.end()); + return 0; + } + return &m_libraries.back(); +} + +bool stlplus::library_manager::load(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->load(); +} + +bool stlplus::library_manager::save(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->save(); +} + +bool stlplus::library_manager::purge(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->purge(); +} + +bool stlplus::library_manager::close(const std::string& name) +{ + bool result= true; + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + result &= found->close(); + m_libraries.erase(found); + if (name == m_work) m_work = ""; + return result; +} + +bool stlplus::library_manager::erase(const std::string& name) +{ + bool result= true; + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + result &= found->erase(); + m_libraries.erase(found); + if (name == m_work) m_work = ""; + return result; +} + +// operations on all libraries - as above but applied to all the libraries in the manager + +bool stlplus::library_manager::load(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->load(); + return result; +} + +bool stlplus::library_manager::save(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->save(); + return result; +} + +bool stlplus::library_manager::purge(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->purge(); + return result; +} + +bool stlplus::library_manager::close(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) + { + std::list::iterator next = i; + next++; + result &= i->close(); + m_libraries.erase(i); + i = next; + } + return result; +} + +bool stlplus::library_manager::erase(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) + { + std::list::iterator next = i; + next++; + result &= i->erase(); + m_libraries.erase(i); + i = next; + } + return result; +} + +// get name and path of a library - name can differ in case if the library manager is case-insensitive + +std::string stlplus::library_manager::name(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::string(); + return found->name(); +} + +std::string stlplus::library_manager::path(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::string(); + return found->path(); +} + +// control and test read/write status + +bool stlplus::library_manager::set_writable(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->set_writable(); +} + +bool stlplus::library_manager::set_read_only(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->set_read_only(); +} + +bool stlplus::library_manager::writable(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->writable(); +} + +bool stlplus::library_manager::read_only(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->read_only(); +} + +bool stlplus::library_manager::os_writable(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->os_writable(); +} + +bool stlplus::library_manager::os_read_only(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->os_read_only(); +} + +bool stlplus::library_manager::lm_writable(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->lm_writable(); +} + +bool stlplus::library_manager::lm_read_only(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->lm_read_only(); +} + +// find a library in the manager - returns null if not found + +stlplus::lm_library* stlplus::library_manager::find(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return &(*found); +} + +const stlplus::lm_library* stlplus::library_manager::find(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return &(*found); +} + +// get the set of all library names + +std::vector stlplus::library_manager::names(void) const +{ + std::vector result; + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(i->name()); + return result; +} + +// get the set of all libraries + +std::vector stlplus::library_manager::handles(void) const +{ + std::vector result; + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(&(*i)); + return result; +} + +std::vector stlplus::library_manager::handles(void) +{ + std::vector result; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(&(*i)); + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// current library management + +bool stlplus::library_manager::setwork(const std::string& name) +{ + unsetwork(); + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + m_work = found->name(); + return true; +} + +bool stlplus::library_manager::unsetwork(void) +{ + m_work = ""; + return true; +} + +const stlplus::lm_library* stlplus::library_manager::work(void) const +{ + if (m_work.empty()) return 0; + return find(m_work); +} + +stlplus::lm_library* stlplus::library_manager::work(void) +{ + if (m_work.empty()) return 0; + return find(m_work); +} + +std::string stlplus::library_manager::work_name(void) const +{ + return m_work; +} + +////////////////////////////////////////////////////////////////////////////// +// unit management within a library + +bool stlplus::library_manager::exists(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->exists(unit); +} + +stlplus::lm_unit_ptr stlplus::library_manager::create(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->create(unit); +} + +bool stlplus::library_manager::loaded(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->loaded(unit); +} + +bool stlplus::library_manager::load(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->load(unit); +} + +bool stlplus::library_manager::purge(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->purge(unit); +} + +bool stlplus::library_manager::save(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->save(unit); +} + +bool stlplus::library_manager::erase(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->erase(unit); +} + +bool stlplus::library_manager::mark(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->mark(unit); +} + +time_t stlplus::library_manager::modified(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return found->modified(unit); +} + +bool stlplus::library_manager::erase_by_source(const std::string& source_file) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + if (i->writable()) + result &= i->erase_by_source(source_file); + return result; +} + +const stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) + return stlplus::lm_unit_ptr(); + return found->find(unit); +} + +stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->find(unit); +} + +std::vector stlplus::library_manager::names(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->names(); +} + +std::vector stlplus::library_manager::names(const std::string& name, const std::string& type) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->names(type); +} + +std::vector stlplus::library_manager::handles(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->handles(); +} + +std::vector stlplus::library_manager::handles(const std::string& name, const std::string& type) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->handles(type); +} + +////////////////////////////////////////////////////////////////////////////// +// dependency checking +// done at the top level because a global view of the libraries is required + +bool stlplus::library_manager::out_of_date(const std::string& library, const stlplus::lm_unit_name& name) const +{ + return !up_to_date(library, name); +} + +bool stlplus::library_manager::up_to_date(const std::string& library, const stlplus::lm_unit_name& name) const +{ + lm_dependencies reason = out_of_date_reason(library, name); + return reason.empty(); +} + +stlplus::lm_dependencies stlplus::library_manager::out_of_date_reason(const std::string& library, const stlplus::lm_unit_name& name) const +{ + std::map,lm_dependencies> visited; + return out_of_date_check(visited, this, library, name); +} + +std::pair stlplus::library_manager::tidy(const std::string& library) +{ + std::list::iterator found = local_find(library); + if (found == m_libraries.end()) return std::make_pair(false,0); + return found->tidy(); +} + +std::pair stlplus::library_manager::tidy(void) +{ + std::pair result = std::make_pair(true,0); + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (i->writable()) + { + std::pair library_result = i->tidy(); + result.second += library_result.second; + result.first &= library_result.first; + } + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// do-everything print routine! + +bool stlplus::library_manager::pretty_print(std::ostream& str, + bool print_units, + const std::string& lib, + const std::string& type) const +{ + bool library_found = false; + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + { + // select the library + if (lib.empty() || lib == l->name()) + { + l->pretty_print(str, print_units, type); + library_found = true; + } + } + if (!library_found) + { + if (lib.empty()) + str << "there are no libraries in the library list" << std::endl; + else + str << "there is no library called " << lib << " in the library list" << std::endl; + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// diagnostic print routines + +bool stlplus::library_manager::print(std::ostream& str) const +{ + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + l->print(str); + return str; +} + +bool stlplus::library_manager::print_long(std::ostream& str) const +{ + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + l->print_long(str); + return str; +} + +// find a library by name + +std::list::iterator stlplus::library_manager::local_find(const std::string& name) +{ + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (m_library_case) + { + if (i->name() == name) return i; + } + else + { + if (lowercase(i->name()) == lowercase(name)) return i; + } + } + return m_libraries.end(); +} + +std::list::const_iterator stlplus::library_manager::local_find(const std::string& name) const +{ + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (m_library_case) + { + if (i->name() == name) return i; + } + else + { + if (lowercase(i->name()) == lowercase(name)) return i; + } + } + return m_libraries.end(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::library_manager& manager) +{ + manager.print(str); + return str; +} + +////////////////////////////////////////////////////////////////////////////////