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

Generated by: LCOV version 2.0-1