From f8d839d6eb08be5e15ff8140ab911e20d1e9b204 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 14 Dec 2023 12:30:14 +0100 Subject: [PATCH] move root folder detection out of file_zip. added support for Descent Hog and Mvl files., mainly useful for playing Descent's music directly from the asset files. --- src/CMakeLists.txt | 2 + src/common/audio/music/music.cpp | 2 + src/common/filesystem/include/resourcefile.h | 1 + src/common/filesystem/source/file_hog.cpp | 109 + src/common/filesystem/source/file_mvl.cpp | 98 + src/common/filesystem/source/file_zip.cpp | 60 +- src/common/filesystem/source/resourcefile.cpp | 113 +- src/common/filesystem/source/wildcards.hpp | 1833 +++++++++++++++++ 8 files changed, 2156 insertions(+), 62 deletions(-) create mode 100644 src/common/filesystem/source/file_hog.cpp create mode 100644 src/common/filesystem/source/file_mvl.cpp create mode 100644 src/common/filesystem/source/wildcards.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 29737154f2..d57a56c86e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1220,6 +1220,8 @@ set( GAME_SOURCES common/filesystem/source/file_pak.cpp common/filesystem/source/file_whres.cpp common/filesystem/source/file_ssi.cpp + common/filesystem/source/file_hog.cpp + common/filesystem/source/file_mvl.cpp common/filesystem/source/file_directory.cpp common/filesystem/source/resourcefile.cpp common/filesystem/source/files.cpp diff --git a/src/common/audio/music/music.cpp b/src/common/audio/music/music.cpp index 51797220e1..43e05cb5c6 100644 --- a/src/common/audio/music/music.cpp +++ b/src/common/audio/music/music.cpp @@ -703,6 +703,8 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force) // opening the music must be done by the game because it's different depending on the game's file system use. FileReader reader = mus_cb.OpenMusic(musicname); if (!reader.isOpen()) return false; + auto m = reader.Read(); + reader.Seek(0, FileReader::SeekSet); // shutdown old music S_StopMusic(true); diff --git a/src/common/filesystem/include/resourcefile.h b/src/common/filesystem/include/resourcefile.h index 7dea3304bb..ea7940b251 100644 --- a/src/common/filesystem/include/resourcefile.h +++ b/src/common/filesystem/include/resourcefile.h @@ -140,6 +140,7 @@ private: int FilterLumps(const std::string& filtername, uint32_t max); bool FindPrefixRange(const char* filter, uint32_t max, uint32_t &start, uint32_t &end); void JunkLeftoverFilters(uint32_t max); + void FindCommonFolder(LumpFilterInfo* filter); static FResourceFile *DoOpenResourceFile(const char *filename, FileReader &file, bool containeronly, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); public: diff --git a/src/common/filesystem/source/file_hog.cpp b/src/common/filesystem/source/file_hog.cpp new file mode 100644 index 0000000000..f81c921c6c --- /dev/null +++ b/src/common/filesystem/source/file_hog.cpp @@ -0,0 +1,109 @@ +/* +** file_hog.cpp +** +** reads Descent .hog files +** +**--------------------------------------------------------------------------- +** Copyright 2023 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + + +#include "resourcefile.h" +#include "fs_swap.h" + +namespace FileSys { + using namespace byteswap; + + + + +static bool OpenHog(FResourceFile* rf, LumpFilterInfo* filter) +{ + auto Reader = rf->GetContainerReader(); + FileReader::Size length = Reader->GetLength(); + + std::vector entries; + // Hogs store their data as a list of file records, each containing a name, length and the actual data. + // To read the directory the entire file must be scanned. + while (Reader->Tell() <= length) + { + char name[13]; + + auto r = Reader->Read(&name, 13); + if (r < 13) break; + name[12] = 0; + uint32_t elength = Reader->ReadUInt32(); + + FResourceEntry Entry; + Entry.Position = Reader->Tell(); + Entry.Length = elength; + Entry.Flags = 0; + Entry.CRC32 = 0; + Entry.Namespace = ns_global; + Entry.ResourceID = -1; + Entry.Method = METHOD_STORED; + Entry.FileName = rf->NormalizeFileName(name); + entries.push_back(Entry); + Reader->Seek(elength, FileReader::SeekCur); + } + auto Entries = rf->AllocateEntries(entries.size()); + memcpy(Entries, entries.data(), entries.size() * sizeof(Entries[0])); + rf->GenerateHash(); + return true; +} + + +//========================================================================== +// +// File open +// +//========================================================================== + +FResourceFile* CheckHog(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp) +{ + char head[3]; + + if (file.GetLength() >= 20) + { + file.Seek(0, FileReader::SeekSet); + file.Read(&head, 3); + if (!memcmp(head, "DHF", 3)) + { + auto rf = new FResourceFile(filename, file, sp); + if (OpenHog(rf, filter)) return rf; + file = rf->Destroy(); + } + file.Seek(0, FileReader::SeekSet); + } + return nullptr; +} + + +} \ No newline at end of file diff --git a/src/common/filesystem/source/file_mvl.cpp b/src/common/filesystem/source/file_mvl.cpp new file mode 100644 index 0000000000..48babef16d --- /dev/null +++ b/src/common/filesystem/source/file_mvl.cpp @@ -0,0 +1,98 @@ +/* +** file_mvl.cpp +** +** reads Descent2 .mvl files +** +**--------------------------------------------------------------------------- +** Copyright 2023 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + + +#include "resourcefile.h" +#include "fs_swap.h" + +namespace FileSys { + using namespace byteswap; + + + +static bool OpenMvl(FResourceFile* rf, LumpFilterInfo* filter) +{ + auto Reader = rf->GetContainerReader(); + auto count = Reader->ReadUInt32(); + auto Entries = rf->AllocateEntries(count); + size_t pos = 8 + (17 * count); // files start after the directory + + for (uint32_t i = 0; i < count; i++) + { + char name[13]; + Reader->Read(&name, 13); + name[12] = 0; + uint32_t elength = Reader->ReadUInt32(); + + Entries[i].Position = pos; + Entries[i].Length = elength; + Entries[i].ResourceID = -1; + Entries[i].FileName = rf->NormalizeFileName(name); + + pos += elength; + } + + return true; +} + + +//========================================================================== +// +// File open +// +//========================================================================== + +FResourceFile* CheckMvl(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp) +{ + char head[4]; + + if (file.GetLength() >= 20) + { + file.Seek(0, FileReader::SeekSet); + file.Read(&head, 4); + if (!memcmp(head, "DMVL", 4)) + { + auto rf = new FResourceFile(filename, file, sp); + if (OpenMvl(rf, filter)) return rf; + file = rf->Destroy(); + } + file.Seek(0, FileReader::SeekSet); + } + return nullptr; +} + + +} \ No newline at end of file diff --git a/src/common/filesystem/source/file_zip.cpp b/src/common/filesystem/source/file_zip.cpp index df6929e23c..95d05c604c 100644 --- a/src/common/filesystem/source/file_zip.cpp +++ b/src/common/filesystem/source/file_zip.cpp @@ -209,55 +209,7 @@ bool FZipFile::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf) Printf(FSMessageLevel::Error, "%s: Central directory corrupted.", FileName); return false; } - - for (auto& c : name) c = tolower(c); - - auto vv = name.find("__macosx"); - if (name.find("filter/") == 0) - continue; // 'filter' is a reserved name of the file system. - if (name.find("__macosx") == 0) - continue; // skip Apple garbage. At this stage only the root folder matters. - if (name.find(".bat") != std::string::npos || name.find(".exe") != std::string::npos) - continue; // also ignore executables for this. - if (!foundprefix) - { - // check for special names, if one of these gets found this must be treated as a normal zip. - bool isspecial = name.find("/") == std::string::npos || - (filter && std::find(filter->reservedFolders.begin(), filter->reservedFolders.end(), name) != filter->reservedFolders.end()); - if (isspecial) break; - name0 = std::string(name, 0, name.rfind("/")+1); - name1 = std::string(name, 0, name.find("/") + 1); - foundprefix = true; - } - - if (name.find(name0) != 0) - { - if (!name1.empty()) - { - name0 = name1; - if (name.find(name0) != 0) - { - name0 = ""; - } - } - if (name0.empty()) - break; - } - if (!foundspeciallump && filter) - { - // at least one of the more common definition lumps must be present. - for (auto &p : filter->requiredPrefixes) - { - if (name.find(name0 + p) == 0 || name.rfind(p) == size_t(name.length() - p.length())) - { - foundspeciallump = true; - break; - } - } - } } - // If it ran through the list without finding anything it should not attempt any path remapping. - if (!foundspeciallump) name0 = ""; dirptr = (char*)directory; AllocateEntries(NumLumps); @@ -280,13 +232,6 @@ bool FZipFile::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf) return false; } - if (name.find("__macosx") == 0 || name.find("__MACOSX") == 0) - { - skipped++; - continue; // Weed out Apple's resource fork garbage right here because it interferes with safe operation. - } - if (!name0.empty()) name = std::string(name, name0.length()); - // skip Directories if (name.empty() || (name.back() == '/' && LittleLong(zip_fh->UncompressedSize32) == 0)) { @@ -317,9 +262,6 @@ bool FZipFile::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf) continue; } - FixPathSeparator(&name.front()); - for (auto& c : name) c = tolower(c); - uint32_t UncompressedSize =LittleLong(zip_fh->UncompressedSize32); uint32_t CompressedSize = LittleLong(zip_fh->CompressedSize32); uint64_t LocalHeaderOffset = LittleLong(zip_fh->LocalHeaderOffset32); @@ -358,7 +300,7 @@ bool FZipFile::Open(LumpFilterInfo* filter, FileSystemMessageFunc Printf) if (Entry->Method != METHOD_STORED) Entry->Flags |= RESFF_COMPRESSED; if (Entry->Method == METHOD_IMPLODE) { - // merge the flags into the compression method to tag less data around. + // for Implode merge the flags into the compression method to make handling in the file system easier and save one variable. if ((zip_fh->Flags & 6) == 2) Entry->Method = METHOD_IMPLODE_2; else if ((zip_fh->Flags & 6) == 4) Entry->Method = METHOD_IMPLODE_4; else if ((zip_fh->Flags & 6) == 6) Entry->Method = METHOD_IMPLODE_6; diff --git a/src/common/filesystem/source/resourcefile.cpp b/src/common/filesystem/source/resourcefile.cpp index 1933c59aae..5590b7a26a 100644 --- a/src/common/filesystem/source/resourcefile.cpp +++ b/src/common/filesystem/source/resourcefile.cpp @@ -42,6 +42,7 @@ #include "unicode.h" #include "fs_findfile.h" #include "fs_decompress.h" +#include "wildcards.hpp" namespace FileSys { @@ -145,11 +146,13 @@ FResourceFile *CheckPak(const char *filename, FileReader &file, LumpFilterInfo* FResourceFile *CheckZip(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); FResourceFile *Check7Z(const char *filename, FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); FResourceFile* CheckSSI(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); +FResourceFile* CheckHog(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); +FResourceFile* CheckMvl(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); FResourceFile* CheckWHRes(const char* filename, FileReader& file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); FResourceFile *CheckLump(const char *filename,FileReader &file, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); FResourceFile *CheckDir(const char *filename, bool nosub, LumpFilterInfo* filter, FileSystemMessageFunc Printf, StringPool* sp); -static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckSSI, CheckWHRes, CheckLump }; +static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckSSI, CheckHog, CheckMvl, CheckWHRes, CheckLump }; static int nulPrintf(FSMessageLevel msg, const char* fmt, ...) { @@ -350,10 +353,37 @@ void FResourceFile::GenerateHash() void FResourceFile::PostProcessArchive(LumpFilterInfo *filter) { // only do this for archive types which contain full file names. All others are assumed to be pre-sorted. - if (NumLumps < 2 || !(Entries[0].Flags & RESFF_FULLPATH)) return; + if (NumLumps == 0 || !(Entries[0].Flags & RESFF_FULLPATH)) return; + + // First eliminate all unwanted files + for (uint32_t i = 0; i < NumLumps; i++) + { + std::string name = Entries[i].FileName; + // remove Apple garbage unconditionally. + if (name.find("__macosx") == 0 || name.find("/__macosx") != std::string::npos) + { + Entries[i].FileName = ""; + continue; + } + // Skip executables as well. + if (wildcards::match(name, "*.bat") || wildcards::match(name, "*.exe")) + { + Entries[i].FileName = ""; + continue; + } + if (filter) for (auto& pattern : filter->blockednames) + { + if (wildcards::match(name, pattern)) + { + Entries[i].FileName = ""; + continue; + } + } + } // Entries in archives are sorted alphabetically. qsort(Entries, NumLumps, sizeof(Entries[0]), entrycmp); + FindCommonFolder(filter); if (!filter) return; // Filter out lumps using the same names as the Autoload.* sections @@ -377,6 +407,80 @@ void FResourceFile::PostProcessArchive(LumpFilterInfo *filter) JunkLeftoverFilters(max); } +//========================================================================== +// +// FResourceFile :: FindCommonFolder +// +// Checks if all content is in a common folder that can be stripped out. +// +//========================================================================== + +void FResourceFile::FindCommonFolder(LumpFilterInfo* filter) +{ + std::string name0, name1; + bool foundspeciallump = false; + bool foundprefix = false; + + // try to find a path prefix. + for (uint32_t i = 0; i < NumLumps; i++) + { + if (*Entries[i].FileName == 0) continue; + std::string name = Entries[i].FileName; + + // first eliminate files we do not want to have. + // Some, like MacOS resource forks and executables are eliminated unconditionally, but the calling code can alsp pass a list of invalid content. + if (name.find("filter/") == 0) + return; // 'filter' is a reserved name of the file system. If this appears in the root we got no common folder, and 'filter' cannot be it. + + if (!foundprefix) + { + // check for special names, if one of these gets found this must be treated as a normal zip. + bool isspecial = name.find("/") == std::string::npos || + (filter && std::find(filter->reservedFolders.begin(), filter->reservedFolders.end(), name) != filter->reservedFolders.end()); + if (isspecial) break; + name0 = std::string(name, 0, name.rfind("/") + 1); + name1 = std::string(name, 0, name.find("/") + 1); + foundprefix = true; + } + + if (name.find(name0) != 0) + { + if (!name1.empty()) + { + name0 = name1; + if (name.find(name0) != 0) + { + name0 = ""; + } + } + if (name0.empty()) + break; + } + if (!foundspeciallump && filter) + { + // at least one of the more common definition lumps must be present. + for (auto& p : filter->requiredPrefixes) + { + if (name.find(name0 + p) == 0 || name.rfind(p) == size_t(name.length() - p.length())) + { + foundspeciallump = true; + break; + } + } + } + } + // If it ran through the list without finding anything it should not attempt any path remapping. + if (!foundspeciallump || name0.empty()) return; + + size_t pathlen = name0.length(); + for (uint32_t i = 0; i < NumLumps; i++) + { + if (Entries[i].FileName[0] == 0) continue; + Entries[i].FileName += pathlen; + + } +} + //========================================================================== // // FResourceFile :: FilterLumps @@ -537,13 +641,16 @@ bool FResourceFile::FindPrefixRange(const char* filter, uint32_t maxlump, uint32 int FResourceFile::FindEntry(const char *name) { + auto norm_fn = tolower_normalize(name); for (unsigned i = 0; i < NumLumps; i++) { - if (!stricmp(name, getName(i))) + if (!strcmp(norm_fn, getName(i))) { + free(norm_fn); return i; } } + free(norm_fn); return -1; } diff --git a/src/common/filesystem/source/wildcards.hpp b/src/common/filesystem/source/wildcards.hpp new file mode 100644 index 0000000000..9b8e4eac6b --- /dev/null +++ b/src/common/filesystem/source/wildcards.hpp @@ -0,0 +1,1833 @@ +// THIS FILE HAS BEEN GENERATED AUTOMATICALLY. DO NOT EDIT DIRECTLY. +// Generated: 2019-03-08 09:59:35.958950200 +// Copyright Tomas Zeman 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef WILDCARDS_HPP +#define WILDCARDS_HPP +#define WILDCARDS_VERSION_MAJOR 1 +#define WILDCARDS_VERSION_MINOR 5 +#define WILDCARDS_VERSION_PATCH 0 +#ifndef WILDCARDS_CARDS_HPP +#define WILDCARDS_CARDS_HPP +#include +namespace wildcards +{ +template +struct cards +{ +constexpr cards(T a, T s, T e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(T a, T s, T e, T so, T sc, T sn, T ao, T ac, T ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +T anything; +T single; +T escape; +bool set_enabled; +T set_open; +T set_close; +T set_not; +bool alt_enabled; +T alt_open; +T alt_close; +T alt_or; +}; +enum class cards_type +{ +standard, +extended +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char a, char s, char e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char a, char s, char e, char so, char sc, char sn, char ao, char ac, char ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char anything{'*'}; +char single{'?'}; +char escape{'\\'}; +bool set_enabled{true}; +char set_open{'['}; +char set_close{']'}; +char set_not{'!'}; +bool alt_enabled{true}; +char alt_open{'('}; +char alt_close{')'}; +char alt_or{'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char16_t a, char16_t s, char16_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char16_t a, char16_t s, char16_t e, char16_t so, char16_t sc, char16_t sn, +char16_t ao, char16_t ac, char16_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char16_t anything{u'*'}; +char16_t single{u'?'}; +char16_t escape{u'\\'}; +bool set_enabled{true}; +char16_t set_open{u'['}; +char16_t set_close{u']'}; +char16_t set_not{u'!'}; +bool alt_enabled{true}; +char16_t alt_open{u'('}; +char16_t alt_close{u')'}; +char16_t alt_or{u'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(char32_t a, char32_t s, char32_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(char32_t a, char32_t s, char32_t e, char32_t so, char32_t sc, char32_t sn, +char32_t ao, char32_t ac, char32_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +char32_t anything{U'*'}; +char32_t single{U'?'}; +char32_t escape{U'\\'}; +bool set_enabled{true}; +char32_t set_open{U'['}; +char32_t set_close{U']'}; +char32_t set_not{U'!'}; +bool alt_enabled{true}; +char32_t alt_open{U'('}; +char32_t alt_close{U')'}; +char32_t alt_or{U'|'}; +}; +template <> +struct cards +{ +constexpr cards(cards_type type = cards_type::extended) +: set_enabled{type == cards_type::extended}, alt_enabled{type == cards_type::extended} +{ +} +constexpr cards(wchar_t a, wchar_t s, wchar_t e) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{false}, +alt_enabled{false} +{ +} +constexpr cards(wchar_t a, wchar_t s, wchar_t e, wchar_t so, wchar_t sc, wchar_t sn, wchar_t ao, +wchar_t ac, wchar_t ar) +: anything{std::move(a)}, +single{std::move(s)}, +escape{std::move(e)}, +set_enabled{true}, +set_open{std::move(so)}, +set_close{std::move(sc)}, +set_not{std::move(sn)}, +alt_enabled{true}, +alt_open{std::move(ao)}, +alt_close{std::move(ac)}, +alt_or{std::move(ar)} +{ +} +wchar_t anything{L'*'}; +wchar_t single{L'?'}; +wchar_t escape{L'\\'}; +bool set_enabled{true}; +wchar_t set_open{L'['}; +wchar_t set_close{L']'}; +wchar_t set_not{L'!'}; +bool alt_enabled{true}; +wchar_t alt_open{L'('}; +wchar_t alt_close{L')'}; +wchar_t alt_or{L'|'}; +}; +template +constexpr cards make_cards(T&& a, T&& s, T&& e) +{ +return {std::forward(a), std::forward(s), std::forward(e)}; +} +template +constexpr cards make_cards(T&& a, T&& s, T&& e, T&& so, T&& sc, T&& sn, T&& ao, T&& ac, T&& ar) +{ +return {std::forward(a), std::forward(s), std::forward(e), +std::forward(so), std::forward(sc), std::forward(sn), +std::forward(ao), std::forward(ac), std::forward(ar)}; +} +} +#endif +#ifndef WILDCARDS_MATCH_HPP +#define WILDCARDS_MATCH_HPP +#include +#include +#include +#ifndef CONFIG_HPP +#define CONFIG_HPP +#ifndef QUICKCPPLIB_HAS_FEATURE_H +#define QUICKCPPLIB_HAS_FEATURE_H +#if __cplusplus >= 201103L +#if !defined(__cpp_alias_templates) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) +#if __cplusplus >= 201402L +#define __cpp_constexpr 201304 +#else +#define __cpp_constexpr 190000 +#endif +#endif +#if !defined(__cpp_decltype) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) +#define __cpp_variadic_templates 190000 +#endif +#endif +#if __cplusplus >= 201402L +#if !defined(__cpp_aggregate_nsdmi) +#define __cpp_aggregate_nsdmi 190000 +#endif +#if !defined(__cpp_binary_literals) +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) +#define __cpp_variable_templates 190000 +#endif +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#if !defined(__cpp_exceptions) && defined(_CPPUNWIND) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(_CPPRTTI) +#define __cpp_rtti 190000 +#endif +#if !defined(__cpp_alias_templates) && _MSC_VER >= 1800 +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && _MSC_FULL_VER >= 190023506 +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && _MSC_VER >= 1600 +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && _MSC_VER >= 1800 +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && _MSC_VER >= 1800 +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && _MSC_VER >= 1900 +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && _MSC_VER >= 1900 +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && _MSC_VER >= 1600 +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && _MSC_VER >= 1900 +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && _MSC_VER >= 1700 +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && _MSC_VER >= 1800 +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && _MSC_VER >= 1900 +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && _MSC_VER >= 1600 +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && _MSC_VER >= 1600 +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_user_defined_literals) && _MSC_VER >= 1900 +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && _MSC_VER >= 1800 +#define __cpp_variadic_templates 190000 +#endif +#if !defined(__cpp_binary_literals) && _MSC_VER >= 1900 +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) && _MSC_VER >= 1900 +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) && _MSC_VER >= 1900 +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) && _MSC_VER >= 1900 +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) && _MSC_VER >= 1900 +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) && _MSC_VER >= 1900 +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) && _MSC_FULL_VER >= 190023506 +#define __cpp_variable_templates 190000 +#endif +#endif +#if(defined(__GNUC__) && !defined(__clang__)) +#define QUICKCPPLIB_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if !defined(__cpp_exceptions) && defined(__EXCEPTIONS) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(__GXX_RTTI) +#define __cpp_rtti 190000 +#endif +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_GCC >= 40801) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_GCC >= 40400) +#define __cpp_variadic_templates 190000 +#endif +#endif +#endif +#if defined(__clang__) +#define QUICKCPPLIB_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#if !defined(__cpp_exceptions) && (defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && (defined(__GXX_RTTI) || defined(_CPPRTTI)) +#define __cpp_rtti 190000 +#endif +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && defined(__cpp_raw_string_literals) +#define __cpp_raw_strings __cpp_raw_string_literals +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_rvalue_references) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) && defined(__cpp_user_literals) +#define __cpp_user_defined_literals __cpp_user_literals +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_variadic_templates 190000 +#endif +#endif +#endif +#endif +#define cfg_HAS_CONSTEXPR14 (__cpp_constexpr >= 201304) +#if cfg_HAS_CONSTEXPR14 +#define cfg_constexpr14 constexpr +#else +#define cfg_constexpr14 +#endif +#if cfg_HAS_CONSTEXPR14 && defined(__clang__) +#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 1 +#else +#define cfg_HAS_FULL_FEATURED_CONSTEXPR14 1 +#endif +#endif +#ifndef CX_FUNCTIONAL_HPP +#define CX_FUNCTIONAL_HPP +#include +namespace cx +{ +template +struct less +{ +constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs < rhs) +{ +return lhs < rhs; +} +}; +template <> +struct less +{ +template +constexpr auto operator()(T&& lhs, U&& rhs) const +-> decltype(std::forward(lhs) < std::forward(rhs)) +{ +return std::forward(lhs) < std::forward(rhs); +} +}; +template +struct equal_to +{ +constexpr auto operator()(const T& lhs, const T& rhs) const -> decltype(lhs == rhs) +{ +return lhs == rhs; +} +}; +template <> +struct equal_to +{ +template +constexpr auto operator()(T&& lhs, U&& rhs) const +-> decltype(std::forward(lhs) == std::forward(rhs)) +{ +return std::forward(lhs) == std::forward(rhs); +} +}; +} +#endif +#ifndef CX_ITERATOR_HPP +#define CX_ITERATOR_HPP +#include +#include +namespace cx +{ +template +constexpr It next(It it) +{ +return it + 1; +} +template +constexpr It prev(It it) +{ +return it - 1; +} +template +constexpr auto size(const C& c) -> decltype(c.size()) +{ +return c.size(); +} +template +constexpr std::size_t size(const T (&)[N]) +{ +return N; +} +template +constexpr auto empty(const C& c) -> decltype(c.empty()) +{ +return c.empty(); +} +template +constexpr bool empty(const T (&)[N]) +{ +return false; +} +template +constexpr bool empty(std::initializer_list il) +{ +return il.size() == 0; +} +template +constexpr auto begin(const C& c) -> decltype(c.begin()) +{ +return c.begin(); +} +template +constexpr auto begin(C& c) -> decltype(c.begin()) +{ +return c.begin(); +} +template +constexpr T* begin(T (&array)[N]) +{ +return &array[0]; +} +template +constexpr const E* begin(std::initializer_list il) +{ +return il.begin(); +} +template +constexpr auto cbegin(const C& c) -> decltype(cx::begin(c)) +{ +return cx::begin(c); +} +template +constexpr auto end(const C& c) -> decltype(c.end()) +{ +return c.end(); +} +template +constexpr auto end(C& c) -> decltype(c.end()) +{ +return c.end(); +} +template +constexpr T* end(T (&array)[N]) +{ +return &array[N]; +} +template +constexpr const E* end(std::initializer_list il) +{ +return il.end(); +} +template +constexpr auto cend(const C& c) -> decltype(cx::end(c)) +{ +return cx::end(c); +} +} +#endif +#ifndef WILDCARDS_UTILITY_HPP +#define WILDCARDS_UTILITY_HPP +#include +#include +namespace wildcards +{ +template +struct const_iterator +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using const_iterator_t = typename const_iterator::type; +template +struct iterator +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using iterator_t = typename iterator::type; +template +struct iterated_item +{ +using type = typename std::remove_cv< +typename std::remove_reference())>::type>::type; +}; +template +using iterated_item_t = typename iterated_item::type; +template +struct container_item +{ +using type = typename std::remove_cv< +typename std::remove_reference()))>::type>::type; +}; +template +using container_item_t = typename container_item::type; +} +#endif +namespace wildcards +{ +template +struct full_match_result +{ +bool res; +SequenceIterator s, send, s1; +PatternIterator p, pend, p1; +constexpr operator bool() const +{ +return res; +} +}; +namespace detail +{ +template +struct match_result +{ +bool res; +SequenceIterator s; +PatternIterator p; +constexpr operator bool() const +{ +return res; +} +}; +template +constexpr match_result make_match_result(bool res, +SequenceIterator s, +PatternIterator p) +{ +return {std::move(res), std::move(s), std::move(p)}; +} +template +constexpr full_match_result make_full_match_result( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +match_result mr) +{ +return {std::move(mr.res), std::move(s), std::move(send), std::move(mr.s), +std::move(p), std::move(pend), std::move(mr.p)}; +} +#if !cfg_HAS_FULL_FEATURED_CONSTEXPR14 +constexpr bool throw_invalid_argument(const char* what_arg) +{ +return what_arg == nullptr ? false : throw std::invalid_argument(what_arg); +} +template +constexpr T throw_invalid_argument(T t, const char* what_arg) +{ +return what_arg == nullptr ? t : throw std::invalid_argument(what_arg); +} +constexpr bool throw_logic_error(const char* what_arg) +{ +return what_arg == nullptr ? false : throw std::logic_error(what_arg); +} +template +constexpr T throw_logic_error(T t, const char* what_arg) +{ +return what_arg == nullptr ? t : throw std::logic_error(what_arg); +} +#endif +enum class is_set_state +{ +open, +not_or_first, +first, +next +}; +template +constexpr bool is_set( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +is_set_state state = is_set_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +return false; +} +while (p != pend) +{ +switch (state) +{ +case is_set_state::open: +if (*p != c.set_open) +{ +return false; +} +state = is_set_state::not_or_first; +break; +case is_set_state::not_or_first: +if (*p == c.set_not) +{ +state = is_set_state::first; +} +else +{ +state = is_set_state::next; +} +break; +case is_set_state::first: +state = is_set_state::next; +break; +case is_set_state::next: +if (*p == c.set_close) +{ +return true; +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +"The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +return false; +#else +return c.set_enabled && p != pend && +(state == is_set_state::open +? *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first) +: +state == is_set_state::not_or_first +? *p == c.set_not ? is_set(cx::next(p), pend, c, is_set_state::first) +: is_set(cx::next(p), pend, c, is_set_state::next) +: state == is_set_state::first +? is_set(cx::next(p), pend, c, is_set_state::next) +: state == is_set_state::next +? *p == c.set_close || +is_set(cx::next(p), pend, c, is_set_state::next) +: throw std::logic_error("The program execution should never end up " +"here throwing this exception")); +#endif +} +enum class set_end_state +{ +open, +not_or_first, +first, +next +}; +template +constexpr PatternIterator set_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +set_end_state state = set_end_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of sets is disabled"); +#else +return throw_invalid_argument(p, "The use of sets is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case set_end_state::open: +if (*p != c.set_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid set"); +#endif +} +state = set_end_state::not_or_first; +break; +case set_end_state::not_or_first: +if (*p == c.set_not) +{ +state = set_end_state::first; +} +else +{ +state = set_end_state::next; +} +break; +case set_end_state::first: +state = set_end_state::next; +break; +case set_end_state::next: +if (*p == c.set_close) +{ +return cx::next(p); +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid set"); +#endif +#else +return !c.set_enabled +? throw std::invalid_argument("The use of sets is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid set") +: +state == set_end_state::open +? *p == c.set_open +? set_end(cx::next(p), pend, c, set_end_state::not_or_first) +: throw std::invalid_argument("The given pattern is not a valid set") +: +state == set_end_state::not_or_first +? *p == c.set_not ? set_end(cx::next(p), pend, c, set_end_state::first) +: set_end(cx::next(p), pend, c, set_end_state::next) +: state == set_end_state::first +? set_end(cx::next(p), pend, c, set_end_state::next) +: state == set_end_state::next +? *p == c.set_close +? cx::next(p) +: set_end(cx::next(p), pend, c, set_end_state::next) +: throw std::logic_error( +"The program execution should never end up " +"here throwing this exception"); +#endif +} +enum class match_set_state +{ +open, +not_or_first_in, +first_out, +next_in, +next_out +}; +template > +constexpr match_result match_set( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), match_set_state state = match_set_state::open) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.set_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of sets is disabled"); +#else +return throw_invalid_argument(make_match_result(false, s, p), "The use of sets is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case match_set_state::open: +if (*p != c.set_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(make_match_result(false, s, p), +"The given pattern is not a valid set"); +#endif +} +state = match_set_state::not_or_first_in; +break; +case match_set_state::not_or_first_in: +if (*p == c.set_not) +{ +state = match_set_state::first_out; +} +else +{ +if (s == send) +{ +return make_match_result(false, s, p); +} +if (equal_to(*s, *p)) +{ +return make_match_result(true, s, p); +} +state = match_set_state::next_in; +} +break; +case match_set_state::first_out: +if (s == send || equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +state = match_set_state::next_out; +break; +case match_set_state::next_in: +if (*p == c.set_close || s == send) +{ +return make_match_result(false, s, p); +} +if (equal_to(*s, *p)) +{ +return make_match_result(true, s, p); +} +break; +case match_set_state::next_out: +if (*p == c.set_close) +{ +return make_match_result(true, s, p); +} +if (s == send || equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +make_match_result(false, s, p), +"The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid set"); +#else +return throw_invalid_argument(make_match_result(false, s, p), +"The given pattern is not a valid set"); +#endif +#else +return !c.set_enabled +? throw std::invalid_argument("The use of sets is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid set") +: state == match_set_state::open +? *p == c.set_open +? match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::not_or_first_in) +: +throw std::invalid_argument("The given pattern is not a valid set") +: +state == match_set_state::not_or_first_in +? *p == c.set_not +? match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::first_out) +: +s == send ? make_match_result(false, s, p) +: equal_to(*s, *p) +? make_match_result(true, s, p) +: match_set(s, send, cx::next(p), pend, c, +equal_to, match_set_state::next_in) +: +state == match_set_state::first_out +? s == send || equal_to(*s, *p) +? make_match_result(false, s, p) +: match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::next_out) +: +state == match_set_state::next_in +? *p == c.set_close || s == send +? make_match_result(false, s, p) +: equal_to(*s, *p) ? make_match_result(true, s, p) +: match_set(s, send, cx::next(p), +pend, c, equal_to, state) +: +state == match_set_state::next_out +? *p == c.set_close +? make_match_result(true, s, p) +: s == send || equal_to(*s, *p) +? make_match_result(false, s, p) +: match_set(s, send, cx::next(p), pend, c, +equal_to, state) +: throw std::logic_error( +"The program execution should never end up " +"here " +"throwing this exception"); +#endif +} +enum class is_alt_state +{ +open, +next, +escape +}; +template +constexpr bool is_alt( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +is_alt_state state = is_alt_state::open, int depth = 0) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +return false; +} +while (p != pend) +{ +switch (state) +{ +case is_alt_state::open: +if (*p != c.alt_open) +{ +return false; +} +state = is_alt_state::next; +++depth; +break; +case is_alt_state::next: +if (*p == c.escape) +{ +state = is_alt_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return true; +} +} +break; +case is_alt_state::escape: +state = is_alt_state::next; +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +return false; +#else +return c.alt_enabled && p != pend && +(state == is_alt_state::open +? *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, depth + 1) +: state == is_alt_state::next +? *p == c.escape +? is_alt(cx::next(p), pend, c, is_alt_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first) +? is_alt(set_end(cx::next(p), pend, c, set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? is_alt(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 || +is_alt(cx::next(p), pend, c, state, depth - 1) +: is_alt(cx::next(p), pend, c, state, depth) +: +state == is_alt_state::escape +? is_alt(cx::next(p), pend, c, is_alt_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing this " +"exception")); +#endif +} +enum class alt_end_state +{ +open, +next, +escape +}; +template +constexpr PatternIterator alt_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +alt_end_state state = alt_end_state::open, int depth = 0) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of alternatives is disabled"); +#else +return throw_invalid_argument(p, "The use of alternatives is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case alt_end_state::open: +if (*p != c.alt_open) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +} +state = alt_end_state::next; +++depth; +break; +case alt_end_state::next: +if (*p == c.escape) +{ +state = alt_end_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return cx::next(p); +} +} +break; +case alt_end_state::escape: +state = alt_end_state::next; +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +#else +return !c.alt_enabled +? throw std::invalid_argument("The use of alternatives is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid alternative") +: state == alt_end_state::open +? *p == c.alt_open +? alt_end(cx::next(p), pend, c, alt_end_state::next, depth + 1) +: throw std::invalid_argument( +"The given pattern is not a valid alternative") +: state == alt_end_state::next +? *p == c.escape +? alt_end(cx::next(p), pend, c, alt_end_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, +is_set_state::not_or_first) +? alt_end(set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? alt_end(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 ? cx::next(p) +: alt_end(cx::next(p), pend, c, +state, depth - 1) +: alt_end(cx::next(p), pend, c, state, depth) +: +state == alt_end_state::escape +? alt_end(cx::next(p), pend, c, alt_end_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing " +"this " +"exception"); +#endif +} +enum class alt_sub_end_state +{ +next, +escape +}; +template +constexpr PatternIterator alt_sub_end( +PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +alt_sub_end_state state = alt_sub_end_state::next, int depth = 1) +{ +#if cfg_HAS_CONSTEXPR14 +if (!c.alt_enabled) +{ +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The use of alternatives is disabled"); +#else +return throw_invalid_argument(p, "The use of alternatives is disabled"); +#endif +} +while (p != pend) +{ +switch (state) +{ +case alt_sub_end_state::next: +if (*p == c.escape) +{ +state = alt_sub_end_state::escape; +} +else if (c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +p = cx::prev(set_end(cx::next(p), pend, c, set_end_state::not_or_first)); +} +else if (*p == c.alt_open) +{ +++depth; +} +else if (*p == c.alt_close) +{ +--depth; +if (depth == 0) +{ +return p; +} +} +else if (*p == c.alt_or) +{ +if (depth == 1) +{ +return p; +} +} +break; +case alt_sub_end_state::escape: +state = alt_sub_end_state::next; +break; +default: +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::logic_error( +"The program execution should never end up here throwing this exception"); +#else +return throw_logic_error( +p, "The program execution should never end up here throwing this exception"); +#endif +} +p = cx::next(p); +} +#if cfg_HAS_FULL_FEATURED_CONSTEXPR14 +throw std::invalid_argument("The given pattern is not a valid alternative"); +#else +return throw_invalid_argument(p, "The given pattern is not a valid alternative"); +#endif +#else +return !c.alt_enabled +? throw std::invalid_argument("The use of alternatives is disabled") +: p == pend +? throw std::invalid_argument("The given pattern is not a valid alternative") +: state == alt_sub_end_state::next +? *p == c.escape +? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::escape, depth) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, is_set_state::not_or_first) +? alt_sub_end(set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, state, depth) +: *p == c.alt_open +? alt_sub_end(cx::next(p), pend, c, state, depth + 1) +: *p == c.alt_close +? depth == 1 ? p : alt_sub_end(cx::next(p), pend, +c, state, depth - 1) +: *p == c.alt_or +? depth == 1 ? p +: alt_sub_end(cx::next(p), pend, +c, state, depth) +: alt_sub_end(cx::next(p), pend, c, state, +depth) +: +state == alt_sub_end_state::escape +? alt_sub_end(cx::next(p), pend, c, alt_sub_end_state::next, depth) +: throw std::logic_error( +"The program execution should never end up here throwing " +"this " +"exception"); +#endif +} +template > +constexpr match_result match( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), bool partial = false, bool escape = false); +template > +constexpr match_result match_alt( +SequenceIterator s, SequenceIterator send, PatternIterator p1, PatternIterator p1end, +PatternIterator p2, PatternIterator p2end, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo(), bool partial = false) +{ +#if cfg_HAS_CONSTEXPR14 +auto result1 = match(s, send, p1, p1end, c, equal_to, true); +if (result1) +{ +auto result2 = match(result1.s, send, p2, p2end, c, equal_to, partial); +if (result2) +{ +return result2; +} +} +p1 = cx::next(p1end); +if (p1 == p2) +{ +return make_match_result(false, s, p1end); +} +return match_alt(s, send, p1, alt_sub_end(p1, p2, c), p2, p2end, c, equal_to, partial); +#else +return match(s, send, p1, p1end, c, equal_to, true) && +match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, +partial) +? match(match(s, send, p1, p1end, c, equal_to, true).s, send, p2, p2end, c, equal_to, +partial) +: cx::next(p1end) == p2 +? make_match_result(false, s, p1end) +: match_alt(s, send, cx::next(p1end), alt_sub_end(cx::next(p1end), p2, c), p2, +p2end, c, equal_to, partial); +#endif +} +template +constexpr match_result match( +SequenceIterator s, SequenceIterator send, PatternIterator p, PatternIterator pend, +const cards>& c, const EqualTo& equal_to, bool partial, +bool escape) +{ +#if cfg_HAS_CONSTEXPR14 +if (p == pend) +{ +return make_match_result(partial || s == send, s, p); +} +if (escape) +{ +if (s == send || !equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +} +if (*p == c.anything) +{ +auto result = match(s, send, cx::next(p), pend, c, equal_to, partial); +if (result) +{ +return result; +} +if (s == send) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, p, pend, c, equal_to, partial); +} +if (*p == c.single) +{ +if (s == send) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +} +if (*p == c.escape) +{ +return match(s, send, cx::next(p), pend, c, equal_to, partial, true); +} +if (c.set_enabled && *p == c.set_open && is_set(cx::next(p), pend, c, is_set_state::not_or_first)) +{ +auto result = +match_set(s, send, cx::next(p), pend, c, equal_to, match_set_state::not_or_first_in); +if (!result) +{ +return result; +} +return match(cx::next(s), send, set_end(cx::next(p), pend, c, set_end_state::not_or_first), +pend, c, equal_to, partial); +} +if (c.alt_enabled && *p == c.alt_open && is_alt(cx::next(p), pend, c, is_alt_state::next, 1)) +{ +auto p_alt_end = alt_end(cx::next(p), pend, c, alt_end_state::next, 1); +return match_alt(s, send, cx::next(p), alt_sub_end(cx::next(p), p_alt_end, c), p_alt_end, pend, +c, equal_to, partial); +} +if (s == send || !equal_to(*s, *p)) +{ +return make_match_result(false, s, p); +} +return match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial); +#else +return p == pend +? make_match_result(partial || s == send, s, p) +: escape +? s == send || !equal_to(*s, *p) +? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, c, equal_to, partial) +: *p == c.anything +? match(s, send, cx::next(p), pend, c, equal_to, partial) +? match(s, send, cx::next(p), pend, c, equal_to, partial) +: s == send ? make_match_result(false, s, p) +: match(cx::next(s), send, p, pend, c, equal_to, partial) +: *p == c.single +? s == send ? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, c, +equal_to, partial) +: *p == c.escape +? match(s, send, cx::next(p), pend, c, equal_to, partial, true) +: c.set_enabled && *p == c.set_open && +is_set(cx::next(p), pend, c, +is_set_state::not_or_first) +? !match_set(s, send, cx::next(p), pend, c, equal_to, +match_set_state::not_or_first_in) +? match_set(s, send, cx::next(p), pend, c, +equal_to, +match_set_state::not_or_first_in) +: match(cx::next(s), send, +set_end(cx::next(p), pend, c, +set_end_state::not_or_first), +pend, c, equal_to, partial) +: c.alt_enabled && *p == c.alt_open && +is_alt(cx::next(p), pend, c, +is_alt_state::next, 1) +? match_alt( +s, send, cx::next(p), +alt_sub_end(cx::next(p), +alt_end(cx::next(p), pend, c, +alt_end_state::next, 1), +c), +alt_end(cx::next(p), pend, c, +alt_end_state::next, 1), +pend, c, equal_to, partial) +: s == send || !equal_to(*s, *p) +? make_match_result(false, s, p) +: match(cx::next(s), send, cx::next(p), pend, +c, equal_to, partial); +#endif +} +} +template > +constexpr full_match_result, const_iterator_t> match( +Sequence&& sequence, Pattern&& pattern, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo()) +{ +return detail::make_full_match_result( +cx::cbegin(sequence), cx::cend(sequence), cx::cbegin(pattern), cx::cend(pattern), +detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), +cx::cbegin(pattern), cx::cend(std::forward(pattern)), c, equal_to)); +} +template , +typename = typename std::enable_if::value>::type> +constexpr full_match_result, const_iterator_t> match( +Sequence&& sequence, Pattern&& pattern, const EqualTo& equal_to) +{ +return match(std::forward(sequence), std::forward(pattern), +cards>(), equal_to); +} +} +#endif +#ifndef WILDCARDS_MATCHER_HPP +#define WILDCARDS_MATCHER_HPP +#include +#include +#include +#ifndef CX_STRING_VIEW_HPP +#define CX_STRING_VIEW_HPP +#include +#include +#ifndef CX_ALGORITHM_HPP +#define CX_ALGORITHM_HPP +namespace cx +{ +template +constexpr bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) +{ +#if cfg_HAS_CONSTEXPR14 +while (first1 != last1 && first2 != last2 && *first1 == *first2) +{ +++first1, ++first2; +} +return first1 == last1 && first2 == last2; +#else +return first1 != last1 && first2 != last2 && *first1 == *first2 +? equal(first1 + 1, last1, first2 + 1, last2) +: first1 == last1 && first2 == last2; +#endif +} +} +#endif +namespace cx +{ +template +class basic_string_view +{ +public: +using value_type = T; +constexpr basic_string_view() = default; +template +constexpr basic_string_view(const T (&str)[N]) : data_{&str[0]}, size_{N - 1} +{ +} +constexpr basic_string_view(const T* str, std::size_t s) : data_{str}, size_{s} +{ +} +constexpr const T* data() const +{ +return data_; +} +constexpr std::size_t size() const +{ +return size_; +} +constexpr bool empty() const +{ +return size() == 0; +} +constexpr const T* begin() const +{ +return data_; +} +constexpr const T* cbegin() const +{ +return begin(); +} +constexpr const T* end() const +{ +return data_ + size_; +} +constexpr const T* cend() const +{ +return end(); +} +private: +const T* data_{nullptr}; +std::size_t size_{0}; +}; +template +constexpr bool operator==(const basic_string_view& lhs, const basic_string_view& rhs) +{ +return equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} +template +constexpr bool operator!=(const basic_string_view& lhs, const basic_string_view& rhs) +{ +return !(lhs == rhs); +} +template +std::basic_ostream& operator<<(std::basic_ostream& o, const basic_string_view& s) +{ +o << s.data(); +return o; +} +template +constexpr basic_string_view make_string_view(const T (&str)[N]) +{ +return {str, N - 1}; +} +template +constexpr basic_string_view make_string_view(const T* str, std::size_t s) +{ +return {str, s}; +} +using string_view = basic_string_view; +using u16string_view = basic_string_view; +using u32string_view = basic_string_view; +using wstring_view = basic_string_view; +namespace literals +{ +constexpr string_view operator"" _sv(const char* str, std::size_t s) +{ +return {str, s}; +} +constexpr u16string_view operator"" _sv(const char16_t* str, std::size_t s) +{ +return {str, s}; +} +constexpr u32string_view operator"" _sv(const char32_t* str, std::size_t s) +{ +return {str, s}; +} +constexpr wstring_view operator"" _sv(const wchar_t* str, std::size_t s) +{ +return {str, s}; +} +} +} +#endif +namespace wildcards +{ +template > +class matcher +{ +public: +constexpr explicit matcher(Pattern&& pattern, const cards>& c = +cards>(), +const EqualTo& equal_to = EqualTo()) +: p_{cx::cbegin(pattern)}, +pend_{cx::cend(std::forward(pattern))}, +c_{c}, +equal_to_{equal_to} +{ +} +constexpr matcher(Pattern&& pattern, const EqualTo& equal_to) +: p_{cx::cbegin(pattern)}, +pend_{cx::cend(std::forward(pattern))}, +c_{cards>()}, +equal_to_{equal_to} +{ +} +template +constexpr full_match_result, const_iterator_t> matches( +Sequence&& sequence) const +{ +return detail::make_full_match_result( +cx::cbegin(sequence), cx::cend(sequence), p_, pend_, +detail::match(cx::cbegin(sequence), cx::cend(std::forward(sequence)), p_, pend_, +c_, equal_to_)); +} +private: +const_iterator_t p_; +const_iterator_t pend_; +cards> c_; +EqualTo equal_to_; +}; +template > +constexpr matcher make_matcher( +Pattern&& pattern, +const cards>& c = cards>(), +const EqualTo& equal_to = EqualTo()) +{ +return matcher{std::forward(pattern), c, equal_to}; +} +template , +typename = typename std::enable_if::value>::type> +constexpr matcher make_matcher(Pattern&& pattern, const EqualTo& equal_to) +{ +return make_matcher(std::forward(pattern), cards>(), equal_to); +} +namespace literals +{ +constexpr auto operator"" _wc(const char* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const char16_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const char32_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +constexpr auto operator"" _wc(const wchar_t* str, std::size_t s) +-> decltype(make_matcher(cx::make_string_view(str, s + 1))) +{ +return make_matcher(cx::make_string_view(str, s + 1)); +} +} +} +#endif +#endif