crop.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #include <deque>
  2. #include <iostream>
  3. #include <iomanip>
  4. #include "simple/file.hpp"
  5. #include "simple/support/enum.hpp"
  6. #include "simple/support/misc.hpp"
  7. #include "simple/support/algorithm.hpp"
  8. #include "simple/support/iterator/match.hpp"
  9. using namespace simple;
  10. using namespace std::literals;
  11. enum class Options
  12. {
  13. Padding,
  14. Color,
  15. Source,
  16. Format,
  17. Separator,
  18. Wordwise,
  19. Invalid
  20. };
  21. using Option = support::mapped_enum<Options, Options::Invalid, 2>;
  22. template <> Option::guts::map_type Option::guts::map
  23. {{
  24. { "-p"s, "--padding"s },
  25. { "-c"s, "--color"s },
  26. { "-i"s, "--source"s },
  27. { "-f"s, "--format"s },
  28. { "-s"s, "--separator"s },
  29. { "-w"s, "--words"s },
  30. }};
  31. enum class Formats
  32. {
  33. PlainText, OneLine, Hex,
  34. Invalid
  35. };
  36. using Format = support::mapped_enum<Formats, Formats::Invalid>;
  37. template <> Format::guts::map_type Format::guts::map
  38. {{
  39. { "plain"s }, { "one-line"s }, { "hex"s },
  40. }};
  41. enum class Colors
  42. {
  43. Black, Red,
  44. Green, Yellow,
  45. Blue, Magenta,
  46. Cyan, White,
  47. Invalid
  48. };
  49. using Color = support::mapped_enum<Colors, Colors::Invalid>;
  50. template <> Color::guts::map_type Color::guts::map
  51. {{
  52. { "black"s }, { "red"s },
  53. { "green"s }, { "yellow"s },
  54. { "blue"s }, { "magenta"s },
  55. { "cyan"s }, { "white"s }
  56. }};
  57. using buffer = std::vector<unsigned char>;
  58. using const_range = support::range<buffer::const_iterator>;
  59. template <typename Range>
  60. void print(Range range, Format format, Color color = Colors::Invalid)
  61. {
  62. assert(range.valid());
  63. auto cout_flags = std::cout.flags();
  64. std::cout << std::hex;
  65. if(Colors::Invalid != color)
  66. std::cout << "\33[4" << char('0' + (int)Colors(color)) << 'm';
  67. switch(format)
  68. {
  69. case Formats::Hex:
  70. if constexpr (std::is_same_v<Range, const_range>)
  71. for(auto&& c : range)
  72. {
  73. if(c <= 0xf)
  74. std::cout << '0';
  75. std::cout << +c;
  76. }
  77. else std::cerr << "ERROR: Hex format not supported for wordwise crop." << '\n';
  78. break;
  79. case Formats::OneLine:
  80. if constexpr (std::is_same_v<Range, const_range>)
  81. for(auto&& c : range)
  82. {
  83. auto cc = '\n' == c ? 'n' : '\r' == c ? 'r' : c;
  84. if(cc != c)
  85. std::cout << "\33[7m" << cc << "\33[27m";
  86. else
  87. std::cout << c;
  88. }
  89. else std::cerr << "ERROR: One line format not supported for wordwise crop." << '\n';
  90. break;
  91. default:
  92. for(auto&& c : range)
  93. {
  94. std::cout << c;
  95. if constexpr (not std::is_same_v<Range, const_range>)
  96. std::cout << ' ';
  97. }
  98. }
  99. if(Colors::Invalid != color)
  100. std::cout << "\33[0m";
  101. std::cout.flags(cout_flags);
  102. }
  103. auto split_words(const buffer& b)
  104. {
  105. std::vector<std::string> words;
  106. simple::support::split(b, simple::support::match_iterator(simple::support::is_space), std::back_inserter(words));
  107. return words;
  108. }
  109. void crop(const std::string& filename, const std::string& range, Color color, int padding, Format format, bool wordwise)
  110. {
  111. auto r = support::storn<int64_t>(range);
  112. const auto source = file::dump<buffer>(file::bropex(filename));
  113. auto do_print = [r, padding, format, color](const auto& source)
  114. {
  115. print(support::get_iterator_range<int64_t>(source, {r.lower()-padding , r.lower()}), format);
  116. print(support::get_iterator_range<int64_t>(source, r), format, color);
  117. print(support::get_iterator_range<int64_t>(source, {r.upper(), r.upper()+padding}), format);
  118. };
  119. if(wordwise)
  120. do_print(split_words(source));
  121. else
  122. do_print(source);
  123. }
  124. void process_arguments(std::deque<std::string> args)
  125. {
  126. std::string filename;
  127. std::string separator;
  128. Color color;
  129. Format format;
  130. int padding = 0;
  131. bool wordwise = false;
  132. args.pop_front();
  133. while(!args.empty())
  134. {
  135. switch(Option(args.front()))
  136. {
  137. case Options::Padding:
  138. args.pop_front();
  139. padding = support::ston<int>(args.at(0));
  140. if(padding < 0)
  141. throw std::out_of_range("negative padding");
  142. break;
  143. case Options::Color:
  144. args.pop_front();
  145. color = Color(args.at(0));
  146. break;
  147. case Options::Source:
  148. args.pop_front();
  149. filename = args.at(0);
  150. break;
  151. case Options::Separator:
  152. args.pop_front();
  153. separator = args.at(0) + '\n';
  154. break;
  155. case Options::Format:
  156. args.pop_front();
  157. format = Format(args.at(0));
  158. break;
  159. case Options::Wordwise:
  160. args.pop_front();
  161. wordwise = support::ston<bool>(args.at(0));
  162. break;
  163. default:
  164. crop(filename, args.front(), color, padding, format, wordwise);
  165. std::cout << '\n' << separator;
  166. break;
  167. }
  168. args.pop_front();
  169. }
  170. }
  171. int main(int argc, char const* argv[]) try
  172. {
  173. process_arguments({argv, argv + argc});
  174. return 0;
  175. }
  176. catch(...)
  177. {
  178. if(errno) std::perror("Oh nooo!");
  179. throw;
  180. }