diff --git a/Makefile b/Makefile index 25f730f..55b6a17 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ OBJS =\ base64\ hash/sha256\ -TOOLS =\ +TOOLS = ${LIB} TESTS =\ test-hex\ diff --git a/src/basen.cpp b/src/basen.cpp index 08ace15..e51308e 100644 --- a/src/basen.cpp +++ b/src/basen.cpp @@ -1,19 +1,83 @@ +#include #include +#include #include #include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +namespace basen +{ + typedef std::string (*encoder_t)(std::span); + typedef std::vector (*decoder_t)(std::string_view); + + const std::unordered_map encoders = { + {"base58", base58::encode}, + {"base58check", base58::encodeCheck}, + {"base64", base64::encode}, + {"hex", hex::encode}, + }; + const std::unordered_map decoders = { + {"base58", base58::decode}, + {"base58check", base58::decodeCheck}, + {"base64", base64::decode}, + {"hex", hex::decode}, + }; + + std::string getPipe() + { + std::string str; + str.reserve(1024 * 1024); +#ifdef _WIN32 + if (_isatty(_fileno(stdin))) +#else + if (isatty(fileno(stdin))) +#endif + { + throw std::logic_error("basen::getPipe: should use pipe syntax"); + } + else + { + char temp[4096]; + while (std::cin.read(temp, sizeof(temp))) + { + str.insert(str.end(), temp, temp + std::cin.gcount()); + } + str.insert(str.end(), temp, temp + std::cin.gcount()); + } + if (!str.empty() && str.back() == '\n') + { + str.erase(str.size() - 1); + } + return str; + } + uint8_t error(const std::string &str, argparse::ArgumentParser &program) noexcept + { + std::cerr << str << '\n'; + std::cerr << program << '\n'; + return 1; + } +} + int main(int argc, char *argv[]) { argparse::ArgumentParser program("basen", "1.1.0"); program.add_argument("-t", "--type") .help("encoding type") .metavar("STRING") - .choices("base58", "base64", "hex") - .default_value("hex"); + .choices("base58", "base58check", "base64", "hex"); + program.add_argument("-a", "--alphabet") + .help("specify alphabet") + .metavar("STRING"); program.add_argument("-d", "--decode") - .help("is decode") + .help("decode flag") .flag(); try { @@ -21,9 +85,65 @@ int main(int argc, char *argv[]) } catch (const std::exception &err) { - std::cerr << err.what() << '\n'; - std::cerr << program; - return 1; + return basen::error(err.what(), program); + } + if (!program.is_used("-t") && !program.is_used("-a")) + { + return basen::error("either -t or -a should be provided", program); + } + if (program.is_used("-t") && program.is_used("-a")) + { + return basen::error("-t and -a cannot be used together", program); + } + std::string str; + try + { + str = basen::getPipe(); + } + catch (const std::exception &e) + { + return basen::error("should use pipe syntax ( | or < )", program); + } + if (program.is_used("-t")) + { + auto type = program.get("-t"); + if (program.is_used("-d")) + { + auto data = (*basen::decoders.at(type))(str); + std::for_each(data.begin(), data.end(), [](uint8_t item) + { std::cout << (char)item; }); + std::cout << '\n'; + } + else + { + std::span dv((uint8_t *)str.data(), str.size()); + std::cout << (*basen::encoders.at(type))(dv) << '\n'; + } + } + if (program.is_used("-a")) + { + auto alphabet = program.get("-a"); + int8_t map[256]; + try + { + baseN::digitsMap(alphabet.data(), alphabet.size(), map); + } + catch (const std::exception &e) + { + return basen::error("alphabet contains same characters", program); + } + if (program.is_used("-d")) + { + auto data = baseN::decode(str, alphabet.size(), alphabet.data(), map); + std::for_each(data.begin(), data.end(), [](uint8_t item) + { std::cout << (char)item; }); + std::cout << '\n'; + } + else + { + std::span dv((uint8_t *)str.data(), str.size()); + std::cout << baseN::encode(dv, alphabet.size(), alphabet.data()) << '\n'; + } } return 0; } \ No newline at end of file