25 #include <boost/locale.hpp>
39 using namespace boost::locale;
42 using namespace std::filesystem;
60 static bool isFileNameIsInStringSet(
const StringSet& set,
const string& filename)
62 #if defined(_WIN32) || defined(_WIN64)
64 StringSet::const_iterator it = set.cbegin();
66 while (notFound && it != set.cend())
75 return (find(set.cbegin(), set.cend(), filename) != set.cend());
82 const string XCKS1FileHandler::parentDir =
"..";
94 const std::filesystem::path& baseDir) : readerHandler(handler),
95 baseDirectory(baseDir),
99 assert(baseDir.is_absolute());
125 if (str ==
"checksum")
127 else if (str ==
"file")
129 else if (str ==
"information")
131 else if (str ==
"directory")
133 else if (str ==
"parentDirectory")
135 else if (str ==
"algorithm")
137 else if (str==
"algorithms")
139 else if (str ==
"localFiles")
141 else if (str ==
"generated")
143 else if (str ==
"checksumsFile")
158 if ((readTags.top() == XMLTags::CHECKSUMSFILE) && (tagInChecksumsFileTag == XMLTags::SEQ_BEGIN))
160 if (atts.getCount() == 3)
162 int byIdx = atts.getIndex(
"by"),
163 versionIdx = atts.getIndex(
"version"),
164 onIdx = atts.getIndex(
"on");
169 atts.getValue(onIdx, onStr);
172 tagInChecksumsFileTag = XMLTags::GENERATED;
173 readTags.push(XMLTags::GENERATED);
175 atts.getValue(byIdx, by);
176 atts.getValue(versionIdx, version);
177 readerHandler.onGenerated(by, version, on);
209 if ((readTags.top() == XMLTags::CHECKSUMSFILE) &&
210 ((tagInChecksumsFileTag == XMLTags::SEQ_BEGIN) || (tagInChecksumsFileTag == XMLTags::GENERATED)))
212 if (atts.getCount() == 0)
214 tagInChecksumsFileTag = XMLTags::ALGORITHMS;
215 readTags.push(XMLTags::ALGORITHMS);
216 readerHandler.onAlgorithmsStart();
238 if (readTags.top() == XMLTags::ALGORITHMS)
240 if (atts.getCount() == 1)
243 if (atts.getValue(
"type", type))
245 if (possibleSumsAlgosName.find(type) != possibleSumsAlgosName.end())
250 if (find(sumsAlgos.cbegin(), sumsAlgos.cend(), ckId) == sumsAlgos.cend())
252 sumsAlgos.push_back(ckId);
253 readTags.push(XMLTags::ALGORITHM);
254 readerHandler.onAlgorithm(ckId);
296 if ((readTags.top() == XMLTags::CHECKSUMSFILE) && (tagInChecksumsFileTag == XMLTags::ALGORITHMS))
298 if (atts.getCount() == 0)
301 tagInChecksumsFileTag = XMLTags::LOCALFILES;
302 readTags.push(XMLTags::LOCALFILES);
303 readerHandler.onLocalFilesStart();
325 if ((readTags.top() == XMLTags::LOCALFILES) || (readTags.top() == XMLTags::PARENTDIRECTORY))
327 if (atts.getCount() == 0)
329 StringSet& filesAndDirsInDir = namesInDirectories.top();
330 if (!isFileNameIsInStringSet(filesAndDirsInDir, parentDir))
332 if (curBaseDirectoryDir >= 0)
334 filesAndDirsInDir.emplace(parentDir);
335 currentRelativeDirectory.emplace_back(parentDir);
337 namesInDirectories.top().emplace(
from_u8string(baseDirectoryDirs[curBaseDirectoryDir].u8string()));
338 curBaseDirectoryDir--;
339 readTags.push(XMLTags::PARENTDIRECTORY);
340 readerHandler.onParentDirectoryStart();
373 if ((readTags.top() == XMLTags::LOCALFILES) ||
374 (readTags.top() == XMLTags::PARENTDIRECTORY) ||
375 (readTags.top() == XMLTags::DIRECTORY))
377 if (atts.getCount() == 1)
380 if (atts.getValue(
"name", name))
384 StringSet& filesAndDirsInDir = namesInDirectories.top();
385 if (!isFileNameIsInStringSet(filesAndDirsInDir, name))
387 filesAndDirsInDir.emplace(name);
389 currentRelativeDirectory.emplace_back(name);
390 readTags.push(XMLTags::DIRECTORY);
391 readerHandler.onDirectoryStart(name);
396 throw XMLParserException((format(translate(
"There is already a file or a directory with the name \"{1}\" in the current directory.").str(
libxcks_domain)) % name).str());
429 if ((readTags.top() == XMLTags::LOCALFILES) ||
430 (readTags.top() == XMLTags::PARENTDIRECTORY) ||
431 (readTags.top() == XMLTags::DIRECTORY))
433 if (atts.getCount() == 1)
436 if (atts.getValue(
"name", name))
440 StringSet& filesAndDirsInDir = namesInDirectories.top();
441 if (!isFileNameIsInStringSet(filesAndDirsInDir, name))
443 filesAndDirsInDir.emplace(name);
445 tagInFileTag = XMLTags::SEQ_BEGIN;
448 readChecksumsValues.clear();
449 algosToRead = sumsAlgos;
450 readTags.push(XMLTags::FILE);
451 readerHandler.onFileStart(fileName);
452 readerHandler.onFileStart((baseDirectory /
constructPath(currentRelativeDirectory, fileName)).lexically_normal());
456 throw XMLParserException((format(translate(
"There is already a file or a directory with the name \"{1}\" in the current directory.").str(
libxcks_domain)) % name).str());
489 if ((readTags.top() == XMLTags::FILE) && (tagInFileTag == XMLTags::SEQ_BEGIN))
491 if (atts.getCount() >= 0 && atts.getCount() <= 2)
493 string fileSize, fileDate;
494 for (
size_t i = 0; i < atts.getCount(); i++)
497 atts.getName(i, attName);
498 if (attName ==
"size")
499 atts.getValue(i, fileSize);
500 else if (attName ==
"date")
501 atts.getValue(i, fileDate);
503 throw XMLParserException((format(translate(
"Invalid attribute \"{1}\" for the \"information\" tag (valid values are \"size\" and \"date\").").str(
libxcks_domain)) % attName).str());
505 if (!fileSize.empty())
510 unsigned long long fs = stoull(fileSize);
511 fileLength =
static_cast<uintmax_t
>(fs);
518 throw XMLParserException((format(translate(
"The \"size\" attribute of the \"information\" tag has an invalid value: {1}.").str(
libxcks_domain)) % fileSize).str());
520 if (!fileDate.empty())
522 throw XMLParserException((format(translate(
"The \"date\" attribute of the \"information\" tag has an invalid value: {1}.").str(
libxcks_domain)) % fileDate).str());
524 tagInFileTag = XMLTags::INFORMATION;
525 readTags.push(XMLTags::INFORMATION);
526 readerHandler.onFileInformation(fileLength, lastFileModicationTime);
548 if ((readTags.top() == XMLTags::FILE) &&
549 ((tagInFileTag == XMLTags::SEQ_BEGIN) || (tagInFileTag == XMLTags::INFORMATION)))
551 if (!sumsAlgos.empty())
553 if (atts.getCount() == 2)
557 for (
size_t i = 0; i < atts.getCount(); i++)
560 atts.getName(i, attName);
561 if (attName ==
"type")
562 atts.getValue(i, type);
563 else if (attName ==
"value")
564 atts.getValue(i, value);
566 throw XMLParserException((format(translate(
"Invalid attribute \"{1}\" for the \"checksum\" tag (valid values are \"type\" and \"value\").").str(
libxcks_domain)) % attName).str());
569 if (possibleSumsAlgosName.find(type) != possibleSumsAlgosName.end())
576 ArrayChecksumAlgoId::const_iterator itCkId;
577 if ((itCkId = find(algosToRead.cbegin(), algosToRead.cend(), ckId)) != algosToRead.cend())
583 readChecksumsValues.emplace_back(ckv);
584 algosToRead.erase(itCkId);
585 readTags.push(XMLTags::CHECKSUM);
586 readerHandler.onFileChecksum(ckv);
595 throw XMLParserException((format(translate(
"The algorithm \"{1}\" isn't present in the algorithms of the checksums' file.").str(
libxcks_domain)) % type).str());
600 throw XMLParserException((format(translate(
"The algorithm \"{1}\" has been already read once for the file \"{2}\".").str(
libxcks_domain)) % type % fileName).str());
639 XMLTags curTag = stringToXMLTags(name);
642 case XMLTags::GENERATED :
643 startGeneratedElement(atts);
646 case XMLTags::ALGORITHMS :
647 startAlgorithmsElement(atts);
650 case XMLTags::ALGORITHM :
651 startAlgorithmElement(atts);
654 case XMLTags::LOCALFILES :
655 startLocalFilesElement(atts);
658 case XMLTags::PARENTDIRECTORY :
659 startParentDirectoryElement(atts);
662 case XMLTags::DIRECTORY :
663 startDirectoryElement(atts);
667 startFileElement(atts);
670 case XMLTags::INFORMATION :
671 startInformationElement(atts);
674 case XMLTags::CHECKSUM :
675 startChecksumElement(atts);
721 XMLTags curTag = stringToXMLTags(name);
722 if (curTag != readTags.top())
728 case XMLTags::GENERATED :
732 case XMLTags::ALGORITHMS :
733 readerHandler.onAlgorithmsEnd();
734 if (sumsAlgos.empty())
738 readerHandler.onAlgorithms(sumsAlgos);
742 case XMLTags::ALGORITHM :
746 case XMLTags::LOCALFILES :
747 if (namesInDirectories.size() != 1)
749 throw XMLParserException((format(
"There should be only one list of filenames to pop, but there is {1}. This shouldn't happen.") % namesInDirectories.size()).str());
753 namesInDirectories.pop();
754 readerHandler.onLocalFilesEnd();
758 case XMLTags::PARENTDIRECTORY :
759 curBaseDirectoryDir++;
760 if (curBaseDirectoryDir >=
static_cast<ssize_t
>(baseDirectoryDirs.size()))
762 throw XMLParserException(
"Pointer to current directory of new checksums' file is out of bounds. This shouldn't happen.");
764 readerHandler.onParentDirectoryEnd();
766 case XMLTags::DIRECTORY :
767 if (namesInDirectories.empty())
769 throw XMLParserException(
"There is no list of filenames to pop. This shouldn't happen.");
773 if (namesInDirectories.top().empty())
779 namesInDirectories.pop();
782 if (currentRelativeDirectory.empty())
784 throw XMLParserException(
"There is no directory to pop in the directories' list. This shouldn't happen.");
788 currentRelativeDirectory.pop_back();
790 if (curTag == XMLTags::DIRECTORY)
791 readerHandler.onDirectoryEnd();
798 case XMLTags::INFORMATION :
802 case XMLTags::CHECKSUM :
840 readerHandler.onFatalError(errorMessage, line, column);
Classes for enumerate and create all the checksums' algorithms that the application knows.
bool LIBXCKS_SO_EXPORT hasChecksumValueWithAlgoId(const ArrayChecksumValue &ckvalues, const ChecksumAlgoId type)
Checks if the array as a value of the given type.
static ChecksumValue getNullValue(const ChecksumAlgoId id)
Gives null value of the specified checksum or hash identifier.
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.
static bool getAlgorithmName(std::string &name, const ChecksumAlgoId id)
Gets the name of a checksum or hash algorithm from its identifier.
Stores the value of a checksum.
size_t getSize() const
Gets the size of the checksum's value.
void startDirectoryElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "directory" element.
virtual ~XCKS1FileHandler()
Destructor.
ArrayChecksumValue readChecksumsValues
Read checksums values.
void fatalError(XML_Error errorCode, const std::string &errorMessage, int line, int column) noexcept override
Receive notification of a non-recoverable error.
void startLocalFilesElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "localFiles" element.
XMLTags tagInChecksumsFileTag
Current tag in "checksumsFile" tag.
void startElement(const std::string &name, const XMLParserAttributes &atts) noexcept(false) override
Receives notification of the beginning of an element.
void endFileElement() noexcept(false)
Receives notification of the end of a "file" element.
void startGeneratedElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "generated" element.
ArrayChecksumAlgoId algosToRead
Remaining algorithms to read for a file.
ssize_t curBaseDirectoryDir
Current directory in the base directory of the checksums' file name.
uintmax_t fileLength
The current file size.
std::string fileName
Name of checksums' file.
time_t lastFileModicationTime
The current last file modification time.
std::stack< XMLTags > readTags
Stack of read tags.
void startFileElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "file" element.
void startAlgorithmElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "algorithm" element.
@ DIRECTORY
directory element
@ GENERATED
generated element
@ LOCALFILES
localFiles element
@ BAD_TAG
Bad or unknown tag.
@ SEQ_BEGIN
Beginning of a sequence.
@ CHECKSUMSFILE
checksumsFile element
@ PARENTDIRECTORY
parentDirectory element
@ CHECKSUM
checksum element
@ INFORMATION
information element
@ ALGORITHM
algorithm element
@ ALGORITHMS
algorithms element
void startChecksumElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "checksum" element.
ArrayString currentRelativeDirectory
The current relative directory.
void startAlgorithmsElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "algorithms" element.
void characters(const std::string &chars) noexcept(false) override
Receives notification of character data.
XMLTags stringToXMLTags(const std::string &str)
Gets a XML tag identifier from a string.
void startParentDirectoryElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "parentDirectory" element.
const std::filesystem::path baseDirectory
The base directory of the checksums' file.
void startInformationElement(const XMLParserAttributes &atts) noexcept(false)
Receives notification of the beginning of a "information" element.
void endElement(const std::string &name) noexcept(false) override
Receives notification of the end of an element.
const std::vector< std::filesystem::path > baseDirectoryDirs
The base directory of the checksums' file directories.
XCKSReaderHandler & readerHandler
The XCKS file reader handler.
Handler for reading XCKS files.
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 read file element.
virtual void onFileEnd()=0
Called on read file end tag.
Manages elements' attributes.
static constexpr int Not_Found
Not found index.
An exception class for the XML parser.
constexpr const char * libxcks_domain
Domain for translations (i18n).
constexpr std::uintmax_t Invalid_File_Size
Invalid file size.
constexpr std::time_t Invalid_DateTime
Invalid date/time.
bool isFileNameValid(const std::string &filename)
Checks if the name of the file is valid.
std::vector< std::filesystem::path > getDirectories(const std::filesystem::path &p)
Gets directories of a path.
int compareFileName(const std::string &fn1, const std::string &fn2)
Compares two file names.
std::filesystem::path constructPath(const ArrayString &dirs, const std::string &filename)
Constructs a path from an array of directories and an optional filename.
std::filesystem::path ensureEndsWithPathSeparator(const std::filesystem::path &p)
Ensures the path ends with a path separator.
std::string trim_copy(std::string s)
Trim from both ends (copying).
time_t parseISO8601(const std::string &iso8601)
Parse ISO 8601 date/time.
std::string from_u8string(const std::string &s)
Returns an UTF-8 encoded string in an object of type std::string (C++17).
ChecksumAlgoId
Ids of algorithms of checksums.
Version
Known versions of XCKS file.
std::set< std::string > StringSet
Set of strings.
Classes that read a XCKS file version 1.
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.