LCOV - code coverage report
Current view: top level - oks/src - file.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 34.0 % 755 257
Test Date: 2026-10-11 15:05:20 Functions: 37.5 % 48 18

            Line data    Source code
       1              : // DUNE DAQ modification notice:
       2              : // This file has been modified from the original ATLAS oks source for the DUNE DAQ project.
       3              : // Fork baseline commit: oks-08-03-04 (2022-04-14).
       4              : // Renamed since fork: no.
       5              : 
       6              : #define _OksBuildDll_
       7              : 
       8              : #include "oks/file.hpp"
       9              : #include "oks/cstring.hpp"
      10              : #include "oks/kernel.hpp"
      11              : #include "oks/xml.hpp"
      12              : 
      13              : #include "oks_utils.hpp"
      14              : 
      15              : #include <errno.h>
      16              : #include <stdlib.h>
      17              : #include <string.h>
      18              : #include <sys/stat.h>
      19              : #include <unistd.h>
      20              : 
      21              : #include <fstream>
      22              : #include <sstream>
      23              : #include <stdexcept>
      24              : 
      25              : #include <boost/date_time/posix_time/posix_time_types.hpp>
      26              : #include <boost/date_time/posix_time/time_formatters.hpp>
      27              : #include <boost/date_time/posix_time/time_parsers.hpp>
      28              : #include <boost/interprocess/sync/file_lock.hpp>
      29              : 
      30              : #include "ers/ers.hpp"
      31              : #include "logging/Logging.hpp"
      32              : 
      33              : namespace dunedaq {
      34              : namespace oks {
      35              : // Nasty hack to allow code generation on the fly without locking
      36              : // file. Off by default
      37              : bool OksFile::p_nolock_mode = false;
      38              : //
      39              : // Define XML formats used to store OKS schema and data files
      40              : //
      41              : 
      42              : const char OksFile::xml_info_tag[] = "info";
      43              : const char OksFile::xml_include_tag[] = "include";
      44              : const char OksFile::xml_file_tag[] = "file";
      45              : const char OksFile::xml_comments_tag[] = "comments";
      46              : const char OksFile::xml_comment_tag[] = "comment";
      47              : 
      48              : const char OksFile::xml_file_header[] = "<?xml version=\"1.0\" encoding=\"ASCII\"?>";
      49              : 
      50              : const char OksFile::xml_schema_file_dtd[] =
      51              :   "<!DOCTYPE oks-schema [\n"
      52              : 
      53              :   "  <!ELEMENT oks-schema (info, (include)?, (comments)?, (class)+)>\n"
      54              : 
      55              :   "  <!ELEMENT info EMPTY>\n"
      56              :   "  <!ATTLIST info\n"
      57              :   "      name CDATA #IMPLIED\n"
      58              :   "      type CDATA #IMPLIED\n"
      59              :   "      num-of-items CDATA #REQUIRED\n"
      60              :   "      oks-format CDATA #FIXED \"schema\"\n"
      61              :   "      oks-version CDATA #REQUIRED\n"
      62              :   "      created-by CDATA #IMPLIED\n"
      63              :   "      created-on CDATA #IMPLIED\n"
      64              :   "      creation-time CDATA #IMPLIED\n"
      65              :   "      last-modified-by CDATA #IMPLIED\n"
      66              :   "      last-modified-on CDATA #IMPLIED\n"
      67              :   "      last-modification-time CDATA #IMPLIED\n"
      68              :   "  >\n"
      69              : 
      70              :   "  <!ELEMENT include (file)+>\n"
      71              :   "  <!ELEMENT file EMPTY>\n"
      72              :   "  <!ATTLIST file\n"
      73              :   "      path CDATA #REQUIRED\n"
      74              :   "  >\n"
      75              : 
      76              :   "  <!ELEMENT comments (comment)+>\n"
      77              :   "  <!ELEMENT comment EMPTY>\n"
      78              :   "  <!ATTLIST comment\n"
      79              :   "      creation-time CDATA #REQUIRED\n"
      80              :   "      created-by CDATA #REQUIRED\n"
      81              :   "      created-on CDATA #REQUIRED\n"
      82              :   "      author CDATA #REQUIRED\n"
      83              :   "      text CDATA #REQUIRED\n"
      84              :   "  >\n"
      85              : 
      86              :   "  <!ELEMENT class (superclass | attribute | relationship | method)*>\n"
      87              :   "  <!ATTLIST class\n"
      88              :   "      name CDATA #REQUIRED\n"
      89              :   "      description CDATA \"\"\n"
      90              :   "      is-abstract (yes|no) \"no\"\n"
      91              :   "  >\n"
      92              : 
      93              :   "  <!ELEMENT superclass EMPTY>\n"
      94              :   "  <!ATTLIST superclass name CDATA #REQUIRED>\n"
      95              : 
      96              :   "  <!ELEMENT attribute EMPTY>\n"
      97              :   "  <!ATTLIST attribute\n"
      98              :   "      name CDATA #REQUIRED\n"
      99              :   "      description CDATA \"\"\n"
     100              :   "      type (bool|s8|u8|s16|u16|s32|u32|s64|u64|float|double|date|time|string|uid|enum|class) #REQUIRED\n"
     101              :   "      range CDATA \"\"\n"
     102              :   "      format (dec|hex|oct) \"dec\"\n"
     103              :   "      is-multi-value (yes|no) \"no\"\n"
     104              :   "      init-value CDATA \"\"\n"
     105              :   "      is-not-null (yes|no) \"no\"\n"
     106              :   "      ordered (yes|no) \"no\"\n"
     107              :   "  >\n"
     108              : 
     109              :   "  <!ELEMENT relationship EMPTY>\n"
     110              :   "  <!ATTLIST relationship\n"
     111              :   "      name CDATA #REQUIRED\n"
     112              :   "      description CDATA \"\"\n"
     113              :   "      class-type CDATA #REQUIRED\n"
     114              :   "      low-cc (zero|one) #REQUIRED\n"
     115              :   "      high-cc (one|many) #REQUIRED\n"
     116              :   "      is-composite (yes|no) #REQUIRED\n"
     117              :   "      is-exclusive (yes|no) #REQUIRED\n"
     118              :   "      is-dependent (yes|no) #REQUIRED\n"
     119              :   "      ordered (yes|no) \"no\"\n"
     120              :   "  >\n"
     121              : 
     122              :   "  <!ELEMENT method (method-implementation*)>\n"
     123              :   "  <!ATTLIST method\n"
     124              :   "      name CDATA #REQUIRED\n"
     125              :   "      description CDATA \"\"\n"
     126              :   "  >\n"
     127              : 
     128              :   "  <!ELEMENT method-implementation EMPTY>\n"
     129              :   "  <!ATTLIST method-implementation\n"
     130              :   "      language CDATA #REQUIRED\n"
     131              :   "      prototype CDATA #REQUIRED\n"
     132              :   "      body CDATA \"\"\n"
     133              :   "  >\n"
     134              : 
     135              :   "]>";
     136              : 
     137              : const char OksFile::xml_data_file_dtd[] =
     138              :   "<!DOCTYPE oks-data [\n"
     139              : 
     140              :   "  <!ELEMENT oks-data (info, (include)?, (comments)?, (obj)+)>\n"
     141              : 
     142              :   "  <!ELEMENT info EMPTY>\n"
     143              :   "  <!ATTLIST info\n"
     144              :   "      name CDATA #IMPLIED\n"
     145              :   "      type CDATA #IMPLIED\n"
     146              :   "      num-of-items CDATA #REQUIRED\n"
     147              :   "      oks-format CDATA #FIXED \"data\"\n"
     148              :   "      oks-version CDATA #REQUIRED\n"
     149              :   "      created-by CDATA #IMPLIED\n"
     150              :   "      created-on CDATA #IMPLIED\n"
     151              :   "      creation-time CDATA #IMPLIED\n"
     152              :   "      last-modified-by CDATA #IMPLIED\n"
     153              :   "      last-modified-on CDATA #IMPLIED\n"
     154              :   "      last-modification-time CDATA #IMPLIED\n"
     155              :   "  >\n"
     156              : 
     157              :   "  <!ELEMENT include (file)*>\n"
     158              :   "  <!ELEMENT file EMPTY>\n"
     159              :   "  <!ATTLIST file\n"
     160              :   "      path CDATA #REQUIRED\n"
     161              :   "  >\n"
     162              : 
     163              :   "  <!ELEMENT comments (comment)*>\n"
     164              :   "  <!ELEMENT comment EMPTY>\n"
     165              :   "  <!ATTLIST comment\n"
     166              :   "      creation-time CDATA #REQUIRED\n"
     167              :   "      created-by CDATA #REQUIRED\n"
     168              :   "      created-on CDATA #REQUIRED\n"
     169              :   "      author CDATA #REQUIRED\n"
     170              :   "      text CDATA #REQUIRED\n"
     171              :   "  >\n"
     172              : 
     173              :   "  <!ELEMENT obj (attr | rel)*>\n"
     174              :   "  <!ATTLIST obj\n"
     175              :   "      class CDATA #REQUIRED\n"
     176              :   "      id CDATA #REQUIRED\n"
     177              :   "  >\n"
     178              :   "  <!ELEMENT attr (data)*>\n"
     179              :   "  <!ATTLIST attr\n"
     180              :   "      name CDATA #REQUIRED\n"
     181              :   "      type (bool|s8|u8|s16|u16|s32|u32|s64|u64|float|double|date|time|string|uid|enum|class|-) \"-\"\n"
     182              :   "      val CDATA \"\"\n"
     183              :   "  >\n"
     184              :   "  <!ELEMENT data EMPTY>\n"
     185              :   "  <!ATTLIST data\n"
     186              :   "      val CDATA #REQUIRED\n"
     187              :   "  >\n"
     188              :   "  <!ELEMENT rel (ref)*>\n"
     189              :   "  <!ATTLIST rel\n"
     190              :   "      name CDATA #REQUIRED\n"
     191              :   "      class CDATA \"\"\n"
     192              :   "      id CDATA \"\"\n"
     193              :   "  >\n"
     194              :   "  <!ELEMENT ref EMPTY>\n"
     195              :   "  <!ATTLIST ref\n"
     196              :   "      class CDATA #REQUIRED\n"
     197              :   "      id CDATA #REQUIRED\n"
     198              :   "  >\n"
     199              :   "]>";
     200              : 
     201              : std::string
     202            0 : FailedAddInclude::fill(const OksFile& file, const std::string& include_name, const std::string& reason) noexcept
     203              : {
     204            0 :   return (std::string("Failed to add include \'") + include_name + "\' to file \'" + file.get_full_file_name() +
     205            0 :           "\' because:\n" + reason);
     206              : }
     207              : 
     208              : std::string
     209            0 : FailedRemoveInclude::fill(const OksFile& file, const std::string& include_name, const std::string& reason) noexcept
     210              : {
     211            0 :   return (std::string("Failed to remove include \'") + include_name + "\' from file \'" + file.get_full_file_name() +
     212            0 :           "\' because:\n" + reason);
     213              : }
     214              : 
     215              : std::string
     216            0 : FailedRenameInclude::fill(const OksFile& file,
     217              :                           const std::string& from,
     218              :                           const std::string& to,
     219              :                           const std::string& reason) noexcept
     220              : {
     221            0 :   return (std::string("Failed to rename include of \'") + file.get_full_file_name() + "\' from \'" + from + "\' to \'" +
     222            0 :           to + "\' because:\n" + reason);
     223              : }
     224              : 
     225              : std::string
     226            0 : FailedAddComment::fill(const OksFile& file, const std::string& reason) noexcept
     227              : {
     228            0 :   return (std::string("Failed to add new comment to file \'") + file.get_full_file_name() + "\' because:\n" + reason);
     229              : }
     230              : 
     231              : std::string
     232            0 : FailedRemoveComment::fill(const OksFile& file, const std::string& creation_time, const std::string& reason) noexcept
     233              : {
     234            0 :   return (std::string("Failed to remove comment created at \'") + creation_time + "\' from file \'" +
     235            0 :           file.get_full_file_name() + "\' because:\n" + reason);
     236              : }
     237              : 
     238              : std::string
     239            0 : FailedChangeComment::fill(const OksFile& file, const std::string& creation_time, const std::string& reason) noexcept
     240              : {
     241            0 :   return (std::string("Failed to change comment created at \'") + creation_time + "\' from file \'" +
     242            0 :           file.get_full_file_name() + "\' because:\n" + reason);
     243              : }
     244              : 
     245              : std::string
     246            0 : FileLockError::fill(const OksFile& file, bool lock_action, const std::string& reason) noexcept
     247              : {
     248            0 :   return (std::string("Failed to ") + (lock_action ? "" : "un") + "lock file \'" + file.get_full_file_name() +
     249            0 :           "\' because:\n" + reason);
     250              : }
     251              : 
     252              : std::string
     253            0 : FileChangeError::fill(const OksFile& file, const std::string& action, const std::string& reason) noexcept
     254              : {
     255            0 :   return (std::string("Failed to ") + action + " of file \'" + file.get_full_file_name() + "\' because:\n" + reason);
     256              : }
     257              : 
     258              : std::string
     259            0 : FileCompareError::fill(const std::string& src, const std::string& dest, const std::string& reason) noexcept
     260              : {
     261            0 :   return (std::string("Failed to compare file \'") + src + "\' with \'" + dest + "\' because:\n" + reason);
     262              : }
     263              : 
     264            5 : OksFile::OksFile(const std::string& s,
     265              :                  const std::string& ln,
     266              :                  const std::string& ft,
     267              :                  const std::string& ff,
     268            5 :                  OksKernel* k)
     269            5 :   : p_short_name(s)
     270            5 :   , p_full_name(s)
     271            5 :   , p_logical_name(ln)
     272            5 :   , p_type(ft)
     273            5 :   , p_oks_format(ff)
     274            5 :   , p_number_of_items(0)
     275            5 :   , p_size(0)
     276            5 :   , p_created_by(OksKernel::get_user_name())
     277            5 :   , p_creation_time(boost::posix_time::second_clock::universal_time())
     278            5 :   , p_created_on(OksKernel::get_host_name())
     279            5 :   , p_last_modification_time(p_creation_time)
     280            5 :   , p_lock()
     281            5 :   , p_is_updated(false)
     282            5 :   , p_is_read_only(true)
     283            5 :   , p_last_modified(0)
     284            5 :   , p_repository_last_modified(0)
     285            5 :   , p_is_on_disk(false)
     286            5 :   , p_included_by(0)
     287           15 :   , p_kernel(k)
     288              : {
     289            5 :   p_last_modified_on = p_created_on;
     290            5 :   p_last_modified_by = p_created_by;
     291            5 :   create_lock_name();
     292            5 :   check_repository();
     293            5 : }
     294              : 
     295              : void
     296          320 : OksFile::clear_comments() noexcept
     297              : {
     298         1193 :   for (std::map<std::string, oks::Comment*>::iterator i = p_comments.begin(); i != p_comments.end(); ++i) {
     299          873 :     delete i->second;
     300              :   }
     301              : 
     302          320 :   p_comments.clear();
     303          320 : }
     304              : 
     305          320 : OksFile::~OksFile() noexcept
     306              : {
     307          320 :   clear_comments();
     308          320 : }
     309              : 
     310              : OksFile&
     311            0 : OksFile::operator=(const OksFile& f)
     312              : {
     313            0 :   if (&f != this) {
     314            0 :     p_list_of_include_files.clear();
     315            0 :     clear_comments();
     316              : 
     317            0 :     p_short_name = f.p_short_name;
     318            0 :     p_full_name = f.p_full_name;
     319              :     //      p_repository = f.p_repository;
     320            0 :     p_repository_name = f.p_repository_name;
     321            0 :     p_logical_name = f.p_logical_name;
     322            0 :     p_type = f.p_type;
     323            0 :     p_oks_format = f.p_oks_format;
     324            0 :     p_number_of_items = f.p_number_of_items;
     325            0 :     p_size = f.p_size;
     326            0 :     p_created_by = f.p_created_by;
     327            0 :     p_creation_time = f.p_creation_time;
     328            0 :     p_created_on = f.p_created_on;
     329            0 :     p_last_modified_by = f.p_last_modified_by;
     330            0 :     p_last_modification_time = f.p_last_modification_time;
     331            0 :     p_last_modified_on = f.p_last_modified_on;
     332            0 :     p_open_mode = f.p_open_mode;
     333            0 :     p_is_updated = f.p_is_updated;
     334            0 :     p_lock = f.p_lock;
     335            0 :     p_is_read_only = f.p_is_read_only;
     336            0 :     p_lock_file_name = f.p_lock_file_name;
     337            0 :     p_list_of_include_files = f.p_list_of_include_files;
     338            0 :     p_last_modified = f.p_last_modified;
     339            0 :     p_repository_last_modified = f.p_repository_last_modified;
     340            0 :     p_is_on_disk = f.p_is_on_disk;
     341            0 :     p_included_by = f.p_included_by;
     342            0 :     p_kernel = f.p_kernel;
     343              : 
     344            0 :     for (const auto& i : f.p_comments) {
     345            0 :       p_comments[i.first] = new oks::Comment(*i.second);
     346              :     }
     347              :   }
     348              : 
     349            0 :   return *this;
     350              : }
     351              : 
     352              : // read xml-file header from file
     353              : 
     354              : const char _empty_str[] = "empty";
     355              : 
     356          315 : OksFile::OksFile(std::shared_ptr<OksXmlInputStream> xmls, const std::string& sp, const std::string& fp, OksKernel* k)
     357          315 :   : p_short_name(sp)
     358          315 :   , p_full_name(fp)
     359          315 :   , p_oks_format(_empty_str, sizeof(_empty_str) - 1)
     360          315 :   , p_creation_time(boost::posix_time::not_a_date_time)
     361          315 :   , p_last_modification_time(boost::posix_time::not_a_date_time)
     362          315 :   , p_lock()
     363          315 :   , p_is_updated(false)
     364          315 :   , p_is_read_only(true)
     365          315 :   , p_last_modified(0)
     366          315 :   , p_repository_last_modified(0)
     367          315 :   , p_is_on_disk(true)
     368          315 :   , p_included_by(0)
     369         1575 :   , p_kernel(k)
     370              : {
     371          315 :   const char* fname = "OksFile::OksFile()";
     372              : 
     373              :   //  create_lock_name();
     374          315 :   check_repository();
     375              : 
     376              :   // check file header
     377              : 
     378          315 :   try {
     379              : 
     380              :     // skip an xml file header
     381              : 
     382          315 :     {
     383          315 :       const char* tag_start = xmls->get_tag_start();
     384              : 
     385          315 :       if (!oks::cmp_str4(tag_start, "?xml")) {
     386            0 :         xmls->throw_unexpected_tag(tag_start, "?xml");
     387              :       }
     388              :     }
     389              : 
     390              :     // skip possible tag attributes
     391              : 
     392         1575 :     while (true) {
     393          945 :       OksXmlAttribute attr(*xmls);
     394              : 
     395              :       // check for close of tag
     396              : 
     397          945 :       if (oks::cmp_str1(attr.name(), ">") || oks::cmp_str1(attr.name(), "/") || oks::cmp_str1(attr.name(), "?")) {
     398              :         break;
     399              :       }
     400              : 
     401              :       // check for start of info tag
     402              : 
     403          630 :       if (!oks::cmp_str7(attr.name(), "version") && !oks::cmp_str8(attr.name(), "encoding")) {
     404            0 :         if (!p_kernel->get_silence_mode()) {
     405            0 :           Oks::warning_msg(fname) << "  Unexpected attribute \'" << attr.name()
     406            0 :                                   << "\' in the xml file header. Either \'version\' or \'encoding\' is expected.\n";
     407              :         }
     408              :       }
     409          630 :     }
     410              : 
     411            0 :   } catch (oks::exception& e) {
     412            0 :     throw oks::FailedRead("an xml file header", e);
     413            0 :   } catch (std::exception& e) {
     414            0 :     throw oks::FailedRead("an xml file header", e.what());
     415            0 :   }
     416              : 
     417              :   // skip document type declaration and comments
     418              : 
     419          315 :   {
     420          315 :     while (true) {
     421          315 :       try {
     422          315 :         const char* dtd = xmls->get_tag();
     423          315 :         const char dtd_start[] = "<!DOCTYPE ";
     424          315 :         if (!strncmp(dtd_start, dtd, sizeof(dtd_start) - 1)) {
     425              :           break;
     426              :         }
     427            0 :       } catch (oks::exception& e) {
     428            0 :         throw oks::FailedRead("DTD section", e);
     429            0 :       } catch (std::exception& e) {
     430            0 :         throw oks::FailedRead("DTD section", e.what());
     431            0 :       }
     432            0 :     }
     433              :   }
     434              : 
     435              :   // skip
     436              : 
     437          315 :   try {
     438          315 :     xmls->store_position();
     439              : 
     440          315 :     const char* a_tag = xmls->get_tag();
     441              : 
     442          315 :     const char xml_stylesheet_start[] = "<?xml-stylesheet";
     443          315 :     if (strncmp(xml_stylesheet_start, a_tag, sizeof(xml_stylesheet_start) - 1)) {
     444          315 :       xmls->restore_position();
     445              :     }
     446            0 :   } catch (oks::exception& e) {
     447            0 :     throw oks::FailedRead("an xml-stylesheet tag or an oks tag", e);
     448            0 :   } catch (std::exception& e) {
     449            0 :     throw oks::FailedRead("an xml-stylesheet tag or an oks tag", e.what());
     450            0 :   }
     451              : 
     452              :   // read start-of-tag
     453              : 
     454          315 :   try {
     455          315 :     xmls->get_tag_start();
     456            0 :   } catch (oks::exception& e) {
     457            0 :     throw oks::FailedRead("tag", e);
     458            0 :   } catch (std::exception& e) {
     459            0 :     throw oks::FailedRead("tag", e.what());
     460            0 :   }
     461              : 
     462              :   // read info tag
     463              : 
     464          315 :   try {
     465          315 :     const char* tag_start = xmls->get_tag_start();
     466              : 
     467          315 :     if (!oks::cmp_str4(tag_start, xml_info_tag)) {
     468            0 :       xmls->throw_unexpected_tag(tag_start, xml_info_tag);
     469              :     }
     470            0 :   } catch (oks::exception& e) {
     471            0 :     throw oks::FailedRead("<info> tag", e);
     472            0 :   } catch (std::exception& e) {
     473            0 :     throw oks::FailedRead("<info> tag", e.what());
     474            0 :   }
     475              : 
     476          315 :   std::string oks_version;
     477              : 
     478              :   // read class attributes
     479              : 
     480         3780 :   try {
     481         3780 :     while (true) {
     482         3780 :       OksXmlAttribute attr(*xmls);
     483              : 
     484              :       // check for close of tag
     485              : 
     486         3780 :       if (oks::cmp_str1(attr.name(), ">") || oks::cmp_str1(attr.name(), "/")) {
     487              :         break;
     488              :       }
     489              : 
     490              :       // check for known info' attributes
     491              : 
     492         3465 :       else if (oks::cmp_str4(attr.name(), "name"))
     493          315 :         p_logical_name.assign(attr.value(), attr.value_len());
     494         3150 :       else if (oks::cmp_str4(attr.name(), "type"))
     495          315 :         p_type.assign(attr.value(), attr.value_len());
     496         2835 :       else if (oks::cmp_str10(attr.name(), "oks-format"))
     497          315 :         p_oks_format.assign(attr.value(), attr.value_len());
     498         2520 :       else if (oks::cmp_str10(attr.name(), "created-by"))
     499          315 :         p_created_by.assign(attr.value(), attr.value_len());
     500         2205 :       else if (oks::cmp_str10(attr.name(), "created-on"))
     501          315 :         p_created_on.assign(attr.value(), attr.value_len());
     502         1890 :       else if (oks::cmp_str11(attr.name(), "oks-version"))
     503          315 :         oks_version.assign(attr.value(), attr.value_len());
     504         1575 :       else if (oks::cmp_str12(attr.name(), "num-of-items"))
     505          315 :         p_number_of_items = atol(attr.value());
     506         1260 :       else if (oks::cmp_str13(attr.name(), "creation-time"))
     507          315 :         p_creation_time = oks::str2time(attr.value(), attr.value_len(), p_full_name.c_str());
     508          945 :       else if (oks::cmp_str15(attr.name(), "num-of-includes"))
     509            0 :         continue; // ignore this value
     510          945 :       else if (oks::cmp_str16(attr.name(), "last-modified-by"))
     511          315 :         p_last_modified_by.assign(attr.value(), attr.value_len());
     512          630 :       else if (oks::cmp_str16(attr.name(), "last-modified-on"))
     513          315 :         p_last_modified_on.assign(attr.value(), attr.value_len());
     514          315 :       else if (!strcmp(attr.name(), "last-modification-time"))
     515          315 :         p_last_modification_time = oks::str2time(attr.value(), attr.value_len(), p_full_name.c_str());
     516              :       else {
     517            0 :         p_oks_format.clear();
     518            0 :         xmls->throw_unexpected_tag(attr.name(), "info\'s tags");
     519              :       }
     520              :     }
     521            0 :   } catch (oks::exception& e) {
     522            0 :     throw oks::FailedRead("<info> tag attribute", e);
     523            0 :   } catch (std::exception& e) {
     524            0 :     throw oks::FailedRead("<info> tag attribute", e.what());
     525            0 :   }
     526              : 
     527              :   // read include and comments sections
     528          535 :   while (true) {
     529          535 :     xmls->store_position();
     530              : 
     531          535 :     bool read_include = false;
     532              : 
     533          535 :     try {
     534          535 :       const char* tag_start = xmls->get_tag_start();
     535          535 :       if (oks::cmp_str7(tag_start, xml_include_tag)) {
     536              :         read_include = true;
     537          315 :       } else if (oks::cmp_str8(tag_start, xml_comments_tag)) {
     538              :         read_include = false;
     539              :       } else {
     540          191 :         xmls->restore_position();
     541              :         return;
     542              :       }
     543            0 :     } catch (oks::exception& e) {
     544            0 :       throw oks::FailedRead("a tag", e);
     545            0 :     } catch (std::exception& e) {
     546            0 :       throw oks::FailedRead("a tag", e.what());
     547            0 :     }
     548              : 
     549          344 :     if (read_include) {
     550          934 :       while (true) {
     551          577 :         try {
     552              : 
     553          577 :           {
     554          577 :             const char* tag_start = xmls->get_tag_start();
     555          577 :             if (!oks::cmp_str4(tag_start, xml_file_tag)) {
     556          220 :               if (!oks::cmp_str8(tag_start, "/include")) {
     557            0 :                 xmls->throw_unexpected_tag(tag_start + 1, xml_file_tag);
     558              :               }
     559              :               break;
     560              :             }
     561              :           }
     562              : 
     563         1071 :           while (true) {
     564          714 :             OksXmlAttribute attr(*xmls);
     565              : 
     566              :             // check for close of tag
     567          714 :             if (oks::cmp_str1(attr.name(), ">") || oks::cmp_str1(attr.name(), "/")) {
     568              :               break;
     569              :             }
     570              : 
     571              :             // check for known info' attributes
     572          357 :             else if (oks::cmp_str4(attr.name(), "path"))
     573          357 :               p_list_of_include_files.push_back(std::string(attr.value(), attr.value_len()));
     574              :             else {
     575            0 :               p_oks_format.clear();
     576            0 :               xmls->throw_unexpected_tag(attr.name(), "path");
     577              :             }
     578          357 :           }
     579              : 
     580            0 :         } catch (oks::exception& e) {
     581            0 :           throw oks::FailedRead("<file> tag", e);
     582            0 :         } catch (std::exception& e) {
     583            0 :           throw oks::FailedRead("<file> tag", e.what());
     584            0 :         }
     585          357 :       }
     586              :     } else {
     587         1870 :       while (true) {
     588          997 :         try {
     589              : 
     590          997 :           {
     591          997 :             const char* tag_start = xmls->get_tag_start();
     592          997 :             if (!oks::cmp_str7(tag_start, xml_comment_tag)) {
     593          124 :               if (!oks::cmp_str9(tag_start, "/comments")) {
     594            0 :                 xmls->throw_unexpected_tag(tag_start, xml_comment_tag);
     595              :               }
     596          124 :               return;
     597              :             }
     598              :           }
     599              : 
     600          873 :           std::unique_ptr<oks::Comment> comment(new oks::Comment());
     601          873 :           std::string creation_time;
     602              : 
     603         9603 :           while (true) {
     604         5238 :             OksXmlAttribute attr(*xmls);
     605              : 
     606              :             // check for close of tag
     607         5238 :             if (oks::cmp_str1(attr.name(), ">") || oks::cmp_str1(attr.name(), "/")) {
     608              :               break;
     609              :             }
     610              : 
     611              :             // check for known info' attributes
     612         4365 :             else if (oks::cmp_str4(attr.name(), "text"))
     613          873 :               comment->p_text.assign(attr.value(), attr.value_len());
     614         3492 :             else if (oks::cmp_str6(attr.name(), "author"))
     615          873 :               comment->p_author.assign(attr.value(), attr.value_len());
     616         2619 :             else if (oks::cmp_str10(attr.name(), "created-by"))
     617          873 :               comment->p_created_by.assign(attr.value(), attr.value_len());
     618         1746 :             else if (oks::cmp_str10(attr.name(), "created-on"))
     619          873 :               comment->p_created_on.assign(attr.value(), attr.value_len());
     620          873 :             else if (oks::cmp_str13(attr.name(), "creation-time"))
     621          873 :               creation_time.assign(attr.value(), attr.value_len());
     622              :             else {
     623            0 :               p_oks_format.clear();
     624            0 :               xmls->throw_unexpected_tag(attr.name(), "comment\'s tags");
     625              :             }
     626         4365 :           }
     627              : 
     628          873 :           comment->validate(creation_time);
     629              : 
     630          873 :           if (p_comments.find(creation_time) != p_comments.end()) {
     631            0 :             std::ostringstream text;
     632            0 :             text << "The comment created at \'" << creation_time << "\' was already defined in this file";
     633            0 :             throw std::runtime_error(text.str().c_str());
     634            0 :           }
     635              : 
     636          873 :           p_comments[creation_time] = comment.release();
     637              : 
     638          873 :         } catch (oks::exception& e) {
     639            0 :           throw oks::FailedRead("<comment>", e);
     640            0 :         } catch (std::exception& e) {
     641            0 :           throw oks::FailedRead("<comment>", e.what());
     642            0 :         }
     643          873 :       }
     644              :     }
     645              :   }
     646          315 : }
     647              : 
     648              : void
     649          873 : oks::Comment::validate(const std::string& creation_time)
     650              : {
     651              :   // YYYYMMDDThhmmss
     652          873 :   if (creation_time.size() != 15 || creation_time[8] != 'T') {
     653            0 :     std::ostringstream msg;
     654            0 :     msg << "the creation time \'" << creation_time << "\' is not an ISO-8601 date-time";
     655            0 :     throw std::runtime_error(msg.str().c_str());
     656            0 :   }
     657              : 
     658          873 :   validate();
     659          873 : }
     660              : 
     661              : void
     662          873 : oks::Comment::validate()
     663              : {
     664          873 :   if (p_text.empty()) {
     665            0 :     throw std::runtime_error("text is empty");
     666              :   }
     667          873 : }
     668              : 
     669            0 : oks::Comment::Comment(const std::string& text, const std::string& author)
     670            0 :   : p_created_by(OksKernel::get_user_name())
     671            0 :   , p_author(author)
     672            0 :   , p_created_on(OksKernel::get_host_name())
     673            0 :   , p_text(text)
     674              : {
     675            0 :   validate();
     676            0 : }
     677              : 
     678            0 : oks::Comment::Comment(const oks::Comment& src) noexcept
     679            0 :   : p_created_by(src.p_created_by)
     680            0 :   , p_author(src.p_author)
     681            0 :   , p_created_on(src.p_created_on)
     682            0 :   , p_text(src.p_text)
     683              : {
     684            0 : }
     685              : 
     686              : void
     687            0 : OksFile::add_comment(const std::string& text, const std::string& author)
     688              : {
     689            0 :   try {
     690            0 :     lock();
     691            0 :   } catch (oks::exception& ex) {
     692            0 :     throw oks::FailedAddComment(*this, ex);
     693            0 :   }
     694              : 
     695            0 :   boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
     696            0 :   std::string creation_time = boost::posix_time::to_iso_string(now);
     697              : 
     698            0 :   try {
     699            0 :     std::unique_ptr<oks::Comment> comment(new oks::Comment(text, author));
     700            0 :     comment->validate();
     701              : 
     702            0 :     oks::Comment*& x(p_comments[creation_time]);
     703              : 
     704            0 :     if (x == 0) {
     705            0 :       x = comment.release();
     706              :     } else {
     707            0 :       x->p_text += "\n-----\n";
     708            0 :       x->p_text.append(text);
     709              :     }
     710            0 :   } catch (std::exception& ex) {
     711            0 :     throw oks::FailedAddComment(*this, ex.what());
     712            0 :   }
     713              : 
     714            0 :   p_is_updated = true;
     715            0 : }
     716              : 
     717              : oks::Comment*
     718            0 : OksFile::get_comment(const std::string& creation_time) noexcept
     719              : {
     720            0 :   std::map<std::string, oks::Comment*>::const_iterator i = p_comments.find(creation_time);
     721            0 :   return (i == p_comments.end()) ? 0 : i->second;
     722              : }
     723              : 
     724              : void
     725            0 : OksFile::modify_comment(const std::string& creation_time, const std::string& text, const std::string& author)
     726              : {
     727            0 :   if (oks::Comment* comment = get_comment(creation_time)) {
     728            0 :     try {
     729            0 :       lock();
     730            0 :     } catch (oks::exception& ex) {
     731            0 :       throw oks::FailedChangeComment(*this, creation_time, ex);
     732            0 :     }
     733              : 
     734            0 :     std::string saved_author(comment->p_author);
     735            0 :     std::string saved_text(comment->p_text);
     736              : 
     737            0 :     comment->p_author = author;
     738            0 :     comment->p_text = text;
     739              : 
     740            0 :     try {
     741            0 :       comment->validate();
     742            0 :     } catch (std::exception& ex) {
     743            0 :       comment->p_author = saved_author;
     744            0 :       comment->p_text = saved_text;
     745            0 :       throw oks::FailedChangeComment(*this, creation_time, ex.what());
     746            0 :     }
     747              : 
     748            0 :     p_is_updated = true;
     749            0 :   } else {
     750            0 :     throw oks::FailedChangeComment(*this, creation_time, "cannot find comment");
     751              :   }
     752            0 : }
     753              : 
     754              : void
     755            0 : OksFile::remove_comment(const std::string& creation_time)
     756              : {
     757            0 :   if (oks::Comment* comment = get_comment(creation_time)) {
     758            0 :     try {
     759            0 :       lock();
     760            0 :     } catch (oks::exception& ex) {
     761            0 :       throw oks::FailedRemoveComment(*this, creation_time, ex);
     762            0 :     }
     763              : 
     764            0 :     delete comment;
     765            0 :     p_comments.erase(creation_time);
     766            0 :     p_is_updated = true;
     767              :   } else {
     768            0 :     throw oks::FailedRemoveComment(*this, creation_time, "cannot find comment");
     769              :   }
     770            0 : }
     771              : 
     772              : void
     773            0 : OksFile::write(OksXmlOutputStream& xmls)
     774              : {
     775            0 :   const static std::string __data("data");
     776            0 :   const static std::string __schema("schema");
     777              : 
     778            0 :   try {
     779            0 :     const char __oks_data[] = "oks-data";
     780            0 :     const char __oks_schema[] = "oks-schema";
     781              : 
     782            0 :     const char* id(__oks_data);
     783            0 :     long id_len(sizeof(__oks_data) - 1);
     784            0 :     const char* dtd(xml_data_file_dtd);
     785            0 :     long dtd_len(sizeof(xml_data_file_dtd) - 1);
     786              : 
     787            0 :     if (p_oks_format != __data) {
     788            0 :       if (p_oks_format == __schema) {
     789              :         id = __oks_schema;
     790              :         id_len = sizeof(__oks_schema) - 1;
     791              :         dtd = xml_schema_file_dtd;
     792              :         dtd_len = sizeof(xml_schema_file_dtd) - 1;
     793              :       } else {
     794            0 :         throw std::runtime_error("bad oks format");
     795              :       }
     796              :     }
     797              : 
     798            0 :     {
     799            0 :       const char __hdr_start[] = "\n\n<!-- ";
     800            0 :       const char __hdr_end[] = " version 2.2 -->\n\n\n";
     801              : 
     802            0 :       std::string header(xml_file_header, sizeof(xml_file_header) - 1);
     803            0 :       header.append(__hdr_start, sizeof(__hdr_start) - 1);
     804            0 :       header.append(id, id_len);
     805            0 :       header.append(__hdr_end, sizeof(__hdr_end) - 1);
     806            0 :       header.append(dtd, dtd_len);
     807              : 
     808            0 :       xmls.put_raw(header.c_str(), header.size());
     809            0 :       xmls.put_raw('\n');
     810            0 :       xmls.put_raw('\n');
     811            0 :     }
     812              : 
     813            0 :     xmls.put_start_tag(id, id_len);
     814              : 
     815            0 :     xmls.put_raw('>');
     816            0 :     xmls.put_raw('\n');
     817            0 :     xmls.put_raw('\n');
     818              : 
     819              :     // "creation-time" may not be filled
     820              : 
     821            0 :     std::string created_c_str;
     822            0 :     if (!p_creation_time.is_not_a_date_time()) {
     823            0 :       created_c_str = boost::posix_time::to_iso_string(p_creation_time);
     824              :     }
     825              : 
     826              :     // get last-modified information
     827              : 
     828            0 :     if (p_repository_name.empty()) {
     829            0 :       p_last_modification_time = boost::posix_time::second_clock::universal_time();
     830            0 :       p_last_modified_by = OksKernel::get_user_name();
     831            0 :       p_last_modified_on = OksKernel::get_host_name();
     832              :     }
     833              : 
     834            0 :     std::string last_modified_c_str = boost::posix_time::to_iso_string(p_last_modification_time);
     835              : 
     836            0 :     xmls.put_start_tag(xml_info_tag, sizeof(xml_info_tag) - 1);
     837            0 :     xmls.put_attribute("name", sizeof("name") - 1, p_logical_name.c_str());
     838            0 :     xmls.put_attribute("type", sizeof("type") - 1, p_type.c_str());
     839            0 :     xmls.put_attribute("num-of-items", sizeof("num-of-items") - 1, p_number_of_items);
     840            0 :     xmls.put_attribute("oks-format", sizeof("oks-format") - 1, p_oks_format.c_str());
     841            0 :     xmls.put_attribute("oks-version", sizeof("oks-version") - 1, OksKernel::GetVersion());
     842            0 :     xmls.put_attribute("created-by", sizeof("created-by") - 1, p_created_by.c_str());
     843            0 :     xmls.put_attribute("created-on", sizeof("created-on") - 1, p_created_on.c_str());
     844            0 :     if (!created_c_str.empty())
     845            0 :       xmls.put_attribute("creation-time", sizeof("creation-time") - 1, created_c_str.c_str());
     846            0 :     if (p_repository_name.empty()) {
     847            0 :       xmls.put_attribute("last-modified-by", sizeof("last-modified-by") - 1, p_last_modified_by.c_str());
     848            0 :       xmls.put_attribute("last-modified-on", sizeof("last-modified-on") - 1, p_last_modified_on.c_str());
     849            0 :       xmls.put_attribute("last-modification-time", sizeof("last-modification-time") - 1, last_modified_c_str.c_str());
     850              :     }
     851            0 :     xmls.put_end_tag();
     852              : 
     853            0 :     if (!p_list_of_include_files.empty()) {
     854            0 :       xmls.put_raw('\n');
     855            0 :       xmls.put_start_tag(xml_include_tag, sizeof(xml_include_tag) - 1);
     856              : 
     857            0 :       xmls.put_raw('>');
     858            0 :       xmls.put_raw('\n');
     859              : 
     860            0 :       for (const auto& i : p_list_of_include_files) {
     861            0 :         xmls.put_raw(' ');
     862            0 :         xmls.put_start_tag(xml_file_tag, sizeof(xml_file_tag) - 1);
     863            0 :         xmls.put_attribute("path", sizeof("path") - 1, i.c_str());
     864            0 :         xmls.put_end_tag();
     865              :       }
     866              : 
     867            0 :       xmls.put_last_tag(xml_include_tag, sizeof(xml_include_tag) - 1);
     868            0 :       xmls.put_raw('\n');
     869              :     }
     870              : 
     871            0 :     if (!p_comments.empty()) {
     872            0 :       xmls.put_start_tag(xml_comments_tag, sizeof(xml_comments_tag) - 1);
     873              : 
     874            0 :       xmls.put_raw('>');
     875            0 :       xmls.put_raw('\n');
     876              : 
     877            0 :       for (const auto& i : p_comments) {
     878            0 :         xmls.put_raw(' ');
     879            0 :         xmls.put_start_tag(xml_comment_tag, sizeof(xml_comment_tag) - 1);
     880            0 :         xmls.put_attribute("creation-time", sizeof("creation-time") - 1, i.first.c_str());
     881            0 :         xmls.put_attribute("created-by", sizeof("created-by") - 1, i.second->p_created_by.c_str());
     882            0 :         xmls.put_attribute("created-on", sizeof("created-on") - 1, i.second->p_created_on.c_str());
     883            0 :         xmls.put_attribute("author", sizeof("author") - 1, i.second->p_author.c_str());
     884            0 :         xmls.put_attribute("text", sizeof("text") - 1, i.second->p_text.c_str());
     885            0 :         xmls.put_end_tag();
     886              :       }
     887              : 
     888            0 :       xmls.put_last_tag(xml_comments_tag, sizeof(xml_comments_tag) - 1);
     889            0 :       xmls.put_raw('\n');
     890              :     }
     891              : 
     892            0 :     xmls.put_raw('\n');
     893            0 :   } catch (std::exception& ex) {
     894            0 :     throw oks::FailedSave("oks-file", p_full_name, ex.what());
     895            0 :   }
     896              : 
     897            0 :   p_is_on_disk = true;
     898            0 : }
     899              : 
     900              : bool
     901            0 : OksFile::is_repository_file() const
     902              : {
     903            0 :   return (!p_kernel->get_user_repository_root().empty());
     904              : }
     905              : 
     906              : void
     907          320 : OksFile::check_repository()
     908              : {
     909              :   //  if(size_t len = OksKernel::get_repository_root().size()) {
     910              :   //    if(p_full_name.size() > len && p_full_name.substr(0, len) == OksKernel::get_repository_root()) {
     911              :   //      p_repository = GlobalRepository;
     912              :   //      p_repository_name = p_full_name.substr(len + 1);
     913              :   //      TLOG_DEBUG( 3 ) << "file \'" << p_full_name << "\' comes from user repository [\'" << p_repository_name <<
     914              :   //      "\']"; return;
     915              :   //    }
     916              :   //  }
     917              : 
     918          320 :   const std::string& s(p_kernel->get_user_repository_root());
     919          320 :   if (size_t len = s.size()) {
     920            0 :     if (p_full_name.size() > len && p_full_name.substr(0, len) == s) {
     921              :       //      p_repository = UserRepository;
     922            0 :       p_repository_name = p_full_name.substr(len + 1);
     923              : 
     924            0 :       TLOG_DEBUG(3) << "file \'" << p_full_name << "\' comes from user repository [\'" << p_repository_name << "\']";
     925            0 :       return;
     926              : 
     927              :       //      std::string repository_file = OksKernel::get_repository_root() + '/' + p_repository_name;
     928              :       //
     929              :       //      struct stat buf;
     930              :       //      if(stat(repository_file.c_str(), &buf) == 0) {
     931              :       //        TLOG_DEBUG( 3 ) << "file \'" << p_full_name << "\' comes from user repository [\'" << p_repository_name
     932              :       //        << "\']" ; return;
     933              :       //      }
     934              :       //      else {
     935              :       //        TLOG_DEBUG( 3 ) << "file \'" << p_full_name << "\' is found in user repository [\'" << p_repository_name
     936              :       //        << "\'] but does not exist in global one [\'" << OksKernel::get_repository_root() << "\']" ;
     937              :       //      }
     938              :     }
     939              :   }
     940              : 
     941          320 :   TLOG_DEBUG(3) << "file \'" << p_full_name << "\' is not in a repository";
     942              :   //  p_repository = NoneRepository;
     943          320 :   p_repository_name.clear();
     944              : }
     945              : 
     946              : void
     947           21 : OksFile::create_lock_name()
     948              : {
     949           21 :   p_lock_file_name = p_full_name;
     950              : 
     951           21 :   std::string::size_type idx = p_lock_file_name.find_last_of('/');
     952           21 :   if (idx == std::string::npos)
     953            0 :     p_lock_file_name = "./";
     954              :   else
     955           21 :     p_lock_file_name.erase(idx + 1);
     956              : 
     957           21 :   const char _prefix_str[] = ".oks-lock-";
     958           21 :   p_lock_file_name.append(_prefix_str, sizeof(_prefix_str) - 1);
     959              : 
     960           21 :   idx = p_full_name.find_last_of('/');
     961           21 :   std::string::size_type len(p_full_name.size());
     962           21 :   if (idx == std::string::npos) {
     963              :     idx = 0;
     964              :   } else {
     965           21 :     idx++;
     966           21 :     len -= idx;
     967              :   }
     968              : 
     969           21 :   p_lock_file_name.append(p_full_name, idx, len);
     970           21 :   p_lock_file_name.append(".txt");
     971           21 : }
     972              : 
     973              : // return true if lock exists and false if does not exist
     974              : // the string is non-empty, if the lock file was read
     975              : 
     976              : bool
     977           21 : OksFile::get_lock_string(std::string& lock_file_contents) const
     978              : {
     979           21 :   init_lock_name();
     980              : 
     981           21 :   lock_file_contents.clear();
     982              : 
     983           21 :   const char* lock_name = p_lock_file_name.c_str();
     984           21 :   struct stat buf;
     985              : 
     986           21 :   if (stat(lock_name, &buf) == 0) {
     987            0 :     std::ifstream f(lock_name);
     988              : 
     989            0 :     if (f.good()) {
     990            0 :       std::getline(f, lock_file_contents);
     991              :       return true;
     992              :     } else {
     993            0 :       lock_file_contents = "unknown [cannot read lock file \'";
     994            0 :       lock_file_contents += p_lock_file_name;
     995            0 :       lock_file_contents += "\']";
     996              :       return true;
     997              :     }
     998            0 :   }
     999              : 
    1000              :   return false;
    1001              : }
    1002              : 
    1003              : void
    1004           46 : OksFile::lock()
    1005              : {
    1006           46 :   if (p_nolock_mode) {
    1007              :     // Nasty hack to allow code generation on the fly without locking
    1008              :     // file. Just return without locking!
    1009              :     return;
    1010              :   }
    1011              : 
    1012           46 :   init_lock_name();
    1013              : 
    1014           46 :   if (p_lock != nullptr) {
    1015              :     return;
    1016              :   }
    1017              : 
    1018              :   // check that the file is not read-only
    1019              : 
    1020           21 :   {
    1021           21 :     struct stat buf;
    1022              : 
    1023           21 :     if (stat(p_full_name.c_str(), &buf) == 0) {
    1024           21 :       if (OksKernel::check_read_only(this) == true) {
    1025            0 :         throw oks::FileLockError(*this, true, "file is read-only");
    1026              :       }
    1027              :     }
    1028              :   }
    1029              : 
    1030              :   // check lock and report problem if it exists and cannot be reset
    1031              : 
    1032           21 :   {
    1033           21 :     std::string lock_file_contents;
    1034              : 
    1035           21 :     if (get_lock_string(lock_file_contents)) {
    1036            0 :       try {
    1037            0 :         boost::interprocess::file_lock lock(p_lock_file_name.c_str());
    1038              : 
    1039            0 :         if (lock.try_lock() == false) {
    1040            0 :           std::ostringstream text;
    1041            0 :           text << "file is already locked by \"" << lock_file_contents << '\"';
    1042            0 :           throw oks::FileLockError(*this, true, text.str());
    1043            0 :         } else {
    1044            0 :           try {
    1045            0 :             lock.unlock();
    1046            0 :           } catch (const boost::interprocess::interprocess_exception& ex) {
    1047            0 :             std::ostringstream text;
    1048            0 :             text << "boost::interprocess::unlock() failed: \"" << ex.what() << '\"';
    1049            0 :             throw oks::FileLockError(*this, false, text.str());
    1050            0 :           }
    1051              : 
    1052            0 :           if (!p_kernel->get_silence_mode()) {
    1053            0 :             Oks::warning_msg("OksFile::lock()") << "Remove obsolete lock of file \'" << p_full_name
    1054            0 :                                                 << "\' created by \n\"" << lock_file_contents << "\"\n";
    1055              :           }
    1056              : 
    1057            0 :           if (unlink(p_lock_file_name.c_str()) != 0) {
    1058            0 :             std::ostringstream text;
    1059            0 :             text << "failed to remove lock file \'" << p_lock_file_name << "\':\n" << oks::strerror(errno);
    1060            0 :             throw oks::FileLockError(*this, false, text.str());
    1061            0 :           }
    1062              :         }
    1063            0 :       } catch (const boost::interprocess::interprocess_exception& ex) {
    1064            0 :         std::ostringstream text;
    1065            0 :         text << "boost::interprocess::try_lock() failed: \"" << ex.what() << '\"';
    1066            0 :         throw oks::FileLockError(*this, false, text.str());
    1067            0 :       }
    1068              :     }
    1069           21 :   }
    1070              : 
    1071              :   // write lock file
    1072              : 
    1073           21 :   {
    1074           21 :     std::ofstream f(p_lock_file_name.c_str());
    1075              : 
    1076           21 :     if (!f.good()) {
    1077            0 :       std::ostringstream text;
    1078            0 :       text << "failed to create lock file \'" << p_lock_file_name << '\'';
    1079            0 :       throw oks::FileLockError(*this, true, text.str());
    1080            0 :     }
    1081              : 
    1082           21 :     try {
    1083           21 :       boost::posix_time::ptime now(boost::posix_time::second_clock::universal_time());
    1084           42 :       f << "process " << getpid() << " on " << OksKernel::get_host_name() << " started by "
    1085           42 :         << OksKernel::get_user_name() << " at " << boost::posix_time::to_simple_string(now) << " (UTC)" << std::endl;
    1086           21 :       f.flush();
    1087           21 :       f.close();
    1088            0 :     } catch (std::exception& ex) {
    1089            0 :       std::ostringstream text;
    1090            0 :       text << "failed to write lock file \'" << p_lock_file_name << "\': " << ex.what();
    1091            0 :       throw oks::FileLockError(*this, true, text.str());
    1092            0 :     }
    1093           21 :   }
    1094              : 
    1095           21 :   try {
    1096           21 :     p_lock.reset(new boost::interprocess::file_lock(p_lock_file_name.c_str()));
    1097              : 
    1098           21 :     if (p_lock->try_lock() == false) {
    1099            0 :       throw std::runtime_error("file is locked by another process");
    1100              :     }
    1101            0 :   } catch (const std::exception& ex) {
    1102            0 :     std::ostringstream text;
    1103            0 :     text << "boost::interprocess::try_lock() failed: \"" << ex.what() << '\"';
    1104            0 :     p_lock.reset();
    1105            0 :     throw oks::FileLockError(*this, false, text.str());
    1106            0 :   }
    1107              : }
    1108              : 
    1109              : void
    1110          320 : OksFile::unlock()
    1111              : {
    1112          320 :   if (p_lock == nullptr)
    1113              :     return;
    1114              : 
    1115           21 :   try {
    1116           21 :     p_lock->unlock();
    1117            0 :   } catch (const boost::interprocess::interprocess_exception& ex) {
    1118            0 :     std::ostringstream text;
    1119            0 :     text << "boost::interprocess::unlock() failed: \"" << ex.what() << '\"';
    1120            0 :     p_lock.reset();
    1121            0 :     throw oks::FileLockError(*this, false, text.str());
    1122            0 :   }
    1123              : 
    1124           21 :   p_lock.reset();
    1125              : 
    1126           21 :   if (unlink(p_lock_file_name.c_str()) != 0 && errno != ENOENT) {
    1127            0 :     std::ostringstream text;
    1128            0 :     text << "failed to remove lock file \'" << p_lock_file_name << "\':\n" << oks::strerror(errno);
    1129            0 :     throw oks::FileLockError(*this, false, text.str());
    1130            0 :   }
    1131              : }
    1132              : 
    1133              : std::list<std::string>::iterator
    1134           10 : OksFile::find_include_file(const std::string& s)
    1135              : {
    1136           10 :   std::list<std::string>::iterator i = p_list_of_include_files.begin();
    1137              : 
    1138           15 :   for (; i != p_list_of_include_files.end(); ++i) {
    1139            5 :     if (*i == s)
    1140              :       break;
    1141              :   }
    1142              : 
    1143           10 :   return i;
    1144              : }
    1145              : 
    1146              : OksFile*
    1147          142 : OksFile::check_parent(const OksFile* parent_h)
    1148              : {
    1149          142 :   if (p_included_by == 0 && parent_h != 0) {
    1150            0 :     p_included_by = parent_h;
    1151              :   }
    1152              : 
    1153          142 :   return this;
    1154              : }
    1155              : 
    1156              : void
    1157           10 : OksFile::add_include_file(const std::string& s)
    1158              : {
    1159              :   // check if the file was already included
    1160           10 :   if (find_include_file(s) != p_list_of_include_files.end()) {
    1161              :     return;
    1162              :   }
    1163              : 
    1164              :   // lock file
    1165           10 :   try {
    1166           10 :     lock();
    1167            0 :   } catch (oks::exception& ex) {
    1168            0 :     throw oks::FailedAddInclude(*this, s, ex);
    1169            0 :   }
    1170              : 
    1171              :   // try to find path to include and load it
    1172           10 :   try {
    1173           10 :     std::string path = p_kernel->get_file_path(s, this);
    1174           10 :     p_kernel->k_load_file(path, true, this, 0);
    1175           10 :   } catch (std::exception& ex) {
    1176            0 :     throw oks::FailedAddInclude(*this, s, ex.what());
    1177            0 :   }
    1178              : 
    1179              :   // add include and mark file as updated
    1180           10 :   p_list_of_include_files.push_back(s);
    1181           10 :   p_is_updated = true;
    1182              : }
    1183              : 
    1184              : void
    1185            0 : OksFile::remove_include_file(const std::string& s)
    1186              : {
    1187              :   // search included file
    1188            0 :   std::list<std::string>::iterator i = find_include_file(s);
    1189              : 
    1190              :   // check if the file was included
    1191            0 :   if (i == p_list_of_include_files.end()) {
    1192            0 :     throw oks::FailedRemoveInclude(*this, s, "there is no such include file");
    1193              :   }
    1194              : 
    1195              :   // lock file
    1196            0 :   try {
    1197            0 :     lock();
    1198            0 :   } catch (oks::exception& ex) {
    1199            0 :     throw oks::FailedRemoveInclude(*this, s, ex);
    1200            0 :   }
    1201              : 
    1202              :   // remove include and mark file as updated
    1203            0 :   p_list_of_include_files.erase(i);
    1204            0 :   p_is_updated = true;
    1205              : 
    1206              :   // close file, if it is not referenced by others
    1207            0 :   p_kernel->k_close_dangling_includes();
    1208            0 : }
    1209              : 
    1210              : void
    1211            0 : OksFile::rename_include_file(const std::string& old_s, const std::string& new_s)
    1212              : {
    1213            0 :   if (old_s == new_s)
    1214            0 :     return;
    1215              : 
    1216            0 :   std::list<std::string>::iterator i1 = find_include_file(old_s);
    1217            0 :   std::list<std::string>::iterator i2 = find_include_file(new_s);
    1218              : 
    1219            0 :   if (i1 == p_list_of_include_files.end()) {
    1220            0 :     throw oks::FailedRenameInclude(*this, old_s, new_s, "there is no such include file");
    1221              :   }
    1222              : 
    1223            0 :   if (i2 != p_list_of_include_files.end()) {
    1224            0 :     throw oks::FailedRenameInclude(*this, old_s, new_s, "file with new name is already included");
    1225              :   }
    1226              : 
    1227            0 :   try {
    1228            0 :     lock();
    1229            0 :   } catch (oks::exception& ex) {
    1230            0 :     throw oks::FailedRenameInclude(*this, old_s, new_s, ex);
    1231            0 :   }
    1232              : 
    1233            0 :   (*i1) = new_s;
    1234            0 :   p_is_updated = true;
    1235              : }
    1236              : 
    1237              : void
    1238            0 : OksFile::set_logical_name(const std::string& s)
    1239              : {
    1240            0 :   if (s == p_logical_name)
    1241              :     return;
    1242              : 
    1243            0 :   try {
    1244            0 :     lock();
    1245            0 :   } catch (oks::exception& ex) {
    1246            0 :     throw oks::FileChangeError(*this, "set logical name", ex);
    1247            0 :   }
    1248              : 
    1249            0 :   p_logical_name = s;
    1250            0 :   p_is_updated = true;
    1251              : }
    1252              : 
    1253              : void
    1254            0 : OksFile::set_type(const std::string& s)
    1255              : {
    1256            0 :   if (s == p_type)
    1257              :     return;
    1258              : 
    1259            0 :   try {
    1260            0 :     lock();
    1261            0 :   } catch (oks::exception& ex) {
    1262            0 :     throw oks::FileChangeError(*this, "set type", ex);
    1263            0 :   }
    1264              : 
    1265            0 :   p_type = s;
    1266            0 :   p_is_updated = true;
    1267              : }
    1268              : 
    1269              : std::ostream&
    1270            0 : operator<<(std::ostream& s, const oks::Comment& c)
    1271              : {
    1272            0 :   s << "user \"" << c.get_created_by() << "\" (" << c.get_author() << ")"
    1273            0 :     << " on \"" << c.get_created_on() << "\" wrote: \"" << c.get_text() << "\"";
    1274              : 
    1275            0 :   return s;
    1276              : }
    1277              : 
    1278              : std::ostream&
    1279            0 : operator<<(std::ostream& s, const OksFile& f)
    1280              : {
    1281            0 :   s << "OKS file\n"
    1282              :        " short file name:        \'"
    1283            0 :     << f.get_short_file_name()
    1284              :     << "\'\n"
    1285              :        " full file name:         \'"
    1286            0 :     << f.get_full_file_name()
    1287              :     << "\'\n"
    1288              :        " lock file name:         \'"
    1289            0 :     << f.get_lock_file()
    1290              :     << "\'\n"
    1291              :        " logical name:           \'"
    1292            0 :     << f.get_logical_name()
    1293              :     << "\'\n"
    1294              :        " type:                   \'"
    1295            0 :     << f.get_type()
    1296              :     << "\'\n"
    1297              :        " oks format:             \'"
    1298            0 :     << f.get_oks_format()
    1299              :     << "\'\n"
    1300            0 :        " number of items:        "
    1301            0 :     << f.get_number_of_items()
    1302              :     << "\n"
    1303              :        " created by:             "
    1304            0 :     << f.get_created_by()
    1305              :     << "\n"
    1306              :        " creation time:          "
    1307            0 :     << boost::posix_time::to_simple_string(f.get_creation_time())
    1308              :     << "\n"
    1309              :        " created on:             "
    1310            0 :     << f.get_created_on()
    1311              :     << "\n"
    1312              :        " last modified by:       \'"
    1313            0 :     << f.get_last_modified_by()
    1314              :     << "\'\n"
    1315              :        " last modification time: "
    1316            0 :     << boost::posix_time::to_simple_string(f.get_last_modification_time())
    1317              :     << "\'\n"
    1318              :        " last modified on:       "
    1319            0 :     << f.get_last_modified_on()
    1320              :     << "\'\n"
    1321              :        " is "
    1322            0 :     << (f.is_read_only() ? "read-only" : "read-write")
    1323              :     << "\n"
    1324              :        " is "
    1325            0 :     << (f.is_locked() ? "" : "not ")
    1326              :     << "locked\n"
    1327              :        " is "
    1328            0 :     << (f.is_updated() ? "" : "not ") << "updated\n";
    1329              : 
    1330            0 :   {
    1331            0 :     size_t num_of_includes = f.get_include_files().size();
    1332              : 
    1333            0 :     if (num_of_includes) {
    1334            0 :       s << " contains " << num_of_includes << " include file(s)";
    1335            0 :       for (const auto& i : f.get_include_files())
    1336            0 :         s << "  - \'" << i << "\'\n";
    1337              :     } else {
    1338            0 :       s << " contains no include files\n";
    1339              :     }
    1340              :   }
    1341              : 
    1342            0 :   {
    1343            0 :     size_t num_of_comments = f.get_comments().size();
    1344              : 
    1345            0 :     if (num_of_comments) {
    1346            0 :       s << " contains " << num_of_comments << " comment(s)";
    1347            0 :       for (const auto& i : f.get_comments())
    1348            0 :         s << "  - " << boost::posix_time::to_simple_string(boost::posix_time::from_iso_string(i.first)) << ": \'"
    1349            0 :           << *i.second << "\'\n";
    1350              :     } else {
    1351            0 :       s << " contains no comments\n";
    1352              :     }
    1353              :   }
    1354              : 
    1355            0 :   s << "END of oks file\n";
    1356              : 
    1357            0 :   return s;
    1358              : }
    1359              : 
    1360              : std::string
    1361          315 : OksFile::make_repository_name() const
    1362              : {
    1363          315 :   std::string name;
    1364              : 
    1365          315 :   if (!OksKernel::get_repository_root().empty()) {
    1366            0 :     name = OksKernel::get_repository_root() + '/' + get_repository_name();
    1367              :   }
    1368              : 
    1369          315 :   return name;
    1370            0 : }
    1371              : 
    1372              : void
    1373          315 : OksFile::update_status_of_file(bool update_local, bool update_repository)
    1374              : {
    1375          315 :   struct stat buf;
    1376              : 
    1377          315 :   if (update_local == true && p_is_on_disk == true) {
    1378              : 
    1379          315 :     if (stat(p_full_name.c_str(), &buf) == 0) {
    1380          315 :       p_last_modified = buf.st_mtime;
    1381              :     }
    1382              :   }
    1383              : 
    1384          315 :   if (update_repository == true /*&& get_repository() == UserRepository*/) {
    1385          315 :     std::string full_repository_name = make_repository_name();
    1386              : 
    1387          315 :     if (!full_repository_name.empty() && stat(full_repository_name.c_str(), &buf) == 0) {
    1388            0 :       p_repository_last_modified = buf.st_mtime;
    1389              :     }
    1390          315 :   }
    1391          315 : }
    1392              : 
    1393              : OksFile::FileStatus
    1394            6 : OksFile::get_status_of_file() const
    1395              : {
    1396            6 :   if (p_is_on_disk == false) {
    1397              :     return FileWasNotSaved;
    1398              :   } else {
    1399            6 :     struct stat buf;
    1400              : 
    1401            6 :     if (stat(p_full_name.c_str(), &buf) != 0) {
    1402            0 :       return FileRemoved;
    1403              :     }
    1404              : 
    1405            6 :     if (buf.st_mtime != p_last_modified) {
    1406              :       return FileModified;
    1407              :     }
    1408              : 
    1409              :     //    if(get_repository() == UserRepository) {
    1410              :     //      std::string full_repository_name = make_repository_name();
    1411              :     //
    1412              :     //      if(!full_repository_name.empty()) {
    1413              :     //        if(stat(full_repository_name.c_str(), &buf) != 0) {
    1414              :     //          return FileRepositoryRemoved;
    1415              :     //        }
    1416              :     //
    1417              :     //        if(buf.st_mtime != p_repository_last_modified) {
    1418              :     //          return FileRepositoryModified;
    1419              :     //        }
    1420              :     //      }
    1421              :     //    }
    1422              :   }
    1423              : 
    1424            6 :   return FileNotModified;
    1425              : }
    1426              : 
    1427              : void
    1428            0 : OksFile::rename(const std::string& short_name, const std::string& full_name)
    1429              : {
    1430            0 :   p_short_name = short_name;
    1431            0 :   p_full_name = full_name;
    1432            0 :   create_lock_name();
    1433            0 :   check_repository();
    1434            0 : }
    1435              : 
    1436              : void
    1437            0 : OksFile::rename(const std::string& full_name)
    1438              : {
    1439            0 :   p_full_name = full_name;
    1440            0 :   create_lock_name();
    1441            0 :   update_status_of_file();
    1442            0 : }
    1443              : 
    1444              : void
    1445            0 : OksFile::get_all_include_files(const OksKernel* kernel, std::set<OksFile*>& out)
    1446              : {
    1447            0 :   const OksFile::Map& schema_files = kernel->schema_files();
    1448            0 :   const OksFile::Map& data_files = kernel->data_files();
    1449              : 
    1450            0 :   for (std::list<std::string>::iterator i = p_list_of_include_files.begin(); i != p_list_of_include_files.end(); ++i) {
    1451            0 :     std::string s = kernel->get_file_path(*i, this);
    1452            0 :     if (s.size()) {
    1453            0 :       OksFile::Map::const_iterator j = data_files.find(&s);
    1454            0 :       if (j != data_files.end()) {
    1455            0 :         if (out.find(j->second) != out.end()) {
    1456            0 :           continue;
    1457              :         }
    1458            0 :         out.insert(j->second);
    1459              :       } else {
    1460            0 :         j = schema_files.find(&s);
    1461            0 :         if (j != schema_files.end()) {
    1462            0 :           if (out.find(j->second) != out.end()) {
    1463            0 :             continue;
    1464              :           }
    1465            0 :           out.insert(j->second);
    1466              :         } else {
    1467            0 :           continue;
    1468              :         }
    1469              :       }
    1470              : 
    1471            0 :       j->second->get_all_include_files(kernel, out);
    1472              :     }
    1473            0 :   }
    1474            0 : }
    1475              : 
    1476              : // return true if the string is not info line:
    1477              : // <info ... last-modification-time="nnnnnnnnTnnnnnn"/>
    1478              : 
    1479              : static bool
    1480            0 : is_not_info(const std::string& line)
    1481              : {
    1482            0 :   static const char start_tag[] = "<info ";
    1483            0 :   static const char last_tag[] =
    1484              :     "last-modification-time=\"yyyymmddThhmmss\"/>"; // 24 - is a position of first double quote
    1485              : 
    1486            0 :   const std::string::size_type len = line.length();
    1487              : 
    1488            0 :   return ((len < (sizeof(start_tag) + sizeof(last_tag))) ||
    1489            0 :           (line.compare(0, sizeof(start_tag) - 1, start_tag, sizeof(start_tag) - 1) != 0) ||
    1490            0 :           (line.compare(len - sizeof(last_tag) + 1, 24, last_tag, 24) != 0));
    1491              : }
    1492              : 
    1493              : bool
    1494            0 : OksFile::compare(const char* file1_name, const char* file2_name)
    1495              : {
    1496            0 :   std::ifstream f1(file1_name);
    1497            0 :   std::ifstream f2(file2_name);
    1498              : 
    1499            0 :   if (!f1) {
    1500            0 :     throw oks::FileCompareError(file1_name, file2_name, "cannot open file");
    1501              :   }
    1502              : 
    1503            0 :   if (!f2) {
    1504            0 :     throw oks::FileCompareError(file2_name, file1_name, "cannot open file");
    1505              :   }
    1506              : 
    1507            0 :   std::string line1;
    1508            0 :   std::string line2;
    1509              : 
    1510            0 :   while (true) {
    1511            0 :     if (f1.eof() && f2.eof()) {
    1512              :       return true;
    1513              :     }
    1514              : 
    1515            0 :     std::getline(f1, line1);
    1516            0 :     std::getline(f2, line2);
    1517              : 
    1518            0 :     if ((line1 != line2) && (is_not_info(line1) || is_not_info(line2))) {
    1519              :       return false;
    1520              :     }
    1521              :   }
    1522              : 
    1523              :   return true;
    1524            0 : }
    1525              : 
    1526              : } // namespace oks
    1527              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1