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