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: 2025-12-21 13:07:08 Functions: 37.5 % 48 18

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

Generated by: LCOV version 2.0-1