libDwm-0.9.45
DwmArguments.hh
Go to the documentation of this file.
1//===========================================================================
2// @(#) $DwmPath$
3//===========================================================================
4// Copyright (c) Daniel W. McRobb 2019, 2020
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// 2. Redistributions in binary form must reproduce the above copyright
14// notice, this list of conditions and the following disclaimer in the
15// documentation and/or other materials provided with the distribution.
16// 3. The names of the authors and copyright holders may not be used to
17// endorse or promote products derived from this software without
18// specific prior written permission.
19//
20// IN NO EVENT SHALL DANIEL W. MCROBB BE LIABLE TO ANY PARTY FOR
21// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
22// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE,
23// EVEN IF DANIEL W. MCROBB HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
24// DAMAGE.
25//
26// THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND
27// DANIEL W. MCROBB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
28// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DANIEL W. MCROBB MAKES NO
29// REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER
30// IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31// WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE,
32// OR THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANY PATENT,
33// TRADEMARK OR OTHER RIGHTS.
34//===========================================================================
35
36//---------------------------------------------------------------------------
39//---------------------------------------------------------------------------
40
41
42extern "C" {
43 #include <unistd.h>
44}
45
46#include <array>
47#include <iomanip>
48#include <regex>
49#include <sstream>
50#include <utility>
51
52#include "DwmArgumentIO.hh"
53
54namespace Dwm {
55
56 //--------------------------------------------------------------------------
60 //--------------------------------------------------------------------------
61 template <char C, typename ValueType, bool Req = false>
62 class Argument
63 {
64 public:
65 //------------------------------------------------------------------------
67 //------------------------------------------------------------------------
68 Argument() : _value(), _valueName(), _help(), _conflicts() { }
69
70 //------------------------------------------------------------------------
72 //------------------------------------------------------------------------
73 Argument(const ValueType & v, const std::string & valueName = "",
74 const std::string & help = "")
75 : _value(v), _valueName(valueName), _help(help), _conflicts()
76 { }
77
78 //------------------------------------------------------------------------
80 //------------------------------------------------------------------------
81 static constexpr char OptChar() { return _c; }
82
83 //------------------------------------------------------------------------
85 //------------------------------------------------------------------------
86 const ValueType & Value() const { return _value; }
87
88 //------------------------------------------------------------------------
90 //------------------------------------------------------------------------
91 const ValueType & Value(const ValueType & v)
92 {
93 _value = v;
94 return _value;
95 }
96
97 //------------------------------------------------------------------------
99 //------------------------------------------------------------------------
100 ValueType & Value() { return _value; }
101
102 //------------------------------------------------------------------------
104 //------------------------------------------------------------------------
105 static constexpr bool Required() { return _required; }
106
107 //------------------------------------------------------------------------
109 //------------------------------------------------------------------------
110 static constexpr bool NeedsValue() { return _needsValue; }
111
112 //------------------------------------------------------------------------
114 //------------------------------------------------------------------------
115 const std::string & ValueName() const { return _valueName; }
116
117 //------------------------------------------------------------------------
119 //------------------------------------------------------------------------
120 const std::string & ValueName(const std::string & valueName)
121 {
122 _valueName = valueName;
123 return _valueName;
124 }
125
126 //------------------------------------------------------------------------
128 //------------------------------------------------------------------------
129 const std::string & Help() const { return _help; }
130
131 //------------------------------------------------------------------------
133 //------------------------------------------------------------------------
134 const std::string & Help(const std::string & help)
135 {
136 _help = help;
137 return _help;
138 }
139
140 //------------------------------------------------------------------------
142 //------------------------------------------------------------------------
143 const std::set<char> & Conflicts() const { return _conflicts; }
144
145 //------------------------------------------------------------------------
147 //------------------------------------------------------------------------
148 const std::set<char> & Conflicts(const std::set<char> & conflicts)
149 {
150 _conflicts = conflicts;
151 return _conflicts;
152 }
153
154 //------------------------------------------------------------------------
156 //------------------------------------------------------------------------
157 std::string UsageString() const
158 {
159 std::string rc;
160 if (isprint(_c)) {
161 rc += '-';
162 rc += _c;
163 }
164 if (_needsValue) {
165 if (isprint(_c)) {
166 rc += ' ';
167 }
168 rc += _valueName;
169 }
170 return rc;
171 }
172
173 //------------------------------------------------------------------------
175 //------------------------------------------------------------------------
176 static std::string IndentedText(const std::string & s)
177 {
178 std::string rc;
179 std::string ss;
180 static const std::set<char> ws({' ','\t','\n'});
181 size_t col = 8;
182 for (auto i = 0; i < s.size(); ++i) {
183 if (ws.find(s[i]) != ws.end()) {
184 if ('\n' == s[i]) {
185 rc += "\n\t";
186 col = 8;
187 }
188 else {
189 size_t j = i+1;
190 for ( ; (j < s.size()) && (ws.find(s[j]) == ws.end()); ++j) {
191 }
192 if ((col + (j - i)) > 76) {
193 rc += "\n\t";
194 col = 8;
195 }
196 else {
197 rc += s[i];
198 ++col;
199 }
200 }
201 }
202 else {
203 rc += s[i];
204 ++col;
205 }
206 }
207 return rc;
208 }
209
210 //------------------------------------------------------------------------
212 //------------------------------------------------------------------------
213 std::string HelpText() const
214 {
215 std::string rc(UsageString());
216 if (NeedsValue()) {
217 rc += '\n';
218 }
219 rc += '\t';
220 rc += IndentedText(_help);
221 return rc;
222 }
223
224 private:
225 ValueType _value;
226 std::string _valueName;
227 std::string _help;
228 std::set<char> _conflicts;
229
230 static constexpr const char _c = C;
231 static constexpr const bool _needsValue =
232 (! std::is_same<bool, ValueType>::value);
233 static constexpr const bool _required = Req;
234 };
235
236 //--------------------------------------------------------------------------
238 //--------------------------------------------------------------------------
239 template <class ...Ts>
240 class Arguments
241 {
242 public:
243 //------------------------------------------------------------------------
245 //------------------------------------------------------------------------
246 Arguments(const char *envVar = nullptr)
247 : _args()
248 {
249 LoadFromEnvironment(envVar);
250 }
251
252 //------------------------------------------------------------------------
254 //------------------------------------------------------------------------
255 Arguments(const char *envVar, Ts ...args)
256 : _args({ args... })
257 {
258 LoadFromEnvironment(envVar);
259 }
260
261 //------------------------------------------------------------------------
263 //------------------------------------------------------------------------
264 int Parse(int argc, char *argv[])
265 {
266 bool erc = false;
267 std::string optStr = OptString();
268 int optChar;
269
270 _gotArgs.clear();
271 while ((optChar = getopt(argc, argv, optStr.c_str())) != -1) {
272 switch (optChar) {
273 case '?':
274 erc = true;
275 break;
276 default:
277 if (ArgNeedsValue(optChar)) {
278 if (Set(optChar, optarg)) { _gotArgs.insert(optChar); }
279 else { erc = true; }
280 }
281 else {
282 if (Set(optChar, true)) { _gotArgs.insert(optChar); }
283 else { erc = true; }
284 }
285 KillConflicts(optChar);
286 break;
287 }
288 }
289 if ((! erc) && GotRequiredArgs() && (! GotConflictingArgs())) {
290 return optind;
291 }
292 else {
293 return -1;
294 }
295 }
296
297 //------------------------------------------------------------------------
299 //------------------------------------------------------------------------
300 static constexpr auto Indexes()
301 {
302 return _indexes;
303 }
304
305 //------------------------------------------------------------------------
307 //------------------------------------------------------------------------
308 std::string OptString() const
309 {
310 std::string rc;
311 for (auto i = 0; i < _indexes.size(); ++i) {
312 if (isprint(_indexes.at(i).first)) {
313 rc += _indexes.at(i).first;
314 if (_indexes.at(i).second) {
315 rc += ':';
316 }
317 }
318 }
319 return rc;
320 }
321
322 //------------------------------------------------------------------------
324 //------------------------------------------------------------------------
325 std::set<std::string> UsageGroups() const
326 {
327 namespace Tuples = Dwm::TupleHelpers;
328
329 std::set<std::string> rc;
330 std::string sep;
331 std::set<std::pair<bool,std::set<std::string>>> groups;
332 Tuples::ForEach(_args,
333 [&sep,&groups,this] (auto e)
334 {
335 std::set<std::string> ss;
336 ss.insert(e.UsageString());
337 for (auto conflict : e.Conflicts()) {
338 ss.insert(UsageString(conflict));
339 }
340 groups.insert(std::make_pair(e.Required(), ss));
341 });
342 for (const auto & gr : groups) {
343 std::string grstr;
344 sep.clear();
345 if (! gr.first) {
346 grstr += '[';
347 }
348 for (const auto & s : gr.second) {
349 grstr += sep;
350 grstr += s;
351 sep = "|";
352 }
353 if (! gr.first) {
354 grstr += ']';
355 }
356 rc.insert(grstr);
357 }
358 return rc;
359 }
360
361 //------------------------------------------------------------------------
363 //------------------------------------------------------------------------
364 std::string UsageString() const
365 {
366 namespace Tuples = Dwm::TupleHelpers;
367
368 std::string rc;
369 std::string sep;
370
371 std::set<std::pair<bool,std::set<std::string>>> groups;
372 Tuples::ForEach(_args,
373 [&sep,&rc,&groups,this] (auto e)
374 {
375 std::set<std::string> ss;
376 ss.insert(e.UsageString());
377 for (auto conflict : e.Conflicts()) {
378 ss.insert(UsageString(conflict));
379 }
380 groups.insert(std::make_pair(e.Required(), ss));
381 });
382 for (const auto & gr : groups) {
383 sep.clear();
384 if (! gr.first) {
385 rc += '[';
386 }
387 for (const auto & s : gr.second) {
388 rc += sep;
389 rc += s;
390 sep = "|";
391 }
392 if (! gr.first) {
393 rc += ']';
394 }
395 rc += ' ';
396 }
397 return rc;
398 }
399
400 //------------------------------------------------------------------------
402 //------------------------------------------------------------------------
403 std::string HelpText() const
404 {
405 namespace Tuples = Dwm::TupleHelpers;
406
407 std::string rc;
408 Tuples::ForEach(_args,
409 [&rc,this] (auto e)
410 {
411 rc += e.HelpText() + '\n';
412 if (! e.Conflicts().empty()) {
413 std::string sep;
414 rc += "\t[conflicts with ";
415 for (auto conflict : e.Conflicts()) {
416 rc += sep;
417 rc += '-';
418 rc += conflict;
419 sep = ", ";
420 }
421 rc += "]\n";
422 }
423 rc += '\n';
424 });
425 return rc;
426 }
427
428 //------------------------------------------------------------------------
430 //------------------------------------------------------------------------
431 std::string Usage(const std::string & argv0,
432 const std::string & tailArgs = "") const
433 {
434 std::ostringstream os;
435
436 auto groups = PrintableUsageGroups();
437 size_t indent = argv0.size() + std::string("Usage: ").size() + 1;
438
439 os << "Usage: " << argv0 << ' ';
440 int col = indent;
441 for (const auto & gr : groups) {
442 if (col + gr.size() + 1 > 76) {
443 os << '\n' << std::setw(indent) << ' ';
444 col = indent;
445 }
446 os << gr << ' ';
447 col += gr.size() + 1;
448 }
449 if (tailArgs.size() + col > 76) {
450 os << '\n' << std::setw(indent) << ' ';
451 }
452 os << tailArgs << '\n' << '\n';
453 os << HelpText();
454 return os.str();
455 }
456
457 //------------------------------------------------------------------------
459 //------------------------------------------------------------------------
460 template <char C>
461 auto & Get() const
462 {
463 return std::get<Index(C)>(_args).Value();
464 }
465
466 //------------------------------------------------------------------------
468 //------------------------------------------------------------------------
469 template <char C>
470 auto & Get()
471 {
472 return std::get<Index(C)>(_args).Value();
473 }
474
475 //------------------------------------------------------------------------
477 //------------------------------------------------------------------------
478 template <char C, typename V>
479 void Set(const V & v)
480 {
481 std::get<Index(C)>(_args).Value(v);
482 return;
483 }
484
485 //------------------------------------------------------------------------
487 //------------------------------------------------------------------------
488 template <char C>
489 void SetHelp(const std::string & help)
490 {
491 std::get<Index(C)>(_args).Help(help);
492 return;
493 }
494
495 //------------------------------------------------------------------------
497 //------------------------------------------------------------------------
498 template <char C>
499 std::string GetHelp() const
500 {
501 return std::get<Index(C)>(_args).Help();
502 }
503
504 //------------------------------------------------------------------------
506 //------------------------------------------------------------------------
507 template <char C>
508 std::string GetHelpText() const
509 {
510 return std::get<Index(C)>(_args).HelpText();
511 }
512
513 //------------------------------------------------------------------------
515 //------------------------------------------------------------------------
516 template <char C>
517 void SetValueName(const std::string & valueName)
518 {
519 std::get<Index(C)>(_args).ValueName(valueName);
520 return;
521 }
522
523 //------------------------------------------------------------------------
525 //------------------------------------------------------------------------
526 void SetConflicts(std::vector<std::set<char>> confs)
527 {
528 for (const auto & c : confs) {
529 SetConflicts1(c);
530 }
531 return;
532 }
533
534 //------------------------------------------------------------------------
536 //------------------------------------------------------------------------
537 bool LoadFromEnvironment(const char *envVar)
538 {
539 bool rc = false;
540 if (envVar) {
541 using std::regex, std::sregex_iterator, std::regex_search,
542 std::smatch, std::regex_match;
543 const char *envVal = getenv(envVar);
544 if (envVal) {
545 rc = true;
546 std::string envStr(envVal);
547 regex rgx("[^:]+", regex::ECMAScript|regex::optimize);
548 regex rgx1("^[^=]", regex::ECMAScript|regex::optimize);
549 regex rgx2("^([^=])[=](.+)$", regex::ECMAScript|regex::optimize);
550 auto vbeg = sregex_iterator(envStr.begin(), envStr.end(), rgx);
551 auto vend = sregex_iterator();
552 for (auto it = vbeg; it != vend; ++it) {
553 smatch match;
554 std::string itstr(it->str());
555 if (regex_match(itstr, match, rgx2)) {
556 if (match.size() == 3) {
557 if (! Set(match[0].str()[0], match[2].str())) {
558 rc = false;
559 break;
560 }
561 }
562 }
563 else if (regex_match(itstr, match, rgx1)) {
564 if (match.size() == 1) {
565 if (! Set(match[0].str()[0], true)) {
566 rc = false;
567 break;
568 }
569 }
570 }
571 else {
572 rc = false;
573 break;
574 }
575 }
576 }
577 }
578 return rc;
579 }
580
581 private:
582 static constexpr const std::array<std::pair<char,bool>, std::tuple_size<std::tuple<Ts...>>::value> _indexes = {std::pair<char,bool>(Ts::OptChar(),Ts::NeedsValue())...};
583
584 std::tuple<Ts...> _args;
585 std::set<char> _gotArgs;
586
587 //------------------------------------------------------------------------
589 //------------------------------------------------------------------------
590 static constexpr int Index(char c)
591 {
592 for (auto i = 0; i < _indexes.size(); ++i) {
593 if (_indexes.at(i).first == c) {
594 return i;
595 }
596 }
597 return -1;
598 }
599
600 //------------------------------------------------------------------------
602 //------------------------------------------------------------------------
603 std::string UsageString(char c) const
604 {
605 namespace Tuples = Dwm::TupleHelpers;
606
607 std::string usagestr;
608 auto idx = Tuples::FindIf(_args,
609 [c](auto n)
610 { return n.OptChar() == c; });
611 Tuples::Perform(_args, idx,
612 [&usagestr] (auto n)
613 { usagestr = n.UsageString(); });
614 return usagestr;
615 }
616
617 //------------------------------------------------------------------------
619 //------------------------------------------------------------------------
620 void SetConflicts1(std::set<char> conflicts)
621 {
622 namespace Tuples = Dwm::TupleHelpers;
623
624 for (const auto conflict : conflicts) {
625 auto sc = conflicts;
626 sc.erase(conflict);
627 auto idx = Tuples::FindIf(_args,
628 [conflict] (auto n)
629 { return n.OptChar() == conflict; });
630 Tuples::Perform(_args, idx,
631 [&sc] (auto & n) { n.Conflicts(sc); });
632 }
633 return;
634 }
635
636 //------------------------------------------------------------------------
638 //------------------------------------------------------------------------
639 constexpr bool ArgNeedsValue(char c) const
640 {
641 namespace Tuples = Dwm::TupleHelpers;
642
643 bool rc = false;
644 auto idx = Tuples::FindIf(_args,
645 [c] (auto n) { return n.OptChar() == c; });
646 Tuples::Perform(_args, idx,
647 [&rc] (auto && n)
648 {
649 if (n.NeedsValue()) {
650 rc = true;
651 }
652 });
653 return rc;
654 }
655
656 //------------------------------------------------------------------------
658 //------------------------------------------------------------------------
659 template <typename U>
660 bool Set(char c, const U u)
661 {
662 namespace Tuples = Dwm::TupleHelpers;
663 using namespace ArgumentIO;
664
665 bool rc = false;
666 auto idx = Tuples::FindIf(_args,
667 [c] (auto n) { return n.OptChar() == c; });
668 Tuples::Perform(_args, idx,
669 [&] (auto & n) {
670 std::ostringstream os;
671 os << u;
672 std::istringstream is(os.str());
673 if (is >> n.Value()) {
674 rc = true;
675 }
676 });
677 return rc;
678 }
679
680 //------------------------------------------------------------------------
682 //------------------------------------------------------------------------
683 bool Set(char c, std::string s)
684 {
685 namespace Tuples = Dwm::TupleHelpers;
686 using namespace ArgumentIO;
687
688 std::istringstream iss(s);
689 bool rc = false;
690 auto idx = Tuples::FindIf(_args,
691 [c] (auto n) { return n.OptChar() == c; });
692 Tuples::Perform(_args, idx,
693 [&] (auto & n) {
694 if (iss >> n.Value()) {
695 rc = true;
696 }
697 });
698 return rc;
699 }
700
701 //------------------------------------------------------------------------
703 //------------------------------------------------------------------------
704 void KillConflicts(char c)
705 {
706 namespace Tuples = Dwm::TupleHelpers;
707 std::set<char> conflicts = GetConflicts(c);
708 for (auto conflict : conflicts) {
709 if (_gotArgs.find(conflict) == _gotArgs.end()) {
710 auto idx = Tuples::FindIf(_args,
711 [conflict] (auto n)
712 { return n.OptChar() == conflict; });
713 Tuples::Perform(_args, idx,
714 [&] (auto & n)
715 {
716 if constexpr (std::is_same<std::remove_reference_t<decltype(n.Value())>,bool>::value) {
717 n.Value(false);
718 }
719 else {
720 n.Value(std::remove_reference_t<decltype(n.Value())>());
721 }
722 });
723 }
724 }
725 return;
726 }
727
728 //------------------------------------------------------------------------
730 //------------------------------------------------------------------------
731 std::set<char> GetConflicts(char c)
732 {
733 namespace Tuples = Dwm::TupleHelpers;
734 std::set<char> rc;
735 auto idx = Tuples::FindIf(_args,
736 [c] (auto n) { return n.OptChar() == c; });
737 Tuples::Perform(_args, idx,
738 [&] (auto & n) {
739 rc = n.Conflicts();
740 });
741 return rc;
742 }
743
744 //------------------------------------------------------------------------
746 //------------------------------------------------------------------------
747 bool GotRequiredArgs()
748 {
749 namespace Tuples = Dwm::TupleHelpers;
750 bool rc = true;
751 bool foundConflict = false;
752 Tuples::ForEach(_args,
753 [&rc, &foundConflict, this] (auto e)
754 { if (e.Required()
755 && (_gotArgs.find(e.OptChar())
756 == _gotArgs.end())) {
757 for (auto c : e.Conflicts()) {
758 if (_gotArgs.find(c) != _gotArgs.end()) {
759 foundConflict = true;
760 break;
761 }
762 }
763 if (! foundConflict) {
764 rc = false;
765 }
766 }
767 });
768 return rc;
769 }
770
771 //------------------------------------------------------------------------
773 //------------------------------------------------------------------------
774 bool GotConflictingArgs()
775 {
776 namespace Tuples = Dwm::TupleHelpers;
777 bool rc = false;
778 Tuples::ForEach(_args,
779 [&rc, this] (auto e)
780 {
781 if (_gotArgs.find(e.OptChar())
782 != _gotArgs.end()) {
783 for (auto c : e.Conflicts()) {
784 if (_gotArgs.find(c)
785 != _gotArgs.end()) {
786 rc = true;
787 break;
788 }
789 }
790 }
791 });
792 return rc;
793 }
794
795 //------------------------------------------------------------------------
797 //------------------------------------------------------------------------
798 std::set<std::string> PrintableUsageGroups() const
799 {
800 namespace Tuples = Dwm::TupleHelpers;
801 std::set<std::string> rc;
802 std::string sep;
803 std::set<std::pair<bool,std::set<std::string>>> groups;
804 Tuples::ForEach(_args,
805 [&sep,&groups,this] (auto e)
806 {
807 if (isprint(e.OptChar())) {
808 std::set<std::string> ss;
809 ss.insert(e.UsageString());
810 for (auto conflict : e.Conflicts()) {
811 ss.insert(UsageString(conflict));
812 }
813 groups.insert(std::make_pair(e.Required(), ss));
814 }
815 });
816 for (const auto & gr : groups) {
817 std::string grstr;
818 sep.clear();
819 if (! gr.first) {
820 grstr += '[';
821 }
822 for (const auto & s : gr.second) {
823 grstr += sep;
824 grstr += s;
825 sep = "|";
826 }
827 if (! gr.first) {
828 grstr += ']';
829 }
830 rc.insert(grstr);
831 }
832 return rc;
833 }
834
835 };
836
837} // namespace Dwm
838
839
840//---------------------------- emacs settings -----------------------------
841// Local Variables:
842// mode: C++
843// tab-width: 2
844// indent-tabs-mode: nil
845// c-basic-offset: 2
846// End:
847//-------------------------------------------------------------------------
iostream helpers for Argument values