completion_generator.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. #include "completion_generator.h"
  2. #include <util/generic/overloaded.h>
  3. #include <util/string/ascii.h>
  4. #include <util/generic/hash_set.h>
  5. #include "last_getopt_parse_result.h"
  6. using NLastGetopt::NEscaping::Q;
  7. using NLastGetopt::NEscaping::QQ;
  8. using NLastGetopt::NEscaping::C;
  9. using NLastGetopt::NEscaping::CC;
  10. using NLastGetopt::NEscaping::S;
  11. using NLastGetopt::NEscaping::SS;
  12. using NLastGetopt::NEscaping::B;
  13. using NLastGetopt::NEscaping::BB;
  14. namespace NLastGetopt {
  15. #define L out.Line()
  16. #define I auto Y_GENERATE_UNIQUE_ID(indent) = out.Indent()
  17. TCompletionGenerator::TCompletionGenerator(const TModChooser* modChooser)
  18. : Options_(modChooser)
  19. {
  20. Y_ABORT_UNLESS(modChooser != nullptr);
  21. }
  22. TCompletionGenerator::TCompletionGenerator(const TOpts* opts)
  23. : Options_(opts)
  24. {
  25. Y_ABORT_UNLESS(opts != nullptr);
  26. }
  27. void TZshCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) {
  28. TFormattedOutput out;
  29. NComp::TCompleterManager manager{command};
  30. L << "#compdef " << command;
  31. L;
  32. L << "_" << command << "() {";
  33. {
  34. I;
  35. L << "local state line desc modes context curcontext=\"$curcontext\" ret=1";
  36. L << "local words_orig=(\"${words[@]}\")";
  37. L << "local current_orig=\"$((CURRENT - 1))\"";
  38. L << "local prefix_orig=\"$PREFIX\"";
  39. L << "local suffix_orig=\"$SUFFIX\"";
  40. L;
  41. std::visit(TOverloaded{
  42. [&out, &manager](const TModChooser* modChooser) {
  43. GenerateModesCompletion(out, *modChooser, manager);
  44. },
  45. [&out, &manager](const TOpts* opts) {
  46. GenerateOptsCompletion(out, *opts, manager);
  47. }
  48. }, Options_);
  49. L;
  50. L << "return ret";
  51. }
  52. L << "}";
  53. L;
  54. manager.GenerateZsh(out);
  55. out.Print(stream);
  56. }
  57. void TZshCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager) {
  58. auto modes = chooser.GetUnsortedModes();
  59. L << "_arguments -C \\";
  60. L << " '(- : *)'{-h,--help}'[show help information]' \\";
  61. if (chooser.GetVersionHandler() != nullptr) {
  62. L << " '(- : *)'{-v,--version}'[display version information]' \\";
  63. }
  64. if (!chooser.IsSvnRevisionOptionDisabled()) {
  65. L << " '(- : *)--svnrevision[show build information]' \\";
  66. }
  67. L << " '(-v --version -h --help --svnrevision)1: :->modes' \\";
  68. L << " '(-v --version -h --help --svnrevision)*:: :->args' \\";
  69. L << " && ret=0";
  70. L;
  71. L << "case $state in";
  72. {
  73. I;
  74. L << "modes)";
  75. {
  76. I;
  77. size_t tag = 0;
  78. bool empty = true;
  79. L << "desc='modes'";
  80. L << "modes=(";
  81. for (auto& mode : modes) {
  82. if (mode->Hidden) {
  83. continue;
  84. }
  85. if (!mode->Name.empty()) {
  86. I;
  87. if (!mode->Description.empty()) {
  88. L << QQ(mode->Name) << ":" << QQ(mode->Description);
  89. } else {
  90. L << QQ(mode->Name);
  91. }
  92. empty = false;
  93. } else {
  94. L << ")";
  95. if (!empty) {
  96. L << "_describe -t 'mode-group-" << tag << "' $desc modes";
  97. }
  98. L;
  99. if (mode->Description.empty()) {
  100. L << "desc='modes'";
  101. } else {
  102. L << "desc=" << SS(mode->Description);
  103. }
  104. L << "modes=(";
  105. empty = true;
  106. ++tag;
  107. }
  108. }
  109. L << ")";
  110. if (!empty) {
  111. L << "_describe -t 'mode-group-" << tag << "' $desc modes";
  112. }
  113. L;
  114. L << ";;";
  115. }
  116. L << "args)";
  117. {
  118. I;
  119. L << "case $line[1] in";
  120. {
  121. I;
  122. for (auto& mode : modes) {
  123. if (mode->Name.empty() || mode->Hidden) {
  124. continue;
  125. }
  126. auto& line = L << SS(mode->Name);
  127. for (auto& alias : mode->Aliases) {
  128. line << "|" << SS(alias);
  129. }
  130. line << ")";
  131. {
  132. I;
  133. if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) {
  134. GenerateOptsCompletion(out, mainArgs->GetOptions(), manager);
  135. } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) {
  136. GenerateModesCompletion(out, mainModes->GetSubModes(), manager);
  137. } else {
  138. GenerateDefaultOptsCompletion(out, manager);
  139. }
  140. L << ";;";
  141. }
  142. }
  143. }
  144. L << "esac";
  145. L << ";;";
  146. }
  147. }
  148. L << "esac";
  149. }
  150. void TZshCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager& manager) {
  151. L << "_arguments -s \\";
  152. {
  153. I;
  154. if (opts.ArgPermutation_ == EArgPermutation::REQUIRE_ORDER) {
  155. L << "-S \\";
  156. }
  157. for (auto opt: opts.GetOpts()) {
  158. if (!opt->Hidden_) {
  159. GenerateOptCompletion(out, opts, *opt, manager);
  160. }
  161. }
  162. auto argSpecs = opts.GetFreeArgSpecs();
  163. size_t numFreeArgs = opts.GetFreeArgsMax();
  164. bool unlimitedArgs = false;
  165. if (numFreeArgs == TOpts::UNLIMITED_ARGS) {
  166. numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1);
  167. unlimitedArgs = true;
  168. }
  169. for (size_t i = 0; i < numFreeArgs; ++i) {
  170. auto& spec = argSpecs[i];
  171. auto& line = L << "'" << (i + 1) << ":";
  172. if (spec.IsOptional()) {
  173. line << ":";
  174. }
  175. auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle());
  176. if (argHelp) {
  177. line << Q(argHelp);
  178. } else {
  179. line << " ";
  180. }
  181. line << ":";
  182. if (spec.Completer_) {
  183. line << spec.Completer_->GenerateZshAction(manager);
  184. } else {
  185. line << "_default";
  186. }
  187. line << "' \\";
  188. }
  189. if (unlimitedArgs) {
  190. auto& spec = opts.GetTrailingArgSpec();
  191. auto& line = L << "'*:";
  192. auto argHelp = spec.GetCompletionArgHelp(opts.GetDefaultFreeArgTitle());
  193. if (argHelp) {
  194. line << Q(argHelp);
  195. } else {
  196. line << " ";
  197. }
  198. line << ":";
  199. if (spec.Completer_) {
  200. line << spec.Completer_->GenerateZshAction(manager);
  201. } else {
  202. line << "_default";
  203. }
  204. line << "' \\";
  205. }
  206. L << "&& ret=0";
  207. }
  208. }
  209. void TZshCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) {
  210. L << "_arguments \\";
  211. L << " '(- *)'{-h,--help}'[show help information]' \\";
  212. L << " '(- *)--svnrevision[show build information]' \\";
  213. L << " '(-h --help --svnrevision)*: :_files' \\";
  214. L << " && ret=0";
  215. }
  216. void TZshCompletionGenerator::GenerateOptCompletion(TFormattedOutput& out, const TOpts& opts, const TOpt& opt, NComp::TCompleterManager& manager) {
  217. auto& line = L;
  218. THashSet<TString> disableOptions;
  219. if (opt.DisableCompletionForOptions_) {
  220. disableOptions.insert("-");
  221. } else {
  222. if (!opt.AllowMultipleCompletion_) {
  223. for (auto shortName: opt.GetShortNames()) {
  224. disableOptions.insert(TString("-") + shortName);
  225. }
  226. for (auto& longName: opt.GetLongNames()) {
  227. disableOptions.insert("--" + longName);
  228. }
  229. }
  230. for (auto disabledShortName : opt.DisableCompletionForChar_) {
  231. auto disabledOpt = opts.FindCharOption(disabledShortName);
  232. if (disabledOpt) {
  233. for (auto shortName: disabledOpt->GetShortNames()) {
  234. disableOptions.insert(TString("-") + shortName);
  235. }
  236. for (auto& longName: disabledOpt->GetLongNames()) {
  237. disableOptions.insert("--" + longName);
  238. }
  239. } else {
  240. disableOptions.insert(TString("-") + disabledShortName);
  241. }
  242. }
  243. for (auto& disabledLongName : opt.DisableCompletionForLongName_) {
  244. auto disabledOpt = opts.FindLongOption(disabledLongName);
  245. if (disabledOpt) {
  246. for (auto shortName: disabledOpt->GetShortNames()) {
  247. disableOptions.insert(TString("-") + shortName);
  248. }
  249. for (auto& longName: disabledOpt->GetLongNames()) {
  250. disableOptions.insert("--" + longName);
  251. }
  252. } else {
  253. disableOptions.insert("--" + disabledLongName);
  254. }
  255. }
  256. }
  257. if (opt.DisableCompletionForFreeArgs_) {
  258. disableOptions.insert(":");
  259. disableOptions.insert("*");
  260. } else {
  261. for (auto i : opt.DisableCompletionForFreeArg_) {
  262. disableOptions.insert(ToString(i + 1));
  263. }
  264. }
  265. TStringBuf sep = "";
  266. if (!disableOptions.empty()) {
  267. line << "'(";
  268. for (auto& disableOption : disableOptions) {
  269. line << sep << disableOption;
  270. sep = " ";
  271. }
  272. line << ")";
  273. }
  274. sep = "";
  275. TStringBuf mul = "";
  276. TStringBuf quot = "";
  277. if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) {
  278. if (!disableOptions.empty()) {
  279. line << "'";
  280. }
  281. line << "{";
  282. quot = "'";
  283. } else {
  284. if (disableOptions.empty()) {
  285. line << "'";
  286. }
  287. }
  288. if (opt.AllowMultipleCompletion_) {
  289. mul = "*";
  290. }
  291. for (auto& flag : opt.GetShortNames()) {
  292. line << sep << quot << mul << "-" << Q(TStringBuf(&flag, 1)) << quot;
  293. sep = ",";
  294. }
  295. for (auto& flag : opt.GetLongNames()) {
  296. line << sep << quot << mul << "--" << Q(flag) << quot;
  297. sep = ",";
  298. }
  299. if (opt.GetShortNames().size() + opt.GetLongNames().size() > 1) {
  300. line << "}'";
  301. }
  302. if (opt.GetCompletionHelp()) {
  303. line << "[";
  304. line << Q(opt.GetCompletionHelp());
  305. line << "]";
  306. }
  307. if (opt.HasArg_ != EHasArg::NO_ARGUMENT) {
  308. if (opt.HasArg_ == EHasArg::OPTIONAL_ARGUMENT) {
  309. line << ":";
  310. }
  311. line << ":";
  312. if (opt.GetCompletionArgHelp()) {
  313. line << C(opt.GetCompletionArgHelp());
  314. } else {
  315. line << " ";
  316. }
  317. line << ":";
  318. if (opt.Completer_) {
  319. line << C(opt.Completer_->GenerateZshAction(manager));
  320. } else {
  321. line << "_default";
  322. }
  323. }
  324. line << "' \\";
  325. }
  326. void TBashCompletionGenerator::Generate(TStringBuf command, IOutputStream& stream) {
  327. TFormattedOutput out;
  328. NComp::TCompleterManager manager{command};
  329. L << "_" << command << "() {";
  330. {
  331. I;
  332. L << "COMPREPLY=()";
  333. L;
  334. L << "local i args opts items candidates";
  335. L;
  336. L << "local cur prev words cword";
  337. L << "_get_comp_words_by_ref -n \"\\\"'><=;|&(:\" cur prev words cword";
  338. L;
  339. L << "local need_space=\"1\"";
  340. L << "local IFS=$' \\t\\n'";
  341. L;
  342. std::visit(TOverloaded{
  343. [&out, &manager](const TModChooser* modChooser) {
  344. GenerateModesCompletion(out, *modChooser, manager, 1);
  345. },
  346. [&out, &manager](const TOpts* opts) {
  347. GenerateOptsCompletion(out, *opts, manager, 1);
  348. }
  349. }, Options_);
  350. L;
  351. L;
  352. L << "__ltrim_colon_completions \"$cur\"";
  353. L;
  354. L << "IFS=$'\\n'";
  355. L << "if [ ${#COMPREPLY[@]} -ne 0 ]; then";
  356. {
  357. I;
  358. L << "if [[ -z $need_space ]]; then";
  359. {
  360. I;
  361. L << "COMPREPLY=( $(printf \"%q\\n\" \"${COMPREPLY[@]}\") )";
  362. }
  363. L << "else";
  364. {
  365. I;
  366. L << "COMPREPLY=( $(printf \"%q \\n\" \"${COMPREPLY[@]}\") )";
  367. }
  368. L << "fi";
  369. }
  370. L << "fi";
  371. L;
  372. L << "return 0";
  373. }
  374. L << "}";
  375. L;
  376. L << "complete -o nospace -o default -F _" << command << " " << command;
  377. out.Print(stream);
  378. }
  379. void TBashCompletionGenerator::GenerateModesCompletion(TFormattedOutput& out, const TModChooser& chooser, NComp::TCompleterManager& manager, size_t level) {
  380. auto modes = chooser.GetUnsortedModes();
  381. L << "if [[ ${cword} == " << level << " ]] ; then";
  382. {
  383. I;
  384. L << "if [[ ${cur} == -* ]] ; then";
  385. {
  386. I;
  387. auto& line = L << "COMPREPLY+=( $(compgen -W '-h --help";
  388. if (chooser.GetVersionHandler() != nullptr) {
  389. line << " -v --version";
  390. }
  391. if (!chooser.IsSvnRevisionOptionDisabled()) {
  392. line << " --svnrevision";
  393. }
  394. line << "' -- ${cur}) )";
  395. }
  396. L << "else";
  397. {
  398. I;
  399. auto& line = L << "COMPREPLY+=( $(compgen -W '";
  400. TStringBuf sep = "";
  401. for (auto& mode : modes) {
  402. if (!mode->Hidden && !mode->NoCompletion) {
  403. line << sep << B(mode->Name);
  404. sep = " ";
  405. }
  406. }
  407. line << "' -- ${cur}) )";
  408. }
  409. L << "fi";
  410. }
  411. L << "else";
  412. {
  413. I;
  414. L << "case \"${words[" << level << "]}\" in";
  415. {
  416. I;
  417. for (auto& mode : modes) {
  418. if (mode->Name.empty() || mode->Hidden || mode->NoCompletion) {
  419. continue;
  420. }
  421. auto& line = L << BB(mode->Name);
  422. for (auto& alias : mode->Aliases) {
  423. line << "|" << BB(alias);
  424. }
  425. line << ")";
  426. {
  427. I;
  428. if (auto mainArgs = dynamic_cast<TMainClassArgs*>(mode->Main)) {
  429. GenerateOptsCompletion(out, mainArgs->GetOptions(), manager, level + 1);
  430. } else if (auto mainModes = dynamic_cast<TMainClassModes*>(mode->Main)) {
  431. GenerateModesCompletion(out, mainModes->GetSubModes(), manager, level + 1);
  432. } else {
  433. GenerateDefaultOptsCompletion(out, manager);
  434. }
  435. L << ";;";
  436. }
  437. }
  438. }
  439. L << "esac";
  440. }
  441. L << "fi";
  442. }
  443. void TBashCompletionGenerator::GenerateOptsCompletion(TFormattedOutput& out, const TOpts& opts, NComp::TCompleterManager&, size_t level) {
  444. auto unorderedOpts = opts.GetOpts();
  445. L << "if [[ ${cur} == -* ]] ; then";
  446. {
  447. I;
  448. auto& line = L << "COMPREPLY+=( $(compgen -W '";
  449. TStringBuf sep = "";
  450. for (auto& opt : unorderedOpts) {
  451. if (opt->IsHidden()) {
  452. continue;
  453. }
  454. for (auto& shortName : opt->GetShortNames()) {
  455. line << sep << "-" << B(TStringBuf(&shortName, 1));
  456. sep = " ";
  457. }
  458. for (auto& longName: opt->GetLongNames()) {
  459. line << sep << "--" << B(longName);
  460. sep = " ";
  461. }
  462. }
  463. line << "' -- ${cur}) )";
  464. }
  465. L << "else";
  466. {
  467. I;
  468. L << "case ${prev} in";
  469. {
  470. I;
  471. for (auto& opt : unorderedOpts) {
  472. if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) {
  473. continue;
  474. }
  475. auto& line = L;
  476. TStringBuf sep = "";
  477. for (auto& shortName : opt->GetShortNames()) {
  478. line << sep << "'-" << B(TStringBuf(&shortName, 1)) << "'";
  479. sep = "|";
  480. }
  481. for (auto& longName: opt->GetLongNames()) {
  482. line << sep << "'--" << B(longName) << "'";
  483. sep = "|";
  484. }
  485. line << ")";
  486. {
  487. I;
  488. if (opt->Completer_ != nullptr) {
  489. opt->Completer_->GenerateBash(out);
  490. }
  491. L << ";;";
  492. }
  493. }
  494. L << "*)";
  495. {
  496. I;
  497. L << "args=0";
  498. auto& line = L << "opts='@(";
  499. TStringBuf sep = "";
  500. for (auto& opt : unorderedOpts) {
  501. if (opt->HasArg_ == EHasArg::NO_ARGUMENT || opt->IsHidden()) {
  502. continue;
  503. }
  504. for (auto& shortName : opt->GetShortNames()) {
  505. line << sep << "-" << B(TStringBuf(&shortName, 1));
  506. sep = "|";
  507. }
  508. for (auto& longName: opt->GetLongNames()) {
  509. line << sep << "--" << B(longName);
  510. sep = "|";
  511. }
  512. }
  513. line << ")'";
  514. L << "for (( i=" << level << "; i < cword; i++ )); do";
  515. {
  516. I;
  517. L << "if [[ ${words[i]} != -* && ${words[i-1]} != $opts ]]; then";
  518. {
  519. I;
  520. L << "(( args++ ))";
  521. }
  522. L << "fi";
  523. }
  524. L << "done";
  525. L;
  526. auto argSpecs = opts.GetFreeArgSpecs();
  527. size_t numFreeArgs = opts.GetFreeArgsMax();
  528. bool unlimitedArgs = false;
  529. if (numFreeArgs == TOpts::UNLIMITED_ARGS) {
  530. numFreeArgs = argSpecs.empty() ? 0 : (argSpecs.rbegin()->first + 1);
  531. unlimitedArgs = true;
  532. }
  533. L << "case ${args} in";
  534. {
  535. I;
  536. for (size_t i = 0; i < numFreeArgs; ++i) {
  537. L << i << ")";
  538. {
  539. I;
  540. auto& spec = argSpecs[i];
  541. if (spec.Completer_ != nullptr) {
  542. spec.Completer_->GenerateBash(out);
  543. }
  544. L << ";;";
  545. }
  546. }
  547. if (unlimitedArgs) {
  548. L << "*)";
  549. {
  550. I;
  551. auto& spec = opts.GetTrailingArgSpec();
  552. if (spec.Completer_ != nullptr) {
  553. spec.Completer_->GenerateBash(out);
  554. }
  555. L << ";;";
  556. }
  557. }
  558. }
  559. L << "esac";
  560. L << ";;";
  561. }
  562. }
  563. L << "esac";
  564. }
  565. L << "fi";
  566. }
  567. void TBashCompletionGenerator::GenerateDefaultOptsCompletion(TFormattedOutput& out, NComp::TCompleterManager&) {
  568. L << "if [[ ${cur} == -* ]] ; then";
  569. {
  570. I;
  571. L << "COMPREPLY+=( $(compgen -W '-h --help --svnrevision' -- ${cur}) )";
  572. }
  573. L << "fi";
  574. }
  575. #undef I
  576. #undef L
  577. TString NEscaping::Q(TStringBuf string) {
  578. TStringBuilder out;
  579. out.reserve(string.size());
  580. for (auto c: string) {
  581. switch (c) {
  582. case '\a':
  583. case '\b':
  584. case '\f':
  585. case '\n':
  586. case '\r':
  587. case '\t':
  588. case '\v':
  589. out << " ";
  590. break;
  591. case '\\':
  592. out << "\\\\";
  593. break;
  594. case '\'':
  595. out << "''";
  596. break;
  597. case '\"':
  598. out << "\\\"";
  599. break;
  600. case '[':
  601. out << "\\[";
  602. break;
  603. case ']':
  604. out << "\\]";
  605. break;
  606. case ':':
  607. out << "\\:";
  608. break;
  609. case '+':
  610. out << "\\+";
  611. break;
  612. case '=':
  613. out << "\\=";
  614. break;
  615. default:
  616. out << c;
  617. break;
  618. }
  619. }
  620. return out;
  621. }
  622. TString NEscaping::QQ(TStringBuf string) {
  623. auto q = Q(string);
  624. return "'" + q + "'";
  625. }
  626. TString NEscaping::C(TStringBuf string) {
  627. TStringBuilder out;
  628. out.reserve(string.size() + 1);
  629. for (auto c: string) {
  630. switch (c) {
  631. case '\a':
  632. case '\b':
  633. case '\f':
  634. case '\n':
  635. case '\r':
  636. case '\t':
  637. case '\v':
  638. out << " ";
  639. break;
  640. case '\'':
  641. out << "''";
  642. break;
  643. case ':':
  644. out << "\\:";
  645. break;
  646. default:
  647. out << c;
  648. break;
  649. }
  650. }
  651. return out;
  652. }
  653. TString NEscaping::CC(TStringBuf string) {
  654. auto c = C(string);
  655. return "'" + c + "'";
  656. }
  657. TString NEscaping::S(TStringBuf string) {
  658. TStringBuilder out;
  659. out.reserve(string.size() + 1);
  660. for (auto c: string) {
  661. switch (c) {
  662. case '\a':
  663. case '\b':
  664. case '\f':
  665. case '\n':
  666. case '\r':
  667. case '\t':
  668. case '\v':
  669. out << " ";
  670. break;
  671. case '\'':
  672. out << "''";
  673. break;
  674. default:
  675. out << c;
  676. break;
  677. }
  678. }
  679. return out;
  680. }
  681. TString NEscaping::SS(TStringBuf string) {
  682. auto s = S(string);
  683. return "'" + s + "'";
  684. }
  685. TString NEscaping::B(TStringBuf string) {
  686. TStringBuilder out;
  687. out.reserve(string.size() + 1);
  688. for (auto c: string) {
  689. switch (c) {
  690. case '\a':
  691. case '\b':
  692. case '\f':
  693. case '\n':
  694. case '\r':
  695. case '\t':
  696. case '\v':
  697. out << " ";
  698. break;
  699. case '\'':
  700. out << "'\"'\"'";
  701. break;
  702. default:
  703. out << c;
  704. break;
  705. }
  706. }
  707. return out;
  708. }
  709. TString NEscaping::BB(TStringBuf string) {
  710. auto b = B(string);
  711. return "'" + b + "'";
  712. }
  713. }