#include "file.h" #include "unicode.h" #include #include #include #include #include #include #include #include namespace fs = std::filesystem; using namespace std::string_literals; namespace { void usage() { std::cout << "Usage: recode " << std::endl; std::cout << "Format:" << std::endl; std::cout << " UTF-8 UTF-8" << std::endl; std::cout << " UTF-16 UTF-16, native endian" << std::endl; std::cout << " UTF-16LE UTF-16, little endian" << std::endl; std::cout << " UTF-16BE UTF-16, big endian" << std::endl; std::cout << " UTF-32 UTF-32, native endian" << std::endl; std::cout << " UTF-32LE UTF-32, little endian" << std::endl; std::cout << " UTF-32BE UTF-32, big endian" << std::endl; std::cout << " ISO-8859-1 ISO-8859-1 (Latin-1)" << std::endl; std::cout << " ISO-8859-15 ISO-8859-15 (Latin-9)" << std::endl; std::cout << "Exit code: 0 if valid, 1 otherwise." << std::endl; } std::unordered_map typeid_name_map { { "UTF-8", typeid(unicode::UTF_8).name() }, { "UTF-16", typeid(unicode::UTF_16).name() }, { "UTF-16LE", typeid(unicode::UTF_16).name() }, { "UTF-16BE", typeid(unicode::UTF_16).name() }, { "UTF-32", typeid(unicode::UTF_32).name() }, { "UTF-32LE", typeid(unicode::UTF_32).name() }, { "UTF-32BE", typeid(unicode::UTF_32).name() }, { "ISO-8859-1", typeid(unicode::ISO_8859_1).name() }, { "ISO-8859-15", typeid(unicode::ISO_8859_15).name() }, }; std::string get_id(const std::string& from, const std::string& to) { return from + "," + to; } template std::string get_id() { return get_id(std::string{typeid(From).name()}, typeid(To).name()); } template void reverse_endian(std::basic_string& s) { std::for_each(s.begin(), s.end(), [](T& c){boost::endian::endian_reverse_inplace(c);}); } std::unordered_map> convert_map {}; template void register_convert() { std::string id{ get_id() }; std::function f([](const std::string& s, bool swap_from_endian, bool swap_to_endian) -> std::string { if (s.size() % sizeof(typename From::value_type) != 0) throw std::invalid_argument("Bad number of input bytes. Need multiple of "s + std::to_string(sizeof(typename From::value_type)) + ", got " + std::to_string(s.size())); std::basic_string from_data(s.size(), static_cast(0)); std::memcpy(from_data.data(), s.data(), s.size()); if (swap_from_endian) { reverse_endian(from_data); } std::basic_string to_data {unicode::convert(from_data)}; if (swap_to_endian) { reverse_endian(to_data); } std::string result(to_data.size() * sizeof(typename To::value_type), '\0'); std::memcpy(result.data(), to_data.data(), to_data.size() * sizeof(typename To::value_type)); return result; }); convert_map[id] = f; } template using NthTypeOf = typename std::tuple_element>::type; template void iterate_over() { register_convert, NthTypeOf>(); if constexpr (i + 1 < sizeof...(Ts)) { iterate_over(); } else if constexpr (j + 1 < sizeof...(Ts)) { iterate_over<0, j + 1, Ts...>(); } } template void build_map() { iterate_over<0, 0, Ts...>(); } } int main(int argc, char* argv[]) { if (argc != 5) { usage(); return 1; } try { build_map(); std::string from_format {argv[1]}; fs::path from_path {argv[2]}; std::string to_format {argv[3]}; fs::path to_path {argv[4]}; std::string data{unicode::File::getFile(from_path)}; auto it_from{typeid_name_map.find(from_format)}; if (it_from == typeid_name_map.end()) throw std::invalid_argument("Bad input format: "s + from_format); auto it_to{typeid_name_map.find(to_format)}; if (it_to == typeid_name_map.end()) throw std::invalid_argument("Bad output format: "s + to_format); std::string id{get_id(it_from->second, it_to->second)}; auto it { convert_map.find(id) }; if (it == convert_map.end()) { std::cerr << "Error: Conversion ID " << id << " not supported." << std::endl; return 1; } bool swap_from_endian{(boost::algorithm::ends_with(from_format, "LE") && boost::endian::order::native != boost::endian::order::little) || (boost::algorithm::ends_with(from_format, "BE") && boost::endian::order::native != boost::endian::order::big)}; bool swap_to_endian{(boost::algorithm::ends_with(to_format, "LE") && boost::endian::order::native != boost::endian::order::little) || (boost::algorithm::ends_with(to_format, "BE") && boost::endian::order::native != boost::endian::order::big)}; // actual conversion std::string to_data{it->second(data, swap_from_endian, swap_to_endian)}; unicode::File::setFile(to_path, to_data); } catch (const std::exception& ex) { std::cerr << "Error: " << ex.what() << std::endl; return 1; } return 0; }