modchooser.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. #include "completer.h"
  2. #include "completer_command.h"
  3. #include "last_getopt.h"
  4. #include "modchooser.h"
  5. #include <library/cpp/colorizer/colors.h>
  6. #include <util/folder/path.h>
  7. #include <util/stream/output.h>
  8. #include <util/generic/yexception.h>
  9. #include <util/generic/ptr.h>
  10. #include <util/string/builder.h>
  11. class PtrWrapper: public TMainClass {
  12. public:
  13. explicit PtrWrapper(const TMainFunctionPtr& main)
  14. : Main(main)
  15. {
  16. }
  17. int operator()(const int argc, const char** argv) override {
  18. return Main(argc, argv);
  19. }
  20. private:
  21. TMainFunctionPtr Main;
  22. };
  23. class PtrvWrapper: public TMainClass {
  24. public:
  25. explicit PtrvWrapper(const TMainFunctionPtrV& main)
  26. : Main(main)
  27. {
  28. }
  29. int operator()(const int argc, const char** argv) override {
  30. TVector<TString> nargv(argv, argv + argc);
  31. return Main(nargv);
  32. }
  33. private:
  34. TMainFunctionPtrV Main;
  35. };
  36. class ClassWrapper: public TMainClass {
  37. public:
  38. explicit ClassWrapper(TMainClassV* main)
  39. : Main(main)
  40. {
  41. }
  42. int operator()(const int argc, const char** argv) override {
  43. TVector<TString> nargv(argv, argv + argc);
  44. return (*Main)(nargv);
  45. }
  46. private:
  47. TMainClassV* Main;
  48. };
  49. TModChooser::TMode::TMode(const TString& name, TMainClass* main, const TString& descr, bool hidden, bool noCompletion)
  50. : Name(name)
  51. , Main(main)
  52. , Description(descr)
  53. , Hidden(hidden)
  54. , NoCompletion(noCompletion)
  55. {
  56. }
  57. TModChooser::TModChooser()
  58. : ModesHelpOption("-?") // Default help option in last_getopt
  59. , VersionHandler(nullptr)
  60. , ShowSeparated(true)
  61. , SvnRevisionOptionDisabled(false)
  62. , PrintShortCommandInUsage(false)
  63. {
  64. }
  65. TModChooser::~TModChooser() = default;
  66. void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtr func, const TString& description, bool hidden, bool noCompletion) {
  67. AddMode(mode, TMainFunctionPtr(func), description, hidden, noCompletion);
  68. }
  69. void TModChooser::AddMode(const TString& mode, const TMainFunctionRawPtrV func, const TString& description, bool hidden, bool noCompletion) {
  70. AddMode(mode, TMainFunctionPtrV(func), description, hidden, noCompletion);
  71. }
  72. void TModChooser::AddMode(const TString& mode, const TMainFunctionPtr func, const TString& description, bool hidden, bool noCompletion) {
  73. Wrappers.push_back(MakeHolder<PtrWrapper>(func));
  74. AddMode(mode, Wrappers.back().Get(), description, hidden, noCompletion);
  75. }
  76. void TModChooser::AddMode(const TString& mode, const TMainFunctionPtrV func, const TString& description, bool hidden, bool noCompletion) {
  77. Wrappers.push_back(MakeHolder<PtrvWrapper>(func));
  78. AddMode(mode, Wrappers.back().Get(), description, hidden, noCompletion);
  79. }
  80. void TModChooser::AddMode(const TString& mode, TMainClass* func, const TString& description, bool hidden, bool noCompletion) {
  81. if (Modes.FindPtr(mode)) {
  82. ythrow yexception() << "TMode '" << mode << "' already exists in TModChooser.";
  83. }
  84. Modes[mode] = UnsortedModes.emplace_back(MakeHolder<TMode>(mode, func, description, hidden, noCompletion)).Get();
  85. }
  86. void TModChooser::AddMode(const TString& mode, TMainClassV* func, const TString& description, bool hidden, bool noCompletion) {
  87. Wrappers.push_back(MakeHolder<ClassWrapper>(func));
  88. AddMode(mode, Wrappers.back().Get(), description, hidden, noCompletion);
  89. }
  90. void TModChooser::AddGroupModeDescription(const TString& description, bool hidden, bool noCompletion) {
  91. UnsortedModes.push_back(MakeHolder<TMode>(TString(), nullptr, description.data(), hidden, noCompletion));
  92. }
  93. void TModChooser::SetDefaultMode(const TString& mode) {
  94. DefaultMode = mode;
  95. }
  96. void TModChooser::AddAlias(const TString& alias, const TString& mode) {
  97. if (!Modes.FindPtr(mode)) {
  98. ythrow yexception() << "TMode '" << mode << "' not found in TModChooser.";
  99. }
  100. Modes[mode]->Aliases.push_back(alias);
  101. Modes[alias] = Modes[mode];
  102. }
  103. void TModChooser::SetDescription(const TString& descr) {
  104. Description = descr;
  105. }
  106. void TModChooser::SetModesHelpOption(const TString& helpOption) {
  107. ModesHelpOption = helpOption;
  108. }
  109. void TModChooser::SetVersionHandler(TVersionHandlerPtr handler) {
  110. VersionHandler = handler;
  111. }
  112. void TModChooser::SetSeparatedMode(bool separated) {
  113. ShowSeparated = separated;
  114. }
  115. void TModChooser::SetSeparationString(const TString& str) {
  116. SeparationString = str;
  117. }
  118. void TModChooser::SetPrintShortCommandInUsage(bool printShortCommandInUsage = false) {
  119. PrintShortCommandInUsage = printShortCommandInUsage;
  120. }
  121. void TModChooser::DisableSvnRevisionOption() {
  122. SvnRevisionOptionDisabled = true;
  123. }
  124. void TModChooser::AddCompletions(TString progName, const TString& name, bool hidden, bool noCompletion) {
  125. if (CompletionsGenerator == nullptr) {
  126. CompletionsGenerator = NLastGetopt::MakeCompletionMod(this, std::move(progName), name);
  127. AddMode(name, CompletionsGenerator.Get(), "generate autocompletion files", hidden, noCompletion);
  128. }
  129. }
  130. int TModChooser::Run(const int argc, const char** argv) const {
  131. Y_ENSURE(argc, "Can't run TModChooser with empty list of arguments.");
  132. bool shiftArgs = true;
  133. TString modeName;
  134. if (argc == 1) {
  135. if (DefaultMode.empty()) {
  136. PrintHelp(argv[0], HelpAlwaysToStdErr);
  137. return 0;
  138. } else {
  139. modeName = DefaultMode;
  140. shiftArgs = false;
  141. }
  142. } else {
  143. modeName = argv[1];
  144. }
  145. if (modeName == "-h" || modeName == "--help" || modeName == "-?") {
  146. PrintHelp(argv[0], HelpAlwaysToStdErr);
  147. return 0;
  148. }
  149. if (VersionHandler && (modeName == "-v" || modeName == "--version")) {
  150. VersionHandler();
  151. return 0;
  152. }
  153. if (!SvnRevisionOptionDisabled && modeName == "--svnrevision") {
  154. NLastGetopt::PrintVersionAndExit(nullptr);
  155. }
  156. auto modeIter = Modes.find(modeName);
  157. if (modeIter == Modes.end() && !DefaultMode.empty()) {
  158. modeIter = Modes.find(DefaultMode);
  159. shiftArgs = false;
  160. }
  161. if (modeIter == Modes.end()) {
  162. Cerr << "Unknown mode " << modeName.Quote() << "." << Endl;
  163. PrintHelp(argv[0], true);
  164. return 1;
  165. }
  166. if (shiftArgs) {
  167. TString firstArg;
  168. TVector<const char*> nargv(Reserve(argc));
  169. if (PrintShortCommandInUsage) {
  170. firstArg = modeIter->second->Name;
  171. } else {
  172. firstArg = argv[0] + TString(" ") + modeIter->second->Name;
  173. }
  174. nargv.push_back(firstArg.data());
  175. for (int i = 2; i < argc; ++i) {
  176. nargv.push_back(argv[i]);
  177. }
  178. // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1).
  179. // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336
  180. nargv.push_back(nullptr);
  181. return (*modeIter->second->Main)(nargv.size() - 1, nargv.data());
  182. } else {
  183. return (*modeIter->second->Main)(argc, argv);
  184. }
  185. }
  186. int TModChooser::Run(const TVector<TString>& argv) const {
  187. TVector<const char*> nargv(Reserve(argv.size() + 1));
  188. for (auto& arg : argv) {
  189. nargv.push_back(arg.c_str());
  190. }
  191. // According to the standard, "argv[argc] shall be a null pointer" (5.1.2.2.1).
  192. // http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336
  193. nargv.push_back(nullptr);
  194. return Run(nargv.size() - 1, nargv.data());
  195. }
  196. size_t TModChooser::TMode::CalculateFullNameLen() const {
  197. size_t len = Name.size();
  198. if (Aliases) {
  199. len += 2;
  200. for (auto& alias : Aliases) {
  201. len += alias.size() + 1;
  202. }
  203. }
  204. return len;
  205. }
  206. TString TModChooser::TMode::FormatFullName(size_t pad, const NColorizer::TColors& colors) const {
  207. TStringBuilder name;
  208. if (Aliases) {
  209. name << "{";
  210. }
  211. name << colors.GreenColor();
  212. name << Name;
  213. name << colors.OldColor();
  214. if (Aliases) {
  215. for (const auto& alias : Aliases) {
  216. name << "|" << colors.GreenColor() << alias << colors.OldColor();
  217. }
  218. name << "}";
  219. }
  220. auto len = CalculateFullNameLen();
  221. if (pad > len) {
  222. name << TString(" ") * (pad - len);
  223. }
  224. return name;
  225. }
  226. void TModChooser::PrintHelp(const TString& progName, bool toStdErr) const {
  227. auto baseName = TFsPath(progName).Basename();
  228. auto& out = toStdErr ? Cerr : Cout;
  229. const auto& colors = toStdErr ? NColorizer::StdErr() : NColorizer::StdOut();
  230. out << Description << Endl << Endl;
  231. out << colors.BoldColor() << "Usage" << colors.OldColor() << ": " << baseName << " MODE [MODE_OPTIONS]" << Endl;
  232. out << Endl;
  233. out << colors.BoldColor() << "Modes" << colors.OldColor() << ":" << Endl;
  234. size_t maxModeLen = 0;
  235. for (const auto& [name, mode] : Modes) {
  236. if (name != mode->Name)
  237. continue; // this is an alias
  238. maxModeLen = Max(maxModeLen, mode->CalculateFullNameLen());
  239. }
  240. if (ShowSeparated) {
  241. for (const auto& unsortedMode : UnsortedModes)
  242. if (!unsortedMode->Hidden) {
  243. if (unsortedMode->Name.size()) {
  244. out << " " << unsortedMode->FormatFullName(maxModeLen + 4, colors) << unsortedMode->Description << Endl;
  245. } else {
  246. out << SeparationString << Endl;
  247. out << unsortedMode->Description << Endl;
  248. }
  249. }
  250. } else {
  251. for (const auto& mode : Modes) {
  252. if (mode.first != mode.second->Name)
  253. continue; // this is an alias
  254. if (!mode.second->Hidden) {
  255. out << " " << mode.second->FormatFullName(maxModeLen + 4, colors) << mode.second->Description << Endl;
  256. }
  257. }
  258. }
  259. out << Endl;
  260. out << "To get help for specific mode type '" << baseName << " MODE " << ModesHelpOption << "'" << Endl;
  261. if (VersionHandler)
  262. out << "To print program version type '" << baseName << " --version'" << Endl;
  263. if (!SvnRevisionOptionDisabled) {
  264. out << "To print svn revision type '" << baseName << " --svnrevision'" << Endl;
  265. }
  266. }
  267. TVersionHandlerPtr TModChooser::GetVersionHandler() const {
  268. return VersionHandler;
  269. }
  270. bool TModChooser::IsSvnRevisionOptionDisabled() const {
  271. return SvnRevisionOptionDisabled;
  272. }
  273. int TMainClassArgs::Run(int argc, const char** argv) {
  274. return DoRun(NLastGetopt::TOptsParseResult(&GetOptions(), argc, argv));
  275. }
  276. const NLastGetopt::TOpts& TMainClassArgs::GetOptions() {
  277. if (Opts_.Empty()) {
  278. Opts_ = NLastGetopt::TOpts();
  279. RegisterOptions(Opts_.GetRef());
  280. }
  281. return Opts_.GetRef();
  282. }
  283. void TMainClassArgs::RegisterOptions(NLastGetopt::TOpts& opts) {
  284. opts.AddHelpOption('h');
  285. }
  286. int TMainClassArgs::operator()(const int argc, const char** argv) {
  287. return Run(argc, argv);
  288. }
  289. int TMainClassModes::operator()(const int argc, const char** argv) {
  290. return Run(argc, argv);
  291. }
  292. int TMainClassModes::Run(int argc, const char** argv) {
  293. auto& chooser = GetSubModes();
  294. return chooser.Run(argc, argv);
  295. }
  296. const TModChooser& TMainClassModes::GetSubModes() {
  297. if (Modes_.Empty()) {
  298. Modes_.ConstructInPlace();
  299. RegisterModes(Modes_.GetRef());
  300. }
  301. return Modes_.GetRef();
  302. }
  303. void TMainClassModes::RegisterModes(TModChooser& modes) {
  304. modes.SetModesHelpOption("-h");
  305. }