libxcks  0.1.0.1
xcksfilewriter.cpp
Go to the documentation of this file.
1 /*
2  * libxcks
3  * Copyright (C) 2022 Julien Couot
4  *
5  * This program is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13  * License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
24 //---------------------------------------------------------------------------
25 #include <boost/locale.hpp>
26 #include <algorithm>
27 //#include <filesystem> // Included in "pathutil.hpp"
28 //#include <string> // Included in "libxcks/version.hpp"
29 #include <iomanip>
30 #include <sstream>
31 #include <ctime>
32 #include <cassert>
33 
34 #include "xcksfilewriter.hpp"
35 // #include "libxcks/types.hpp" // Included in "libxcks/ckvalue.hpp"
36 #include "libxcks/defs.hpp"
37 #include "libxcks/version.hpp"
38 #include "libxcks/ckfactory.hpp"
39 #include "libxcks/xcksver.hpp"
40 // #include "libxcks/ckvalue.hpp" // Included in "libxcks/ckfactory.hpp"
41 #include "pathutil.hpp"
42 #include "strutil.hpp"
43 //---------------------------------------------------------------------------
44 
46 using namespace std;
47 
48 // The Boost.Locale namespace.
49 using namespace boost::locale;
50 //---------------------------------------------------------------------------
51 
52 
53 namespace libxcks
54 {
55 //###########################################################################
56 // XCKSDirTree definition.
57 //###########################################################################
58 
64 class XCKSDirStruct final
65 {
66  protected:
67  class Element; // Element of the directories structure.
68  class File; // File of the directories structure.
69  class Directory; // Directory of the directories structure.
70 
72 
73  public:
74  class const_file_iterator; // Iterator on files.
75 
77  XCKSDirStruct() : root(nullptr) {}
78 
80  XCKSDirStruct(const XCKSDirStruct&) = delete;
81 
84 
85  // Returns an iterator pointing at the first file of the directory structure.
86  const_file_iterator files_begin() const;
87 
88  // Returns an iterator pointing at the one-after-the-last file of directory structure.
89  inline const_file_iterator files_end() const;
90 
91  // Adds a new file in the directory structure.
92  bool addFile(const filesystem::path& filename, void* data = nullptr);
93 };
94 //---------------------------------------------------------------------------
95 
96 
103 {
104  private:
106 
107  protected:
108  string name;
112 
113  public:
115  Element() = delete;
116 
118  Element(const Element&) = delete;
119 
121  Element& operator=(const Element&) = delete;
122 
123  // Constructor.
124  Element(const string& elementName, Element* parentElement, Element* previousElement);
125 
126  // Destructor.
127  virtual ~Element();
128 
134  virtual bool hasChild() const { return false; }
135 
141  virtual bool hasPrevious() const final { return previous != nullptr; }
142 
148  virtual bool hasNext() const final { return next != nullptr; }
149 
155  virtual bool hasParent() const final { return parent != nullptr; }
156 
162  virtual bool isAFile() const = 0;
163 
164  // Does an element exist with the given name in the current level.
165  virtual bool hasElement(const string& elementName) const final;
166 
172  virtual string getName() const final { return name; }
173 
180  virtual Element* getChild() const { return nullptr; }
181 
192  virtual void setChild(Element* newChild) { assert(false); /*"setChild() called on bad element type.";*/ }
193 
200  virtual Element* getPrevious() const final { return previous; }
201 
207  virtual void setPrevious(Element* newPrevious) final { previous = newPrevious; }
208 
215  virtual Element* getNext() const final { return next; }
216 
222  virtual void setNext(Element* newNext) final { next = newNext; }
223 
230  virtual Element* getParent() const final { return parent; }
231 
237  virtual void getParent(Element* newParent) final { parent = newParent; }
238 
244  virtual void* getData() const { return nullptr; }
245 
259  virtual void setData(void* newData) { assert(false); /* setData() called on bad element type. */ }
260 
261  // Gets the element with the given name in the current level.
262  virtual Element* getElement(const string& elementName, Element** lastElement = nullptr) const final;
263 
264  // Adds a new file in the directory structure.
265  bool addFile(const ArrayString& dirs, const size_t cur, const string& filename, void* data = nullptr);
266 
267  protected:
268  // Creates the directory structure for the file.
269  Element* createDirsAndAddFile(const ArrayString& dirs, const size_t cur, const string& filename, void* data = nullptr);
270 
271  // Gets the next file in the directory structure.
272  XCKSDirStruct::File* getNextFileRec() const;
273 
274  // Gets the next file in the directory structure.
275  XCKSDirStruct::File* getNextFile(const bool calledFromChild) const;
276 
277  public:
278  // Gets the next file in the directory structure.
279  XCKSDirStruct::File* getNextFile() const;
280 };
281 //---------------------------------------------------------------------------
282 
283 
287 class XCKSDirStruct::File final : public XCKSDirStruct::Element
288 {
289  protected:
290  void* data;
291 
292  public:
294  File() = delete;
295 
297  File(const File&) = delete;
298 
300  File& operator=(const File&) = delete;
301 
302  // Constructor.
303  File(const string& elementName, XCKSDirStruct::Element* parentElement,
304  XCKSDirStruct::Element* previousElement, void* elementData = nullptr);
305 
307  virtual ~File() {}
308 
314  bool isAFile() const override final { return true; };
315 
321  void* getData() const override { return data; }
322 
328  void setData(void* newData) override final { data = newData; }
329 };
330 //---------------------------------------------------------------------------
331 
332 
337 {
338  protected:
340 
341  public:
343  Directory() = delete;
344 
346  Directory(const Directory&) = delete;
347 
349  Directory& operator=(const Directory&) = delete;
350 
351  // Constructor.
352  Directory(const string& directoryName, XCKSDirStruct::Element* parentElement, XCKSDirStruct::Element* previousElement);
353 
355  virtual ~Directory() {}
356 
362  bool hasChild() const override final { return child != nullptr; }
363 
369  bool isAFile() const override final { return false; }
370 
377  Element* getChild() const override final { return child; }
378 
384  void setChild(Element* newChild) override final { child = newChild; }
385 };
386 //---------------------------------------------------------------------------
387 
388 
395 {
396  protected:
398 
399  public:
401  const_file_iterator() : file(nullptr) {}
402 
405 
408 
414  const_file_iterator(XCKSDirStruct::File* initFile) : file(initFile) {}
415 
416  protected:
418  inline void plusPlus()
419  {
420  if (file != nullptr)
421  file = file->getNextFile();
422  }
423 
424  public:
431  bool operator==(const const_file_iterator& it) const { return this->file == it.file; }
432 
439  bool operator!=(const const_file_iterator& it) const { return this->file != it.file; }
440 
446  const_file_iterator& operator++() { plusPlus(); return *this; }
447 
453  const_file_iterator operator++(int) { const_file_iterator it = *this; plusPlus(); return it; }
454 
460  const XCKSDirStruct::File& operator*() const { return *file; }
461 
467  const XCKSDirStruct::File* operator->() const { return file; }
468 };
469 //---------------------------------------------------------------------------
470 
471 
472 //###########################################################################
473 // XCKSDirTree implementation.
474 //###########################################################################
475 
481 XCKSDirStruct::const_file_iterator XCKSDirStruct::files_begin() const
482 {
483  if (root == nullptr)
484  return const_file_iterator();
485  else
486  {
488  if (root->isAFile())
489  f = dynamic_cast<XCKSDirStruct::File*>(root);
490  else
491  f = root->getNextFile();
492  assert(f != nullptr); // "The file element shouldn't be null."
493 
494  return const_file_iterator(f);
495  }
496 }
497 //---------------------------------------------------------------------------
498 
499 
507 inline XCKSDirStruct::const_file_iterator XCKSDirStruct::files_end() const
508 {
509  return const_file_iterator();
510 }
511 //---------------------------------------------------------------------------
512 
513 
522 bool XCKSDirStruct::addFile(const filesystem::path& filename, void* data)
523 {
524  ArrayString dirs = getDirectoriesAsArrayString(filename);
525 
526  if (root == nullptr)
527  {
528  if (dirs.size() > 0) // add directory
529  {
530  root = new XCKSDirStruct::Directory(dirs[0], nullptr, nullptr);
531  return root->addFile(dirs, 0, from_u8string(filename.filename().u8string()), data);
532  }
533  else
534  {
535  root = new XCKSDirStruct::File(from_u8string(filename.filename().u8string()), nullptr, nullptr, data);
536  return true;
537  }
538  }
539  else
540  return root->addFile(dirs, 0, from_u8string(filename.filename().u8string()), data);
541 }
542 //---------------------------------------------------------------------------
543 
544 
545 //###########################################################################
546 // XCKSDirTree::Element implementation.
547 //###########################################################################
548 
557 XCKSDirStruct::Element::Element(const string& elementName,
558  XCKSDirStruct::Element* parentElement,
559  XCKSDirStruct::Element* previousElement) :
560  name(elementName),
561  parent(parentElement),
562  previous(previousElement),
563  next(nullptr)
564 {
565 }
566 //---------------------------------------------------------------------------
567 
568 
573 {
574  if (hasChild())
575  delete getChild();
576  if (hasNext())
577  delete getNext();
578 }
579 //---------------------------------------------------------------------------
580 
581 
589 bool XCKSDirStruct::Element::hasElement(const string& elementName) const
590 {
591  Element* e = const_cast<Element*>(this);
592  bool found = false;
593 
594  while (!found && e != nullptr)
595  {
596  if (compareFileName(e->getName(), elementName) == 0)
597  found = true;
598  else
599  e = e->getNext();
600  }
601 
602  return true;
603 }
604 //---------------------------------------------------------------------------
605 
606 
618 {
619  Element* e = const_cast<Element*>(this);
620  Element* r = nullptr;
621 
622  while ((r == nullptr) && (e != nullptr))
623  {
624  if (compareFileName(e->getName(), elementName) == 0)
625  r = e;
626  else
627  {
628  if (e->getNext() == nullptr)
629  {
630  if (lastElement != nullptr)
631  *lastElement = e;
632  e = nullptr;
633  }
634  else
635  e = e->getNext();
636  }
637  }
638 
639  return r;
640 }
641 //---------------------------------------------------------------------------
642 
643 
654 bool XCKSDirStruct::Element::addFile(const ArrayString& dirs, const size_t cur, const string& filename, void* data)
655 {
656  bool addDir = cur < dirs.size();
657  const string& fn = addDir ? dirs[cur] : filename;
658  bool res;
659 
660  Element* last; // last element of the current level
661  Element* n; // new element
662  Element* e = this->getElement(fn, &last);
663  if (addDir)
664  {
665  if (e == nullptr)
666  {
667  n = new XCKSDirStruct::Directory(fn, this->getParent(), last);
668  assert(last->getNext() == nullptr); // "Last element have a next element."
669  last->setNext(n);
670  n->setChild(n->createDirsAndAddFile(dirs, cur + 1, filename, data));
671  return true;
672  }
673  else
674  {
675  if (e->isAFile())
676  res = false;
677  else
678  {
679  if (e->hasChild())
680  res = e->getChild()->addFile(dirs, cur + 1, filename, data);
681  else
682  {
683  e->setChild(e->createDirsAndAddFile(dirs, cur + 1, filename, data));
684  return true;
685  }
686  }
687  }
688  }
689  else
690  {
691  if (e == nullptr)
692  {
693  n = new XCKSDirStruct::File(fn, this->getParent(), last, data);
694  assert(last->getNext() == nullptr); // "Last element have a next element."
695  last->setNext(n);
696  return true;
697  }
698  else // already existing
699  {
700  res = false;
701  }
702  }
703 
704  return res;
705 }
706 //---------------------------------------------------------------------------
707 
708 
719  const size_t cur,
720  const string& filename,
721  void* data)
722 {
723  bool addDir = cur < dirs.size();
724  const string& fn = addDir ? dirs[cur] : filename;
725 
726  Element* n; // new element
727  if (addDir)
728  {
729  n = new XCKSDirStruct::Directory(fn, this, nullptr);
730  n->setChild(n->createDirsAndAddFile(dirs, cur + 1, filename, data));
731  }
732  else
733  n = new XCKSDirStruct::File(fn, this, nullptr, data);
734 
735  return n;
736 }
737 //---------------------------------------------------------------------------
738 
739 
747 {
748  XCKSDirStruct::File* next;
749 
750  if (this->isAFile())
751  {
752  next = dynamic_cast<XCKSDirStruct::File*>(const_cast<XCKSDirStruct::Element*>(this));
753  assert(next != nullptr); // "The element should cast to XCKSDirStruct::File*."
754  }
755  else
756  {
757  if (this->hasChild())
758  {
759  next = this->getChild()->getNextFileRec();
760  assert(next != nullptr); // "A directory element should have at least a file in its children."
761  }
762  else if (this->hasNext())
763  {
764  next = this->getNext()->getNextFileRec();
765  assert(next != nullptr); // "An element with a next element should have at least a file in its successors."
766  }
767  else
768  {
769  next = nullptr;
770  assert(false); // "A directory element should have at least a file in its children."
771  }
772  }
773 
774  return next;
775 }
776 //---------------------------------------------------------------------------
777 
778 
788 {
789  XCKSDirStruct::File* next;
790 //const string name = this->getName();
791 
792  if (calledFromChild)
793  {
794  if (this->hasNext())
795  {
796  next = this->getNext()->getNextFileRec();
797  assert(next != nullptr); // "An element with a next element should have at least a file in its successors."
798  }
799  else
800  {
801  if (this->hasParent())
802  next = this->getParent()->getNextFile(true);
803  else
804  next = nullptr;
805  }
806  }
807  else // !calledFromChild
808  {
809  if (this->hasChild())
810  {
811  next = this->getChild()->getNextFileRec();
812  assert(next != nullptr); // "A directory element should have at least a file in its children."
813  }
814  else
815  {
816  if (this->hasNext())
817  {
818  next = this->getNext()->getNextFileRec();
819  assert(next != nullptr); // "An element with a next element should have at least a file in its successors."
820  }
821  else
822  {
823  if (this->hasParent())
824  next = this->getParent()->getNextFile(true);
825  else
826  next = nullptr;
827  }
828  }
829  }
830 
831  return next;
832 }
833 //---------------------------------------------------------------------------
834 
835 
843 {
844  return this->getNextFile(false);
845 }
846 //---------------------------------------------------------------------------
847 
848 
849 //###########################################################################
850 // XCKSDirTree::File implementation.
851 //###########################################################################
852 
862 XCKSDirStruct::File::File(const string& fileName,
863  XCKSDirStruct::Element* parentElement,
864  XCKSDirStruct::Element* previousElement,
865  void* elementData) :
866  XCKSDirStruct::Element(fileName,
867  parentElement,
868  previousElement),
869  data(elementData)
870 {
871 }
872 //---------------------------------------------------------------------------
873 
874 
875 //###########################################################################
876 // XCKSDirTree::Directory implementation.
877 //###########################################################################
878 
887 XCKSDirStruct::Directory::Directory(const string& fileName,
888  XCKSDirStruct::Element* parentElement,
889  XCKSDirStruct::Element* previousElement) :
890  XCKSDirStruct::Element(fileName,
891  parentElement,
892  previousElement),
893  child(nullptr)
894 {
895 }
896 //---------------------------------------------------------------------------
897 
898 
899 //###########################################################################
900 // XCKSDirTree::const_file_iterator implementation.
901 //###########################################################################
902 
903 
904 //---------------------------------------------------------------------------
905 
906 
907 
908 
909 
910 //###########################################################################
911 // XCKSFileBase implementation.
912 //###########################################################################
913 
915 const string XCKSFileWriter::parentDir = "..";
916 //---------------------------------------------------------------------------
917 
918 
935  XCKSWriterChecksumProvider& checksumProvider,
936  const XCKSWriterOptions& options,
937  const std::filesystem::path& baseDir,
938  const ArrayChecksumAlgoId& algoIds,
939  const ChecksumFormatter::ConfigurationProvider& confProvider,
940  Version version) :
941  writerHandler(xcksWriterHandler),
942  ckProvider(checksumProvider),
943  writerOptions(options),
944  baseDirectory(baseDir),
945  version(getValidVersion(version)),
946  possibleSumsAlgos(getAvailableChecksumTypesName()),
947  sumsAlgos(getValidChecksumTypesForNewEntries(algoIds)),
948  ckfConfProvider(confProvider)
949 {
950  assert(baseDir.is_absolute());
951  if (sumsAlgos.empty())
952  writerHandler.onFatalError(translate("No valid algorithm of checksum provided.").str(libxcks_domain));
953 }
954 //---------------------------------------------------------------------------
955 
956 
961 {
962 }
963 //---------------------------------------------------------------------------
964 
965 
977 {
979 }
980 //---------------------------------------------------------------------------
981 
982 
991 {
993  for (const string& algoName : possibleSumsAlgos)
994  {
995  ChecksumAlgoId id;
996  if (ChecksumFactory::getAlgorithmId(id, algoName, true))
997  ids.push_back(id);
998  }
999 
1000  assert(ids.size() == possibleSumsAlgos.size());
1001 
1002  return ids;
1003 }
1004 //---------------------------------------------------------------------------
1005 
1006 
1017 {
1018  return sumsAlgos;
1019 }
1020 //---------------------------------------------------------------------------
1021 
1022 
1029 {
1030  return version;
1031 }
1032 //---------------------------------------------------------------------------
1033 
1034 
1042 void XCKSFileWriter::getVersion(unsigned int& major, unsigned int& minor, unsigned int& revision) const
1043 {
1044  switch (getVersion())
1045  {
1046  case Version::V1_0_0 :
1047  major = 1u;
1048  minor = 0u;
1049  revision = 0u;
1050  break;
1051  }
1052 }
1053 //---------------------------------------------------------------------------
1054 
1055 
1062 {
1063  unsigned int major, minor, revision;
1064  getVersion(major, minor, revision);
1065  return to_string(major) + '.' + to_string(minor) + '.' + to_string(revision);
1066 }
1067 //---------------------------------------------------------------------------
1068 
1069 
1084 bool XCKSFileWriter::write(ostream& os, const ArrayPath& relativePaths)
1085 {
1086  if (sumsAlgos.empty())
1087  return false;
1088 
1089  bool ok;
1090  unsigned int major, minor, revision;
1091  getVersion(major, minor, revision);
1092  if (major == 1)
1093  ok = writeV1(os, relativePaths);
1094  else
1095  {
1096  assert(false); // "Unexpected XCKS version."
1097  writerHandler.onFatalError((format(translate("Unexpected XCKS version: {1}.").str(libxcks_domain)) % getStrVersion()).str());
1098  return false;
1099  }
1100 
1101  if (!ok)
1102  writerHandler.onFatalError(translate("Error while writing the XCKS file.").str(libxcks_domain));
1103 
1104  return ok;
1105 }
1106 //---------------------------------------------------------------------------
1107 
1108 
1116 {
1117  if (ver >= Version::First_Valid && ver <= Version::Last_Valid)
1118  return ver;
1119  else
1120  return Version::Last_Valid;
1121 }
1122 //---------------------------------------------------------------------------
1123 
1124 
1131 {
1133  ArrayChecksumAlgoId res = ids;
1134 
1135  // Removes all duplicates values.
1136  /*sort(res.begin(), res.end());
1137  ArrayChecksumAlgoId::iterator last = unique(res.begin(), res.end());
1138  res.erase(last, res.end());*/
1139  // Want to keep the order.
1140  size_t i = 0;
1141  while (i < res.size())
1142  {
1143  size_t j = i + 1;
1144  while (j < res.size())
1145  {
1146  if (res[j] == res[i])
1147  res.erase(res.begin() + j);
1148  else
1149  j++;
1150  }
1151  i++;
1152  }
1153 
1154  // Check if remaining algorithms of checksums are valid.
1155  i = 0;
1156  while (i < res.size())
1157  {
1158  ArrayChecksumAlgoId::const_iterator a = find(aIds.cbegin(), aIds.cend(), res[i]);
1159  if (a == aIds.cend())
1160  res.erase(res.begin() + i);
1161  else
1162  i++;
1163  }
1164 
1165  return res;
1166 }
1167 //---------------------------------------------------------------------------
1168 
1169 
1191  uintmax_t& filesize,
1192  time_t& filedatetime,
1193  const ArrayChecksumAlgoId& ids,
1194  const filesystem::path& filePath,
1195  bool& stopWriting) const
1196 {
1197  using Status = XCKSWriterChecksumProvider::Status;
1198  Status status = ckProvider.getChecksumValues(ckValues, filesize, filedatetime, sumsAlgos,
1199  (baseDirectory / filePath).lexically_normal());
1200  if (status == Status::Ok || status == Status::MissingAlgorithm)
1201  {
1202  // Checks if all asked values have been returned.
1203  ArrayChecksumAlgoId diff;
1204  for (const ChecksumAlgoId id : ids)
1205  {
1206  if (find_if(ckValues.cbegin(), ckValues.cend(), [&](const ChecksumValue& ckv) { return ckv.getType() == id; }) == ckValues.cend())
1207  diff.push_back(id);
1208  }
1209  if (!diff.empty())
1210  {
1211  status = Status::MissingAlgorithm;
1212  for (const ChecksumAlgoId id : diff)
1213  writerHandler.onFileGetChecksumsError(filePath, (baseDirectory / filePath).lexically_normal(),
1214  status, stopWriting, id);
1215  }
1216 
1217  // Removing all unasked algorithms.
1218  if (status == Status::Ok && ckValues.size() > ids.size())
1219  {
1220  size_t i = 0;
1221  while (i < ckValues.size())
1222  {
1223  if (find(ids.cbegin(), ids.cend(), ckValues[i].getType()) == ids.cend())
1224  ckValues.erase(ckValues.begin() + i);
1225  else
1226  i++;
1227  }
1228  }
1229 
1230  // Checks the returned values.
1231  if (status == Status::Ok && writerOptions.checkReturnedChecksums())
1232  {
1233  for (const ChecksumValue& ckv : ckValues)
1234  {
1235  if (ckv.getSize() != ChecksumFactory::getSize(ckv.getType()))
1236  {
1237  status = Status::BadChecksumValue;
1238 
1239  writerHandler.onFileGetChecksumError(filePath, (baseDirectory / filePath).lexically_normal(),
1240  status, ckv);
1241  }
1242  }
1243  }
1244 
1245  // Checks and removes duplicates values.
1246  if (status == Status::Ok && writerOptions.checkReturnedChecksums() && ckValues.size() > ids.size())
1247  {
1248  size_t i = 0;
1249  while (i < ckValues.size())
1250  {
1251  const ChecksumValue& ckv = ckValues[i];
1252  size_t j = i + 1;
1253  while (j < ckValues.size())
1254  {
1255  if (ckv.getType() == ckValues[j].getType())
1256  {
1257  if (ckv == ckValues[j])
1258  ckValues.erase(ckValues.begin() + j);
1259  else
1260  {
1261  status = Status::BadDuplicateValue;
1262  writerHandler.onFileGetChecksumError(filePath, (baseDirectory / filePath).lexically_normal(),
1263  status, ckv);
1264  j++;
1265  }
1266  }
1267  else
1268  j++;
1269  }
1270  i++;
1271  }
1272  }
1273  }
1274  else
1275  {
1276  writerHandler.onFileGetChecksumsError(filePath, (baseDirectory / filePath).lexically_normal(),
1277  status, stopWriting);
1278  }
1279 
1280  return status;
1281 }
1282 //---------------------------------------------------------------------------
1283 
1284 
1297 bool XCKSFileWriter::writeLocalFilesV1(ostream& os, const ArrayPath& relativePaths, int& indent)
1298 {
1299  using Status = XCKSWriterChecksumProvider::Status;
1300  XCKSDirStruct dirs;
1301  for (const filesystem::path& p : relativePaths)
1302  dirs.addFile(p, const_cast<filesystem::path*>(&p)); // doing this ugly cast because lifetimes of relativePaths and dirs are the compatible
1303 
1304  ArrayChecksumValue ckValues;
1305  uintmax_t filesize;
1306  time_t filedatetime;
1307  ArrayString curDir;
1308  bool ok = true;
1310  while (ok && it != dirs.files_end())
1311  {
1312  const filesystem::path& fnNewRel = *(reinterpret_cast<const filesystem::path* const>(it->getData())); // must be const because of the upper const_cast
1313 
1314  ckValues.clear();
1315  filesize = Invalid_File_Size;
1316  filedatetime = Invalid_DateTime;
1317  Status status;
1318  bool stopWriting = false;
1319  if ((status = getChecksumValues(ckValues, filesize, filedatetime, sumsAlgos, fnNewRel, stopWriting)) == Status::Ok)
1320  {
1321  // Writing directories
1322  ArrayString dirs = getDirectoriesAsArrayString(fnNewRel);
1323  size_t dirIdx = 0;
1324  while ((dirIdx < curDir.size()) && (dirs.size() > 0))
1325  {
1326  if (compareFileName(curDir[dirIdx], dirs[0]) == 0)
1327  {
1328  dirs.erase(dirs.begin());
1329  dirIdx++;
1330  }
1331  else
1332  break;
1333  }
1334  while (ok && curDir.size() > dirIdx)
1335  {
1336  if (curDir.back() == parentDir)
1337  {
1338  ok = writeLine(os, "</parentDirectory>", --indent);
1340  }
1341  else
1342  {
1343  ok = writeLine(os, "</directory>", --indent);
1345  }
1346  curDir.pop_back();
1347  }
1348  while (ok && dirs.size() > 0)
1349  {
1350  curDir.emplace_back(dirs[0]);
1351  if (dirs[0] == parentDir)
1352  {
1353  ok = writeLine(os, "<parentDirectory>", indent++);
1356  }
1357  else
1358  {
1359  ok = writeLine(os, "<directory name=\"" + escapeForXML(dirs[0]) + "\">", indent++);
1362  }
1363  dirs.erase(dirs.begin());
1364  }
1365 
1366  // File part
1367  ok = writeLine(os, "<file name=\"" + escapeForXML(from_u8string(fnNewRel.filename().u8string())) + "\">", indent++);
1368  writerHandler.onFileStart(fnNewRel);
1369  writerHandler.onFileStart(it->getName()); // writerHandler.onFileStart(from_u8string(fnNewRel.filename().u8string()));
1370  if ((writerOptions.writeFileSize() && (filesize != Invalid_File_Size)) || (writerOptions.writeFileDateTime() && (filedatetime != Invalid_DateTime)))
1371  {
1372  string line = "<information";
1373  if (filesize != Invalid_File_Size)
1374  line += " size=\"" + to_string(filesize) + "\"";
1375  if (filedatetime != Invalid_DateTime)
1376  {
1377  ostringstream ss;
1378  #if defined(_WIN32) || defined(_WIN64)
1379  ss << put_time(localtime(&filedatetime), "%Y-%m-%dT%T");
1380  #else
1381  ss << put_time(localtime(&filedatetime), "%FT%T");
1382  #endif
1383  line += " date=\"" + ss.str() + "\"";
1384  }
1385  line += "/>";
1386  ok = writeLine(os, line, indent);
1387  writerHandler.onFileInformation(filesize, filedatetime);
1388  }
1389 
1390  for (const ChecksumValue& ckv : ckValues)
1391  {
1392  string algoName;
1393  if (getXCKSAlgorithmName(algoName, ckv.getType()))
1394  {
1395  ChecksumFormatter ckf(ckfConfProvider, ckv.getType());
1396  ckf.setNumericBase(ChecksumFormatter::NumericBase::hexadecimal);
1399  ok = ok && writeLine(os, "<checksum type=\"" + algoName + "\" value=\"" + ckf.format(ckv.getValue(), ckv.getSize()) + "\"/>", indent);
1401  }
1402  else
1403  assert(false); // "Unexpected unknown checksums algorithm."
1404  }
1405  ok = writeLine(os, "</file>", --indent);
1407  writerHandler.onFile(fnNewRel, ckValues, filesize, filedatetime);
1408  }
1409  else
1410  {
1411  if (stopWriting || (status == Status::BadChecksumValue) ||
1412  (status == Status::BadDuplicateValue) || (status == Status::MissingAlgorithm))
1413  ok = false;
1414  }
1415  ++it;
1416  }
1417  while (ok && curDir.size() > 0)
1418  {
1419  if (curDir.back() == parentDir)
1420  {
1421  ok = writeLine(os, "</parentDirectory>", --indent);
1423  }
1424  else
1425  {
1426  ok = writeLine(os, "</directory>", --indent);
1428  }
1429  curDir.pop_back();
1430  }
1431 
1432  return ok;
1433 }
1434 //---------------------------------------------------------------------------
1435 
1436 
1446 bool XCKSFileWriter::writeV1(ostream& os, const ArrayPath& relativePaths)
1447 {
1448  int indent = 0; // beware to the position of the -- and ++ operators in the calls of writeLine ;)
1449  const ArrayChecksumAlgoId& checksumTypesForNewEntries = sumsAlgos;
1450  bool ok;
1451 
1452  // Header
1453  os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1454  ok = os.good();
1455 
1456  // Root start tag
1457  ok = ok && writeLine(os, "<checksumsFile xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://xcks.sourceforge.net/schemas/xsd-1.0/xcks-" + getStrVersion() + ".xsd\" version=\"" + getStrVersion() + "\">", indent++);
1459 
1460  // "generated" start tag
1462  {
1463  ostringstream ss;
1464  time_t t = time(nullptr);
1465  #if defined(_WIN32) || defined(_WIN64)
1466  ss << put_time(localtime(&t), "%Y-%m-%dT%T");
1467  #else
1468  ss << put_time(localtime(&t), "%FT%T");
1469  #endif
1470  string appName = writerOptions.getApplicationWriter();
1471  string appVersion = writerOptions.getApplicationWriterVersion();
1472  if (appName.empty() || appVersion.empty())
1473  {
1474  appName = getLibraryName();
1475  appVersion = getLibraryVersion();
1476  }
1477  else
1478  {
1480  appName += " (with " + getLibraryNameAndVersion() + ')';
1481  }
1482  ok = ok && writeLine(os, "<generated by=\"" + appName + "\" version=\"" + appVersion + "\" on=\"" + ss.str() + "\"/>", indent);
1484  }
1485 
1486  // "algorithms" start tag
1487  ok = ok && writeLine(os, "<algorithms>", indent++);
1489 
1490  // "algorithm" tags
1491  for (ChecksumAlgoId ckId : checksumTypesForNewEntries)
1492  {
1493  string algoName;
1494  if (getXCKSAlgorithmName(algoName, ckId))
1495  {
1496  ok = ok && writeLine(os, "<algorithm type=\"" + algoName + "\"/>", indent);
1497  writerHandler.onAlgorithm(ckId);
1498  }
1499  else
1500  assert(false); // "Unexpected unknown checksums algorithm."
1501  }
1502 
1503  // "algorithms" end tag
1504  ok = ok && writeLine(os, "</algorithms>", --indent);
1505  writerHandler.onAlgorithms(checksumTypesForNewEntries);
1506 
1507  // "localFiles" start tag
1508  ok = ok && writeLine(os, "<localFiles>", indent++);
1510  ok = ok && writeLocalFilesV1(os, relativePaths, indent);
1511 
1512  // "localFiles" end tag
1513  ok = ok && writeLine(os, "</localFiles>", --indent);
1515 
1516  // Root end tag
1517  ok = ok && writeLine(os, "</checksumsFile>", --indent);
1519 
1520  os.flush();
1521 
1522  return (os.good() && ok);
1523 }
1524 //---------------------------------------------------------------------------
1525 
1526 
1537 bool XCKSFileWriter::writeLine(ostream& os, const string& line, const int indent)
1538 {
1539  if ((!writerOptions.useCompactXML()) && indent > 0)
1540  {
1541  string sIndent;
1543  sIndent.append(indent, '\t');
1544  else
1545  sIndent.append(indent * writerOptions.getIndentSpaces(), ' ');
1546  os << sIndent;
1547  }
1548 
1549  os << line;
1551  os << '\n';
1552 
1553  return os.good();
1554 }
1555 //---------------------------------------------------------------------------
1556 
1557 
1566 bool XCKSFileWriter::getXCKSAlgorithmName(string& algoName, const ChecksumAlgoId algoId)
1567 {
1568  const StringSet& availableChecksumTypesName = possibleSumsAlgos;
1569  ArrayString algoNames;
1570  if (!ChecksumFactory::getAlgorithmAltNames(algoNames, algoId, true))
1571  return false;
1572  StringSet::const_iterator idx = availableChecksumTypesName.cend();
1573  ArrayString::const_iterator it = algoNames.cbegin();
1574  while ((idx == availableChecksumTypesName.cend()) && (it != algoNames.cend()))
1575  {
1576  idx = availableChecksumTypesName.find(*it);
1577  it++;
1578  }
1579  if (idx == availableChecksumTypesName.cend())
1580  return false;
1581  else
1582  {
1583  algoName = *idx;
1584  return true;
1585  }
1586 }
1587 //---------------------------------------------------------------------------
1588 } // namespace libxcks
1589 //---------------------------------------------------------------------------
Classes for enumerate and create all the checksums' algorithms that the application knows.
std::vector< ChecksumValue > ArrayChecksumValue
Array of values of checksum.
Definition: ckvalue.hpp:113
static size_t getSize(const ChecksumAlgoId id)
Gets the size in bytes of the specified checksum or hash identifier.
Definition: ckfactory.cpp:368
static bool getAlgorithmAltNames(ArrayString &altNames, const ChecksumAlgoId id, const bool giveName=false)
Gets the alternatives names of a checksum or hash algorithm from its identifier.
Definition: ckfactory.cpp:667
static bool getAlgorithmId(ChecksumAlgoId &id, const std::string &name, const bool lookInAltNames=true)
Gets the identifier of a checksum or hash algorithm from its name.
Definition: ckfactory.cpp:518
Configuration provider for ChecksumFormatter.
Definition: ckformatter.hpp:69
This class provides some convenient tools to format checksum and hash values.
Definition: ckformatter.hpp:52
std::string format(const uint8_t *value, const size_t size)
Format the given checksum's value.
void setNumericBase(const NumericBase base)
Sets the numeric base for the output.
void setSpacesPositions(const std::string &spacesPositions)
Sets the positions of the spaces between two bytes.
std::set< uint_fast16_t > Positions
Set of unsigned integers.
Definition: ckformatter.hpp:55
Stores the value of a checksum.
Definition: ckvalue.hpp:45
ChecksumAlgoId getType() const
Gets the type of the checksum.
Definition: ckvalue.cpp:436
A directory of the directory structure for XCKS files.
void setChild(Element *newChild) override final
Sets the child of the element.
Element * child
Address of the first child if the element if a directory.
Directory & operator=(const Directory &)=delete
Deleted assignment operator.
bool isAFile() const override final
Does the element is a file?
Directory()=delete
Deleted default constructor.
Element * getChild() const override final
Gets the child of the element.
bool hasChild() const override final
Does the element have a child?
Directory(const Directory &)=delete
Deleted copy constructor.
An element of the directory structure for XCKS files.
bool addFile(const ArrayString &dirs, const size_t cur, const string &filename, void *data=nullptr)
Adds a new file in the directory structure.
virtual Element * getParent() const final
Gets the parent element of the element.
Element(const Element &)=delete
Deleted copy constructor.
virtual void setPrevious(Element *newPrevious) final
Sets the previous element of the element.
virtual Element * getElement(const string &elementName, Element **lastElement=nullptr) const final
Gets the element with the given name in the current level.
virtual Element * getChild() const
Gets the child of the element.
virtual bool hasChild() const
Does the element have a child?
virtual bool hasPrevious() const final
Does the element have a previous element?
string name
The name of the element.
Element * parent
Address of the parent.
virtual bool isAFile() const =0
Does the element is a file?
virtual Element * getNext() const final
Gets the following element of the element.
virtual void setData(void *newData)
Sets the data associated with the element.
virtual void setChild(Element *newChild)
Sets the child of the element.
Element * next
Address of the next element at the same level.
XCKSDirStruct::File * getNextFile() const
Gets the next file in the directory structure.
virtual void getParent(Element *newParent) final
Sets the parent element of the element.
virtual bool hasElement(const string &elementName) const final
Does an element exist with the given name in the current level?
virtual bool hasNext() const final
Does the element have a following element?
Element * createDirsAndAddFile(const ArrayString &dirs, const size_t cur, const string &filename, void *data=nullptr)
Creates directories and adds a new file in the directory structure.
XCKSDirStruct::File * getNextFile(const bool calledFromChild) const
Gets the next file in the directory structure.
XCKSDirStruct::File * getNextFileRec() const
Gets the next file in the directory structure (recursive).
Element * previous
Address of the previous element at the same level.
virtual void setNext(Element *newNext) final
Sets the following element of the element.
Element & operator=(const Element &)=delete
Deleted assignment operator.
virtual bool hasParent() const final
Does the element have a parent?
virtual string getName() const final
Gets the name of the element.
Element()=delete
Deleted default constructor.
virtual Element * getPrevious() const final
Gets the previous element of the element.
virtual void * getData() const
Gets the data associated with the element.
An file of the directory structure for XCKS files.
bool isAFile() const override final
Does the element is a file?
void setData(void *newData) override final
Sets the data associated with the element.
void * data
Data associated with the element.
File & operator=(const File &)=delete
Deleted assignment operator.
File(const File &)=delete
Deleted copy constructor.
void * getData() const override
Gets the data associated with the element.
virtual ~File()
Destructor.
File()=delete
Deleted default constructor.
A forward iterator on files of a directory structure.
const XCKSDirStruct::File & operator*() const
Indirection operator.
const XCKSDirStruct::File * operator->() const
Member of pointer operator.
bool operator!=(const const_file_iterator &it) const
Inequality operator.
const_file_iterator & operator++()
Prefix increment operator.
const_file_iterator operator++(int)
Postfix increment operator.
const_file_iterator(XCKSDirStruct::File *initFile)
Constructor.
bool operator==(const const_file_iterator &it) const
Equality operator.
const_file_iterator & operator=(const const_file_iterator &)=default
Assignment operator.
const_file_iterator(const const_file_iterator &)=default
Copy constructor.
XCKSDirStruct::File * file
Current file.
void plusPlus()
Do the ++ operation on the iterator.
A directories structure for XCKS files.
XCKSDirStruct & operator=(const XCKSDirStruct &)=delete
Deleted assignment operator.
XCKSDirStruct(const XCKSDirStruct &)=delete
Deleted copy constructor.
const_file_iterator files_end() const
Returns an iterator pointing at the one-after-the-last file of directory structure.
bool addFile(const filesystem::path &filename, void *data=nullptr)
Adds a new file in the directory structure.
const_file_iterator files_begin() const
Returns an iterator pointing at the first file of the directory structure.
Element * root
Root of the directory structure.
XCKSDirStruct()
Default constructor.
ArrayChecksumAlgoId getValidChecksumTypesForNewEntries(const ArrayChecksumAlgoId &ids) const
Gets a valid list of algorithms to calculate for each file.
bool getXCKSAlgorithmName(std::string &algoName, const ChecksumAlgoId algoId)
Gets the algorithm name in the XCKS specification.
std::string getStrVersion() const
Gets the version of the checksums' file type as a string.
static const std::string parentDir
Parent directory on Unix and windows.
XCKSFileWriter(XCKSWriterHandler &xcksWriterHandler, XCKSWriterChecksumProvider &checksumProvider, const XCKSWriterOptions &options, const std::filesystem::path &baseDir, const ArrayChecksumAlgoId &algoIds, const ChecksumFormatter::ConfigurationProvider &confProvider, Version version=Version::Last_Valid)
Constructor.
Version getVersion() const
Returns the version of the checksums' file type.
static Version getValidVersion(const Version ver)
Ensures a valid version of XCKS file.
XCKSWriterChecksumProvider::Status getChecksumValues(ArrayChecksumValue &ckValues, uintmax_t &filesize, time_t &filedatetime, const ArrayChecksumAlgoId &ids, const std::filesystem::path &filePath, bool &stopWriting) const
Gets checksums values from an array of wanted algorithms of checksums.
const std::filesystem::path baseDirectory
The base directory of the checksums' file.
bool writeV1(std::ostream &os, const ArrayPath &relativePaths)
Writes the XCKS file version 1.0.0 in a stream.
ArrayChecksumAlgoId getChecksumTypesForNewEntries() const
Returns the list of checksums algorithms to compute for new entries of this type of checksums' file.
const Version version
Version of the (Z)XCKS file to write.
const XCKSWriterOptions & writerOptions
The writer options.
bool writeLine(std::ostream &os, const std::string &line, const int indent)
Writes a line in the XCKS file.
XCKSWriterHandler & writerHandler
The XCKS file writer handler.
const ChecksumFormatter::ConfigurationProvider & ckfConfProvider
Configuration provider for ChecksumFormatter.
StringSet getAvailableChecksumTypesName() const
Returns the list of names of available checksums algorithms for this particular version of XCKS file.
const StringSet possibleSumsAlgos
Possible algorithms names for a (Z)XCKS file.
bool write(std::ostream &os, const ArrayPath &relativePaths)
Writes the checksums in a stream.
bool writeLocalFilesV1(std::ostream &os, const ArrayPath &relativePaths, int &indent)
Writes the "localFiles" section of a XCKS file version 1.0.0 in a stream.
const ArrayChecksumAlgoId sumsAlgos
Type of algorithms' checksums to write for each file.
XCKSWriterChecksumProvider & ckProvider
The provider of checksums values.
ArrayChecksumAlgoId getAvailableChecksumTypes() const
Returns the list of available checksums algorithms for this type of checksums' file.
Provides checksums values from an array of wanted algorithms of checksums for the XCKS writer.
Definition: handlers.hpp:375
virtual Status getChecksumValues(ArrayChecksumValue &sumValues, uintmax_t &filesize, time_t &filedatetime, const ArrayChecksumAlgoId &ids, const std::filesystem::path &filepath)=0
Provides checksums values from an array of wanted algorithms of checksums.
Status
Return status of getChecksumValues().
Definition: handlers.hpp:379
Handler for writing XCKS files.
Definition: handlers.hpp:423
virtual void onFatalError(const std::string &errorMessage)=0
Called on a non-recoverable error.
virtual void onChecksumsFileEnd()=0
Called on write checksumsFile end tag.
virtual void onParentDirectoryStart()=0
Called on write parentDirectory start tag.
virtual void onFileChecksum(const ChecksumValue &value)=0
Called on write checksum element (for file).
virtual void onFile(const std::filesystem::path &path, const ArrayChecksumValue &checksums, const uintmax_t size=Invalid_File_Size, const time_t date=Invalid_DateTime)=0
Called on write file element.
virtual void onDirectoryEnd()=0
Called on write directory end tag.
virtual void onAlgorithms(const ArrayChecksumAlgoId &types)=0
Called on write algorithms element.
virtual void onParentDirectoryEnd()=0
Called on write parentDirectory end tag.
virtual void onChecksumsFileStart(const std::string &version)=0
Called on write checksumsFile start tag.
virtual void onFileGetChecksumsError(const std::filesystem::path &relativePath, const std::filesystem::path &absolutePath, const XCKSWriterChecksumProvider::Status status, bool &stopWriting, const ChecksumAlgoId algoId=ChecksumAlgoId::Invalid)=0
Called on a failure on getting the checksums values for a file.
virtual void onLocalFilesStart()=0
Called on write localFiles start tag.
virtual void onGenerated(const std::string &by, const std::string &version, const std::time_t on)=0
Called on write generated element.
virtual void onFileGetChecksumError(const std::filesystem::path &relativePath, const std::filesystem::path &absolutePath, const XCKSWriterChecksumProvider::Status status, const ChecksumValue &ckValue)=0
Called on a failure on verifying a checksums value for a file.
virtual void onAlgorithm(const ChecksumAlgoId type)=0
Called on write algorithm element.
virtual void onFileInformation(const uintmax_t size=Invalid_File_Size, const time_t date=Invalid_DateTime)=0
Called on write information tag (for file).
virtual void onDirectoryStart(const std::string name)=0
Called on write directory start tag.
virtual void onLocalFilesEnd()=0
Called on write localFiles end tag.
virtual void onAlgorithmsStart()=0
Called on write algorithms start tag.
virtual void onFileStart(const std::string name)=0
Called on write file start tag.
virtual void onFileEnd()=0
Called on write file end tag.
Provides options for the XCKS writer.
Definition: handlers.hpp:818
virtual bool writeGeneratedElement() const final
Indicates whether the generated element must be written in the XCKS file.
Definition: handlers.hpp:890
virtual bool useCompactXML() const final
Indicates whether the XCKS file must be written in its compact form or not.
Definition: handlers.hpp:938
virtual bool writeFileDateTime() const final
Indicates whether the files' date/time must be written in the XCKS file.
Definition: handlers.hpp:922
virtual unsigned int getIndentSpaces() const final
Returns the number of spaces to use for indention (meaningful only if useTabsforIndentation() returns...
Definition: handlers.hpp:970
virtual bool writeLibXCKSVersion() const final
Indicates whether the name and version of the libxcks must be written with the application name and v...
Definition: handlers.hpp:1047
virtual bool checkReturnedChecksums() const final
Indicates whether all checksums values returned by an XCKSWriterChecksumProvider instance must be ver...
Definition: handlers.hpp:988
virtual std::string getApplicationWriter() const final
Gets the name of the application using libxcks to write the XCKS file.
Definition: handlers.hpp:1007
virtual std::string getApplicationWriterVersion() const final
Gets the version of the application using libxcks to write the XCKS file.
Definition: handlers.hpp:1026
virtual bool useTabsforIndentation() const final
Indicates which type of indentation should be used when writing the XCKS file.
Definition: handlers.hpp:954
virtual bool writeFileSize() const final
Indicates whether the files' size must be written in the XCKS file.
Definition: handlers.hpp:906
Common definitions for libxcks.
constexpr const char * libxcks_domain
Domain for translations (i18n).
Definition: defs.hpp:47
constexpr std::uintmax_t Invalid_File_Size
Invalid file size.
Definition: defs.hpp:37
constexpr std::time_t Invalid_DateTime
Invalid date/time.
Definition: defs.hpp:42
int compareFileName(const std::string &fn1, const std::string &fn2)
Compares two file names.
Definition: pathutil.cpp:108
std::filesystem::path constructPath(const ArrayString &dirs, const std::string &filename)
Constructs a path from an array of directories and an optional filename.
Definition: pathutil.cpp:170
ArrayString getDirectoriesAsArrayString(const std::filesystem::path &p)
Gets directories of a path.
Definition: pathutil.cpp:81
std::filesystem::path ensureEndsWithPathSeparator(const std::filesystem::path &p)
Ensures the path ends with a path separator.
Definition: pathutil.cpp:209
Path utilities.
std::string from_u8string(const std::string &s)
Returns an UTF-8 encoded string in an object of type std::string (C++17).
Definition: strutil.cpp:217
std::string escapeForXML(const std::string &str)
Escapes a string for writing it in a XML stream.
Definition: strutil.cpp:318
String utilities.
std::vector< ChecksumAlgoId > ArrayChecksumAlgoId
Array of ids of algorithms of checksums.
Definition: types.hpp:92
std::vector< std::filesystem::path > ArrayPath
Array of paths.
Definition: types.hpp:49
ChecksumAlgoId
Ids of algorithms of checksums.
Definition: types.hpp:65
std::vector< std::string > ArrayString
Array of strings.
Definition: types.hpp:44
Version
Known versions of XCKS file.
Definition: types.hpp:55
std::set< std::string > StringSet
Set of strings.
Definition: types.hpp:39
Version of the library.
LIBXCKS_SO_EXPORT const std::string getLibraryVersion()
Gets the version of the library.
Definition: version.cpp:61
LIBXCKS_SO_EXPORT const std::string getLibraryName()
Gets the name of the library.
Definition: version.cpp:49
LIBXCKS_SO_EXPORT const std::string getLibraryNameAndVersion()
Gets the name and the version of the library.
Definition: version.cpp:73
Classes that write a XCKS file.
Versions of (Z)XCKS files.
LIBXCKS_SO_EXPORT const StringSet getAvailableAlgorithmNames(const Version version)
Gets the available names of algorithm for a version of (Z)XCKS file.
Definition: xcksver.cpp:41