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
|