// FileGenericRepository.cxx #include "dataset_catalog/FileGenericRepository.h" #include #include #include "dataset_util/Environment.h" #include "dataset_util/FileName.h" #include "dataset_util/FileStatus.h" #include "dataset_util/mkdir.h" #include "dataset_util/getcwd.h" #include "dataset_util/DtdRegistry.h" #include "dataset_id/UniqueId.h" #include "dataset_xml/XmlParser.h" #include "dataset_catalog/ConnectionResolver.h" using std::string; using std::ostream; using std::cerr; using std::endl; using std::cout; using dset::GenericRepository; using dset::FileGenericRepository; using dset::ConnectionResolver; typedef FileGenericRepository::size_type size_type; typedef FileGenericRepository::Type Type; typedef FileGenericRepository::TypeList TypeList; typedef FileGenericRepository::IdList IdList; //********************************************************************** // Local definitions. //********************************************************************** namespace { //********************************************************************** // GenericRepository creator. GenericRepository* create(string type, string conn) { if ( ConnectionResolver::accessor_name(conn) != "" ) return 0; if ( ConnectionResolver::location_name(conn) != "FILE" ) return 0; Text::WordList fields = Text::split(conn, ":", false); if ( fields.size() != 2 ) return 0; return new FileGenericRepository(type, fields[1]); } //********************************************************************** // Return the list of files holding ID's. const Text& idfiles(string dir, string action ="") { static Text txt; if ( action == "delete" ) { string cmd = "rm tmp.tmp"; system("rm -f tmp.tmp"); } else { string cmd = "find " + dir; cmd += " -name ??????????"; cmd += "-?????????? > tmp.tmp"; system(cmd.c_str()); txt = Text("tmp.tmp"); } return txt; } //********************************************************************** // Return the name of the file holding an object. string filename(string dir, string type, string id) { string name = dir; name += "/"; if ( type == "id_path" ) { UniqueId id1(id); name += id1.to_path(); name = name.substr(0, name.size()-2); } name += id; return name; } //********************************************************************** } // end unnamed namespace //********************************************************************** // Static member functions //********************************************************************** // Return the DB types. const TypeList& FileGenericRepository::types() { static TypeList dbtypes; if ( dbtypes.size() == 0 ) { dbtypes.push_back("flat"); dbtypes.push_back("id_path"); } return dbtypes; } //********************************************************************** // Return if a type is valid. bool FileGenericRepository::is_valid_type(Type type) { if ( type == "default" ) return true; TypeList::const_iterator i1 = types().begin(); TypeList::const_iterator i2 = types().end(); return find(i1, i2, type) != i2; } //********************************************************************** // Constructors and destructor. //********************************************************************** // Constructor. FileGenericRepository:: FileGenericRepository(Type rtype, string dir, bool create, Type type) : m_rtype(rtype), m_dir(dir), m_type(type), m_error(0) { if ( m_dir == "" ) { m_error = 10; return; } if ( m_rtype == "") { m_error = 9; return; } FileStatus dirstat(m_dir); if ( ! dirstat.exists() && create ) { mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; mkdir(m_dir.c_str(), mode); dirstat.update(); } bool dir_ok = dirstat.is_directory() && dirstat.is_readable() && dirstat.is_writeable() && dirstat.is_executable(); if ( ! dir_ok ) { m_error = 11; m_dir = ""; return; } // Check the DB description file. string descfile = m_dir + "/" + "GenericRepository.txt"; string classname = "FileGenericRepository"; Text desc(descfile); // If there is no file, write one. if ( desc.size() == 0 ) { if ( m_type == "default" ) { m_type = "id_path"; } desc.append(classname); desc.append(m_rtype); desc.append(m_type); desc.write(); // Otherwise, check description is consistent. } else { string oldclassname = desc.line(0); string oldrtype = desc.line(1); string oldtype = desc.line(2); if ( m_type == "default" ) { m_type = oldtype; } if ( desc.size() != 3 || classname != oldclassname || m_type != oldtype ) { cerr << classname << ": Inconsistent description at" << endl; cerr << " " << m_dir << endl; cerr << " Old: " << oldclassname << " " << oldrtype << " " << oldtype << endl; cerr << " New: " << classname << " " << m_rtype << " " << m_type << endl; m_error = 12; return; } } if ( m_type != "flat" && m_type != "id_path" ) { cerr << classname << ": Unknown type: " << m_type << endl; cerr << m_dir << endl; m_error = 13; return; } // Generate DTD if it is not present. // It is stored in the top-level directory and copied to subdirectories // as needed. string dtdname = m_dir + "/" + "dataset.dtd"; FileStatus dtd(dtdname); if ( ! dtd.exists() ) { DtdRegistry::instance("dataset").text().write(dtdname); dtd.update(); } // Check that the DTD is accessible. if ( !dtd.is_regular() || !dtd.is_readable() ) { m_error = 15; m_dir = ""; return; } m_dtd = Text(dtdname); } //********************************************************************** // Destructor. FileGenericRepository::~FileGenericRepository() { } //********************************************************************** // Const member functions. //********************************************************************** // Validity. bool FileGenericRepository::is_valid() const { if( m_error >= 9 ) return false; string descfile = m_dir + "/" + "GenericRepository.txt"; Text desc(descfile); if(desc.size() == 0 || desc.line(0) != "FileGenericRepository") { m_error = 8; return false; } return true; } //********************************************************************** // Error. int FileGenericRepository::error() const { return m_error; } //********************************************************************** // Error string. string FileGenericRepository::error_string(int ecode) const { if ( ecode == 0) return "No error"; else if ( ecode == 1 ) return "Directory is empty"; else if ( ecode == 2 ) return "File already exists"; else if ( ecode == 3 ) return "File doesn't exist"; else if ( ecode == 4 ) return "Invalid directory"; else if ( ecode == 5 ) return "Invalid element"; else if ( ecode == 6 ) return "Error in writing element to file"; else if ( ecode == 7 ) return "File not readable"; else if ( ecode == 8 ) return "Invalid description file"; else if ( ecode == 9 ) return "Type of repository not initialized"; else if ( ecode == 10 ) return "Directory not initialized"; else if ( ecode == 11 ) return "Directory not readable"; else if ( ecode == 12 ) return "Inconsistent description"; else if ( ecode == 13 ) return "Unknown type"; else if ( ecode == 14 ) return "Unknown repository type"; else if ( ecode == 15 ) return "DTD not readable"; else return "Unknown error"; } //********************************************************************** // Return the type of repository. // Returns an empty string on error. string FileGenericRepository::repository_type() const { string descfile = m_dir + "/" + "GenericRepository.txt"; Text desc(descfile); if ( desc.size() == 0 || desc.line(0) != "FileGenericRepository" ) { m_error = 8; return ""; } return desc.line(1); } //********************************************************************** // Return the DB type. Type FileGenericRepository::type() const { return m_type; } //********************************************************************** // Check if file is already present in DB. bool FileGenericRepository::has(string id) const { FileStatus fstat(filename(m_dir, m_type, id)); if (!fstat.exists()) m_error = 3; return fstat.exists(); } //********************************************************************** // Read a file from DB. string FileGenericRepository::get(string id) const { FileStatus fstat(filename(m_dir, m_type, id)); if (!fstat.exists()) { m_error = 3; return ""; } // Write the DTD if missing. // We use the version in the to dataset directory. string dtdfilename = FileName(fstat.name()).directory_name() + "/dataset.dtd"; if ( ! FileStatus(dtdfilename).is_readable() ) { m_dtd.write(dtdfilename); } if ( !fstat.is_regular() || !fstat.is_readable() ) { m_error = 7; return ""; } string res; Text txt(fstat.name()); unsigned int lno = 0; while (lno < txt.size()) { string line = txt.line(lno); if(!line.empty()) res += line + "\n"; ++lno; } return res; } //********************************************************************** // Insert new element. int FileGenericRepository::insert(string id, string element) const { if ( ! authorized(element) ) return 9; // Check that file does not exist. FileStatus fstat(filename(m_dir, m_type, id)); if ( fstat.exists() ) { m_error = 2; return 1; } // Create directory. if ( m_type == "id_path" ) { FileName fname(fstat.name()); string dirname = fname.directory_name(); if ( ! FileStatus(dirname).is_directory() ) { mkfulldir(dirname); if ( ! FileStatus(dirname).is_directory() ) { m_error = 4; return 1; } } } // Create file. XmlParser par; const XmlElement* pele = par.parse(element); if ( pele == 0 ) { delete pele; m_error = 5; return 1; } int stat = par.write(fstat.name(), *pele); delete pele; if ( stat != 0 ) { m_error = 6; return 1; } // Make sure the file now exists. fstat.update(); bool file_ok = fstat.is_regular() && fstat.is_readable(); if ( ! file_ok ) { m_error = 7; return 1; } return 0; } //********************************************************************** // Remove an element. int FileGenericRepository::remove(string id) const { if ( ! authorized(get(id)) ) return 9; // Check if file exists if( !has(id) ) { m_error = 3; return 1; } FileStatus fstat(filename(m_dir, m_type, id)); string cmd = "rm " + fstat.name(); system(cmd.c_str()); return 0; } //********************************************************************** // Retrieve the time an entry was made. time_t FileGenericRepository::time(string sid) const { FileStatus fstat(filename(m_dir, m_type, sid)); return fstat.modtime(); } //********************************************************************** // Get the number of entries in the repository. size_type FileGenericRepository::size() const { const Text& txt = idfiles(m_dir); return txt.size(); } //********************************************************************** // Get the number of entries in the repository since. size_type FileGenericRepository::size_since(time_t) const { cerr << "FileGenericRepository::size_since: Not implemented" << endl; return 0; } //********************************************************************** // Get all the IDs in the repository. IdList FileGenericRepository::get_ids(size_type maxent) const { GenericRepository::IdList ids; const Text& txt = idfiles(m_dir); if ( txt.size() == 0 ) { m_error = 1; return ids; } unsigned int lno = 0; unsigned int maxlines = txt.size(); if ( maxlines > maxent ) maxlines = maxent; string::size_type ipos; string line, fname; while ( lno < maxlines ) { line = txt.line(lno); if ( ! line.empty() ) { // Extract only the filenames from the absolute pathnames ipos = line.rfind('/'); if ( ipos != string::npos ) { fname = line.substr(ipos+1); ids.push_back(fname); } } ++lno; } idfiles(m_dir, "delete"); return ids; } //********************************************************************** // Get all the IDs in the repository. IdList FileGenericRepository::get_ids_since( time_t time, size_type maxent) const { GenericRepository::IdList ids; const Text& txt = idfiles(m_dir); if ( txt.size() == 0 ) { m_error = 1; return ids; } unsigned int lno = 0; unsigned int maxlines = txt.size(); if ( maxlines > maxent ) maxlines = maxent; string::size_type ipos; string line, fname; while ( lno < maxlines ) { line = txt.line(lno); if ( ! line.empty() ) { // Extract only the filenames from the absolute pathnames ipos = line.rfind('/'); if ( ipos != string::npos ) { fname = line.substr(ipos+1); bool keep = true; if ( time > 0 ) { FileStatus fstat(fname); keep = fstat.modtime() >= time; } if ( keep ) { ids.push_back(fname); } } } ++lno; } idfiles(m_dir, "delete"); return ids; } //********************************************************************** // Output stream. ostream& FileGenericRepository::ostr(ostream& str) const { if ( ! is_valid() ) { str << "Invalid FileGenericRepository"; return str; } str << "FileGenericRepository at " << m_dir; return str; } //********************************************************************** // Const member functions. //********************************************************************** // Free functions. //********************************************************************** // Output stream. ostream& operator<<(ostream& lhs, const FileGenericRepository& rhs) { return rhs.ostr(lhs); } //**********************************************************************