From 3cefb5931086d9eb58c2cbd686a10ab4416f17ef Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 13 Oct 2023 04:24:32 -0700 Subject: [PATCH] starting a flags parser --- Flag.cc | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Flag.h | 111 ++++++++++++++++++++++++++++++++ Makefile | 2 +- rcpp.cc | 28 ++++++-- 4 files changed, 327 insertions(+), 5 deletions(-) create mode 100644 Flag.cc create mode 100644 Flag.h diff --git a/Flag.cc b/Flag.cc new file mode 100644 index 0000000..96c8499 --- /dev/null +++ b/Flag.cc @@ -0,0 +1,191 @@ +/// +/// \file Flag.h +/// \author kyle +/// \created 2023-10-12 +/// \brief Flag defines a command-line flag parser. +/// +/// \section COPYRIGHT +/// +/// Copyright 2023 K. Isom +/// +/// Permission to use, copy, modify, and/or distribute this software for +/// any purpose with or without fee is hereby granted, provided that +/// the above copyright notice and this permission notice appear in all /// copies. +/// +/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +/// PERFORMANCE OF THIS SOFTWARE. +/// + + +#include +#include +#include +#include +#include + +#include "Flag.h" + + +namespace klib { + + +static const std::regex isFlag("^-[a-zA-Z0-9]+$", + std::regex_constants::nosubs); + + +Flag::Flag(FlagType fType, std::string fName, std::string fDescription) + : Type(fType), WasSet(false), Name(fName), Description(fDescription) +{ +} + + +Flags::Flags(std::string fName) + : name(fName), description("") +{ +} + + +Flags::Flags(std::string fName, std::string fDescription) + : name(fName), description(fDescription) +{ +} + + +bool +Flags::Register(std::string fName, FlagType fType, std::string fDescription) +{ + if (!std::regex_search(fName, isFlag)) { + return false; + } + + if (this->flags.count(fName) != 0) { + return false; + } + + auto flag = new Flag; + flag->Type = fType; + flag->WasSet = false; + flag->name = name; + this->desription = fDescription; + this->flags[fName] = flag; + return true; +} + + +Flag * +Flags::Lookup(std::string fName) +{ + if (this->flags.count(fName) == 0) { + return nullptr; + } + + +} + + +OptFlagValue +Flags::ValueOf(std::string fName) +{ + if (this->flags.count(fName)) { + return std::nullopt; + } + + return OptFlagValue(this->flags[fName]->Value); +} + + + +ParseStatus +Flags::parseArg(int argc, char **argv, int &index) +{ + std::string arg(argv[index]); + + index++; + if (!std::regex_search(arg, isFlag)) { + return ParseStatus::EndOfFlags; + } + + if (this->flags.count(arg) == 0) { + return ParseStatus::NotRegistered; + } + + auto flag = flags[arg]; + if (flag->Type == FlagType::Boolean) { + flag->WasSet = true; + flag->Value.b = true; + return ParseStatus::OK; + } + + switch (flag->Type) { + default: + throw std::runtime_error("not handled"); + } + + return ParseStatus::OK; +} + + +ParseStatus +Flags::Parse(int argc, char **argv) +{ + int index = 1; + + while (index != argc) { + auto result = this->parseArg(argc, argv, index); + switch (result) { + case ParseStatus::OK: + continue; + + case ParseStatus::EndOfFlags: + while (index < argc) { + this->args.push_back(std::string(argv[index])); + index++; + } + continue; + case ParseStatus::EndOfFlags: + case ParseStatus::NotEnoughArgs: + // fall through // + return result; + default: + throw runtime_error("unhandled parse state"); + } + + } + + return ParseStatus::OK; +} + + +size_t +Flags::NumArgs() +{ + return this->args.size(); +} + + +std::vector +Flags::Args() +{ + return this->args; +} + + +bool +Flags::GetBool(std::string name, bool &flagValue) +{ + if (this->flags[name] == 0) { + return false; + } + + return std::get(this->flags[name]->Value); +} + + +} // namespace klib + diff --git a/Flag.h b/Flag.h new file mode 100644 index 0000000..78ff599 --- /dev/null +++ b/Flag.h @@ -0,0 +1,111 @@ +/// +/// \file Flag.h +/// \author kyle +/// \created 2023-10-12 +/// \brief Flag declares a command-line flag parser. +/// +/// \section COPYRIGHT +/// +/// Copyright 2023 K. Isom +/// +/// Permission to use, copy, modify, and/or distribute this software for +/// any purpose with or without fee is hereby granted, provided that +/// the above copyright notice and this permission notice appear in all /// copies. +/// +/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +/// PERFORMANCE OF THIS SOFTWARE. +/// + + +#include +#include +#include +#include +#include +#include + + +namespace klib { + + + +enum class FlagType : uint8_t { + Unknown = 0, + Boolean = 1, + Integer = 2, + UnsignedInteger = 3, + String = 4, +}; + + +enum class ParseStatus : uint8_t { + OK = 0, + EndOfFlags = 1, + NotRegistered = 2, + NotEnoughArgs = 3, +}; + + +typedef std::variant< + std::string *, + bool *, + int64_t *, + uint64_t *> FlagValue; + + +struct Flag { + Flag(FlagType fType, std::string fName, std::string fDescription); + + FlagType Type; + bool WasSet; + std::string Name; + std::string Description; + FlagValue Value; +}; +typedef std::optional OptFlagValue; + + +class Flags { +public: + Flags(std::string fName); + Flags(std::string fName, std::string fDescription); + + bool Register(std::string fName, + FlagType fType, + std::string fDescription); + Flag *Lookup(std::string fName); + OptFlagValue + ValueOf(std::string fName); + + int Parse(int argc, char **argv); + + void Usage(std::ostream &os, int exitCode); + + size_t NumArgs(); + std::vector Args(); + std::string Arg(int index); + + bool GetBool(std::string name, bool &flagValue); + // bool GetUnsignedInteger(std::string name, uint64_t &flagValue) + // bool GetInteger(std::string name, int64_t &flagValue) + // bool GetString(std::string name, std::string &flagValue) + + +private: + ParseStatus parseArg(int argc, char **argv, int &index); + + std::string name; + std::string description; + std::vector args; + std::map flags; +}; + + + +} // namespace klib diff --git a/Makefile b/Makefile index 4c665bb..3e73d47 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ LDFLAGS := SOURCE_DIR ?= "/mnt/Kyle/Documents/Fonts/Equity + Concourse Standard + Triplicate" TARGET_DIR ?= "$(HOME)/.local/share/fonts" -SOURCES := rcpp.cc +SOURCES := rcpp.cc Flag.cc Flag.h OBJS := $(patsubst %.cc,%.o,$(filter %.cc,$(SOURCES))) TARGET := rcpp diff --git a/rcpp.cc b/rcpp.cc index 2c7e204..087a590 100644 --- a/rcpp.cc +++ b/rcpp.cc @@ -29,6 +29,9 @@ #include #include +#include "Flag.h" +using namespace klib; + #define ALIAS_FS namespace fs = std::filesystem typedef std::filesystem::path Path; @@ -147,13 +150,30 @@ main(int argc, char *argv[]) ALIAS_FS; auto regexOptions = std::regex_constants::optimize; - if (argc < 4) { - std::cerr << "Usage: rcpp pattern files... destination\n"; + Flags optArgs("rcpp", "recursively copy files by pattern"); + + optArgs.Register("-e", FlagType::Boolean, "pattern is a file extension"); + optArgs.Register("-i", FlagType::Boolean, "case-insensitive maches"); + if (optArgs.Parse(argc, argv)) { + optArgs.Usage(std::cerr, 1); } - auto pattern = std::string(argv[1]); - auto destDir = fs::absolute(fs::path(argv[argc-1])); + if (optArgs.NumArgs() < 3) { + optArgs.Usage(std::cerr, 1); + } + + auto pattern = optArgs.Arg(0); + auto destDir = fs::path(optArgs.Arg(optArgs.NumArgs() - 1)); bool value; + if (optArgs.GetBool("-e", value) && value) { + pattern = "\\." + pattern + "$"; + } + + if (optArgs.GetBool("-i", value) && value) { + regexOptions |= std::regex_constants::icase; + } + + destDir = fs::absolute(destDir); TRACE << "argc: " << argc << "\n";