1 #include "metacommands.h"
2 
3 #include <algorithm>
4 #include <cerrno>
5 #include <cstdlib>
6 #include <cstring>
7 #include <functional>
8 #include <iostream>
9 #include <map>
10 #include <memory>
11 
12 #include "argparse.h"
13 #include "attribute_.h"
14 #include "command.h"
15 #include "completion.h"
16 #include "finite.h"
17 #include "ipc-protocol.h"
18 
19 using std::endl;
20 using std::function;
21 using std::pair;
22 using std::string;
23 using std::stringstream;
24 using std::unique_ptr;
25 using std::vector;
26 
27 extern char** environ;
28 
MetaCommands(Object & root_)29 MetaCommands::MetaCommands(Object& root_) : root(root_) {
30 }
31 
get_attr_cmd(Input in,Output output)32 int MetaCommands::get_attr_cmd(Input in, Output output) {
33     string attrName;
34     if (!(in >> attrName)) {
35         return HERBST_NEED_MORE_ARGS;
36     }
37 
38     Attribute* a = getAttribute(attrName, output);
39     if (!a) {
40         return HERBST_INVALID_ARGUMENT;
41     }
42     output << a->str();
43     return 0;
44 }
45 
set_attr_cmd(Input in,Output output)46 int MetaCommands::set_attr_cmd(Input in, Output output) {
47     string path, new_value;
48     if (!(in >> path >> new_value)) {
49         return HERBST_NEED_MORE_ARGS;
50     }
51 
52     Attribute* a = getAttribute(path, output);
53     if (!a) {
54         return HERBST_INVALID_ARGUMENT;
55     }
56     string error_message = a->change(new_value);
57     if (error_message.empty()) {
58         return 0;
59     } else {
60         output << in.command() << ": \""
61             << new_value << "\" is not a valid value for "
62             << a->name() << ": "
63             << error_message << endl;
64         return HERBST_INVALID_ARGUMENT;
65     }
66 }
67 
attr_cmd(Input in,Output output)68 int MetaCommands::attr_cmd(Input in, Output output) {
69     string path = "", new_value = "";
70     in >> path >> new_value;
71     std::ostringstream dummy_output;
72     Object* o = &root;
73     auto p = Path::split(path);
74     if (!p.empty()) {
75         while (p.back().empty()) {
76             p.pop_back();
77         }
78         o = o->child(p);
79     }
80     if (o && new_value.empty()) {
81         o->ls(output);
82         return 0;
83     }
84 
85     Attribute* a = getAttribute(path, output);
86     if (!a) {
87         return HERBST_INVALID_ARGUMENT;
88     }
89     if (new_value.empty()) {
90         // no more arguments -> return the value
91         output << a->str();
92         return 0;
93     } else {
94         // another argument -> set the value
95         string error_message = a->change(new_value);
96         if (error_message.empty()) {
97             return 0;
98         } else {
99             output << in.command() << ": \""
100                 << new_value << "\" is not a valid value for "
101                 << a->name() << ": "
102                 << error_message << endl;
103             return HERBST_INVALID_ARGUMENT;
104         }
105     }
106 }
107 
getAttribute(string path,Output output)108 Attribute* MetaCommands::getAttribute(string path, Output output) {
109     auto attr_path = Object::splitPath(path);
110     auto child = root.child(attr_path.first);
111     if (!child) {
112         output << "No such object " << attr_path.first.join('.') << endl;
113         return nullptr;
114     }
115     Attribute* a = child->attribute(attr_path.second);
116     if (!a) {
117         auto object_path = attr_path.first.join('.');
118         if (object_path.empty()) {
119             object_path = "The root object";
120         } else {
121             // equip object_path with quotes
122             object_path = "Object \"" + object_path + "\"";
123         }
124         output << object_path
125                << " has no attribute \"" << attr_path.second << "\""
126                << endl;
127         return nullptr;
128     }
129     return a;
130 }
131 
print_object_tree_command(Input in,Output output)132 int MetaCommands::print_object_tree_command(Input in, Output output) {
133     auto path = Path(in.empty() ? string("") : in.front()).toVector();
134     while (!path.empty() && path.back().empty()) {
135         path.pop_back();
136     }
137     auto child = root.child(path);
138     if (!child) {
139         output << "No such object " << Path(path).join('.') << endl;
140         return HERBST_INVALID_ARGUMENT;
141     }
142     child->printTree(output, Path(path).join('.'));
143     return 0;
144 }
145 
print_object_tree_complete(Completion & complete)146 void MetaCommands::print_object_tree_complete(Completion& complete) {
147     if (complete == 0) {
148         completeObjectPath(complete);
149     } else {
150         complete.none();
151     }
152 }
153 
154 
substitute_cmd(Input input,Output output)155 int MetaCommands::substitute_cmd(Input input, Output output)
156 {
157     string ident, path;
158     if (!(input >> ident >> path )) {
159         return HERBST_NEED_MORE_ARGS;
160     }
161     Attribute* a = getAttribute(path, output);
162     if (!a) {
163         return HERBST_INVALID_ARGUMENT;
164     }
165 
166     auto carryover = input.fromHere();
167     carryover.replace(ident, a->str());
168     return Commands::call(carryover, output);
169 }
170 
substitute_complete(Completion & complete)171 void MetaCommands::substitute_complete(Completion& complete)
172 {
173     if (complete == 0) {
174         // no completion for the identifier
175     } else if (complete == 1) {
176         completeAttributePath(complete);
177     } else {
178         // later, complete the identifier
179         complete.full(complete[0]);
180         complete.completeCommands(2);
181     }
182 }
183 
foreachCmd(Input input,Output output)184 int MetaCommands::foreachCmd(Input input, Output output)
185 {
186     string ident, pathString;
187     if (!(input >> ident >> pathString)) {
188         return HERBST_NEED_MORE_ARGS;
189     }
190     // remove trailing dots to avoid parsing issues:
191     while (!pathString.empty() && pathString.back() == OBJECT_PATH_SEPARATOR) {
192         // while the last character is a dot, erase it:
193         pathString.erase(pathString.size() - 1);
194     }
195     Path path { pathString, OBJECT_PATH_SEPARATOR };
196     Object* object = root.child(path, output);
197     if (!object) {
198         return HERBST_INVALID_ARGUMENT;
199     }
200 
201     // collect the paths of all children of this object
202     vector<string> childPaths;
203     // collect the children's names first to ensure that
204     // object->children() is not changed by the commands we are
205     // calling.
206     if (!pathString.empty()) {
207         pathString += OBJECT_PATH_SEPARATOR;
208     }
209     for (const auto& entry : object->children()) {
210         childPaths.push_back(pathString + entry.first);
211     }
212     int  lastStatusCode = 0;
213     for (const auto& child : childPaths) {
214         Input carryover = input.fromHere();
215         carryover.replace(ident, child);
216         lastStatusCode = Commands::call(carryover, output);
217     }
218     return lastStatusCode;
219 }
220 
foreachComplete(Completion & complete)221 void MetaCommands::foreachComplete(Completion& complete)
222 {
223     if (complete == 0) {
224         // no completion for the identifier
225     } else if (complete == 1) {
226         completeObjectPath(complete);
227     } else {
228         // later, complete the identifier
229         complete.full(complete[0]);
230         complete.completeCommands(2);
231     }
232 }
233 
234 //! parse a format string or throw an exception
parseFormatString(const string & format)235 MetaCommands::FormatString MetaCommands::parseFormatString(const string &format)
236 {
237     FormatString blobs;
238     size_t lastpos = 0; // the position where the last plaintext blob started
239     for (size_t i = 0; i < format.size(); i++) {
240         if (format[i] == '%') {
241             if (i + 1 >= format.size()) {
242                 throw std::invalid_argument(
243                     "dangling % at the end of format \"" + format + "\"");
244             } else {
245                 if (i > lastpos) {
246                     // add literal text blob
247                     blobs.push_back({ true, format.substr(lastpos, i - lastpos)});
248                 }
249                 char format_type = format[i+1];
250                 lastpos = i + 2;
251                 i++; // also consume the format_type
252                 if (format_type == '%') {
253                     blobs.push_back({true, "%"});
254                 } else if (format_type == 's') {
255                     blobs.push_back({false, "s"});
256                 } else if (format_type == 'c') {
257                     blobs.push_back({false, "c"});
258                 } else {
259                     stringstream msg;
260                     msg << "invalid format type %"
261                         << format_type << " at position "
262                         << i << " in format string \""
263                         << format << "\"";
264                     throw std::invalid_argument(msg.str());
265                 }
266             }
267         }
268     }
269     if (lastpos < format.size()) {
270         blobs.push_back({true, format.substr(lastpos, format.size()-lastpos)});
271     }
272     return blobs;
273 }
274 
sprintf_cmd(Input input,Output output)275 int MetaCommands::sprintf_cmd(Input input, Output output)
276 {
277     string ident, formatStringSrc;
278     if (!(input >> ident >> formatStringSrc)) {
279         return HERBST_NEED_MORE_ARGS;
280     }
281     FormatString format;
282     try {
283         format = parseFormatString(formatStringSrc);
284     }  catch (const std::invalid_argument& e) {
285         output << input.command() << ": " << e.what() << endl;
286         return HERBST_INVALID_ARGUMENT;
287     }
288     // evaluate placeholders in the format string
289     string replacedString = "";
290     for (const auto& blob : format) {
291         if (blob.literal_) {
292             replacedString += blob.data_;
293         } else if (blob.data_ == "c") {
294             // a constant string argument
295             string constString;
296             if (!(input >> constString)) {
297                 return HERBST_NEED_MORE_ARGS;
298             }
299             // just copy the plain string to the output
300             replacedString += constString;
301         } else {
302             // we reached %s
303             string path;
304             if (!(input >> path)) {
305                 return HERBST_NEED_MORE_ARGS;
306             }
307             Attribute* a = getAttribute(path, output);
308             if (!a) {
309                 return HERBST_INVALID_ARGUMENT;
310             }
311             replacedString += a->str();
312         }
313     }
314     auto carryover = input.fromHere();
315     carryover.replace(ident, replacedString);
316     return Commands::call(carryover, output);
317 }
318 
sprintf_complete(Completion & complete)319 void MetaCommands::sprintf_complete(Completion& complete)
320 {
321     if (complete == 0) {
322         // no completion for arg name
323     } else if (complete == 1) {
324         // no completion for format string
325     } else {
326         FormatString fs;
327         try {
328             fs = parseFormatString(complete[1]);
329         }  catch (const std::invalid_argument&) {
330             complete.invalidArguments();
331             return;
332         }
333         int indexOfNextArgument = 2;
334         for (const auto& b : fs) {
335             if (b.literal_ == true) {
336                 continue;
337             }
338             if (complete == indexOfNextArgument) {
339                 if (b.data_ == "s") {
340                     completeAttributePath(complete);
341                 } else if (b.data_ == "c") {
342                     // no completion for constant strings
343                 }
344             }
345             indexOfNextArgument++;
346         }
347         if (complete >= indexOfNextArgument) {
348             complete.full(complete[0]); // complete string replacement
349             complete.completeCommands(indexOfNextArgument);
350         }
351     }
352 }
353 
354 static std::map<string, function<Attribute*(string)>> name2constructor {
__anon52be4b550102(string n) 355     { "bool",  [](string n) { return new Attribute_<bool>(n, false); }},
__anon52be4b550202(string n) 356     { "color", [](string n) { return new Attribute_<Color>(n, {"black"}); }},
__anon52be4b550302(string n) 357     { "int",   [](string n) { return new Attribute_<int>(n, 0); }},
__anon52be4b550402(string n) 358     { "string",[](string n) { return new Attribute_<string>(n, ""); }},
__anon52be4b550502(string n) 359     { "uint",  [](string n) { return new Attribute_<unsigned long>(n, 0); }},
360 };
361 
newAttributeWithType(string typestr,string attr_name,Output output)362 Attribute* MetaCommands::newAttributeWithType(string typestr, string attr_name, Output output) {
363     auto it = name2constructor.find(typestr);
364     if (it == name2constructor.end()) {
365         output << "error: unknown type \"" << typestr << "\"";
366         return nullptr;
367     }
368     auto attr = it->second(attr_name);
369     attr->setWritable(true);
370     return attr;
371 }
372 
completeAttributeType(Completion & complete)373 void MetaCommands::completeAttributeType(Completion& complete)
374 {
375     for (const auto& t : name2constructor) {
376         complete.full(t.first);
377     }
378 }
379 
380 
381 
new_attr_cmd(Input input,Output output)382 int MetaCommands::new_attr_cmd(Input input, Output output)
383 {
384     string type, path, initialValue;
385     bool initialValueSupplied = false;
386     ArgParse ap;
387     ap.mandatory(type).mandatory(path);
388     ap.optional(initialValue, &initialValueSupplied);
389     if (ap.parsingFails(input, output)) {
390         return HERBST_NEED_MORE_ARGS;
391     }
392     auto obj_path_and_attr = Object::splitPath(path);
393     string attr_name = obj_path_and_attr.second;
394     Object* obj = root.child(obj_path_and_attr.first, output);
395     if (!obj) {
396         return HERBST_INVALID_ARGUMENT;
397     }
398     if (attr_name.substr(0,strlen(USER_ATTRIBUTE_PREFIX)) != USER_ATTRIBUTE_PREFIX) {
399         output
400             << input.command() << ": attribute name must start with \""
401             << USER_ATTRIBUTE_PREFIX << "\""
402             << " but is actually \"" << attr_name << "\"" << endl;
403         return HERBST_INVALID_ARGUMENT;
404     }
405     if (obj->attribute(attr_name)) {
406         output
407             << input.command() << ": object \"" << obj_path_and_attr.first.join()
408             << "\" already has an attribute named \"" << attr_name
409             <<  "\"" << endl;
410         return HERBST_INVALID_ARGUMENT;
411     }
412     // create the new attribute and add it
413     Attribute* a = newAttributeWithType(type, attr_name, output);
414     if (!a) {
415         return HERBST_INVALID_ARGUMENT;
416     }
417     obj->addAttribute(a);
418     userAttributes_.push_back(unique_ptr<Attribute>(a));
419     // try to write the attribute
420     if (initialValueSupplied) {
421         string msg = a->change(initialValue);
422         if (!msg.empty()) {
423             output << input.command() << ": \""
424                    << initialValue << "\" is an invalid "
425                    << "value for " << path << ": " << msg << endl;
426             return HERBST_INVALID_ARGUMENT;
427         }
428     }
429     return 0;
430 }
431 
new_attr_complete(Completion & complete)432 void MetaCommands::new_attr_complete(Completion& complete)
433 {
434     if (complete == 0) {
435         completeAttributeType(complete);
436     } else if (complete == 1) {
437         completeObjectPath(complete, false);
438         auto obj_path = Object::splitPath(complete[1]).first;
439         if (obj_path.empty()) {
440             complete.partial(USER_ATTRIBUTE_PREFIX);
441         } else {
442             char s[] = {OBJECT_PATH_SEPARATOR, '\0'};
443             complete.partial(obj_path.join(OBJECT_PATH_SEPARATOR) + s + USER_ATTRIBUTE_PREFIX);
444         }
445     } else if (complete == 2) {
446         // no completion for the initial value
447     } else {
448         complete.none();
449     }
450 }
451 
remove_attr_cmd(Input input,Output output)452 int MetaCommands::remove_attr_cmd(Input input, Output output)
453 {
454     string path;
455     if (!(input >> path )) {
456         return HERBST_NEED_MORE_ARGS;
457     }
458     Attribute* a = root.deepAttribute(path, output);
459     if (!a) {
460         return HERBST_INVALID_ARGUMENT;
461     }
462     if (a->name().substr(0,strlen(USER_ATTRIBUTE_PREFIX)) != USER_ATTRIBUTE_PREFIX) {
463         output << input.command() << ": Cannot remove built-in attribute \"" << path << "\"" << endl;
464         return HERBST_INVALID_ARGUMENT;
465     }
466     a->detachFromOwner();
467     userAttributes_.erase(
468         std::remove_if(
469             userAttributes_.begin(),
470             userAttributes_.end(),
471             [a](const unique_ptr<Attribute>& p) { return p.get() == a; }),
472         userAttributes_.end());
473     return 0;
474 }
475 
remove_attr_complete(Completion & complete)476 void MetaCommands::remove_attr_complete(Completion& complete) {
477     if (complete == 0) {
478         completeObjectPath(complete, true, [] (Attribute* a) {
479             return a->name().substr(0,strlen(USER_ATTRIBUTE_PREFIX))
480                     == USER_ATTRIBUTE_PREFIX;
481         });
482     } else {
483         complete.none();
484     }
485 }
486 
do_comparison(const T & a,const T & b)487 template <typename T> int do_comparison(const T& a, const T& b) {
488     return (a == b) ? 0 : 1;
489 }
490 
do_comparison(const int & a,const int & b)491 template <> int do_comparison<int>(const int& a, const int& b) {
492     if (a < b) {
493         return -1;
494     } else if (a > b) {
495         return 1;
496     } else {
497         return 0;
498     }
499 }
do_comparison(const unsigned long & a,const unsigned long & b)500 template <> int do_comparison<unsigned long>(const unsigned long& a, const unsigned long& b) {
501     if (a < b) {
502         return -1;
503     } else if (a > b) {
504         return 1;
505     } else {
506         return 0;
507     }
508 }
509 
parse_and_compare(string a,string b,Output o)510 template <typename T> int parse_and_compare(string a, string b, Output o) {
511     vector<T> vals;
512     for (auto &x : {a, b}) {
513         try {
514             vals.push_back(Converter<T>::parse(x));
515         } catch(std::exception& e) {
516             o << "cannot parse \"" << x << "\" to "
517               << typeid(T).name() << ": " << e.what() << endl;
518             return (int) HERBST_INVALID_ARGUMENT;
519         }
520     }
521     return do_comparison<T>(vals[0], vals[1]);
522 }
523 
524 //! operators of the 'compare' command
525 using CompareOperator = pair<bool, vector<int> >;
526 
527 template <>
528 struct is_finite<CompareOperator> : std::true_type {};
529 template<> Finite<CompareOperator>::ValueList Finite<CompareOperator>::values = {
530     // map operator names to "for numeric types only" and possible return codes
531     { { false, { 0 }, }, "=", },
532     { { false, { -1, 1 } }, "!=", },
533     { { true, { 1, 0 } }, "ge", },
534     { { true, { 1 } }, "gt", },
535     { { true, { -1, 0 } }, "le", },
536     { { true, { -1 } }, "lt", },
537 };
538 
compare_cmd(Input input,Output output)539 int MetaCommands::compare_cmd(Input input, Output output)
540 {
541     string path, value;
542     CompareOperator oper;
543     ArgParse ap = ArgParse().mandatory(path).mandatory(oper).mandatory(value);
544     if (ap.parsingFails(input, output)) {
545         return ap.exitCode();
546     }
547     Attribute* a = root.deepAttribute(path, output);
548     if (!a) {
549         return HERBST_INVALID_ARGUMENT;
550     }
551     // the following compare functions returns
552     //   -1 if the first value is smaller
553     //    1 if the first value is greater
554     //    0 if the the values match
555     //    HERBST_INVALID_ARGUMENT if there was a parsing error
556     std::map<Type, pair<bool, function<int(string,string,Output)>>> type2compare {
557         // map a type name to "is it numeric" and a comperator function
558         { Type::INT,      { true,  parse_and_compare<int> }, },
559         { Type::ULONG,    { true,  parse_and_compare<int> }, },
560         { Type::BOOL,     { false, parse_and_compare<bool> }, },
561         { Type::COLOR,    { false, parse_and_compare<Color> }, },
562     };
563     // the default comparison is simply string based:
564     pair<bool, function<int(string,string,Output)>> comparator =
565         { false, parse_and_compare<string> };
566     auto it = type2compare.find(a->type());
567     if (it != type2compare.end()) {
568         comparator = it->second;
569     }
570     if (oper.first && !comparator.first) {
571         output << "operator \"" << Converter<CompareOperator>::str(oper) << "\" "
572             << "only allowed for numeric types, but the attribute "
573             << path << " is of non-numeric type "
574             << Entity::typestr(a->type()) << endl;
575         return HERBST_INVALID_ARGUMENT;
576     }
577     int comparison_result = comparator.second(a->str(), value, output);
578     if (comparison_result > 1) {
579         return comparison_result;
580     }
581     vector<int>& possible_values = oper.second;
582     auto found = std::find(possible_values.begin(),
583                            possible_values.end(),
584                            comparison_result);
585     return (found == possible_values.end()) ? 1 : 0;
586 }
587 
compare_complete(Completion & complete)588 void MetaCommands::compare_complete(Completion &complete) {
589     if (complete == 0) {
590         completeAttributePath(complete);
591     } else if (complete == 1) {
592         Converter<CompareOperator>::complete(complete);
593     } else if (complete == 2) {
594         // no completion suggestions for the 'value' field
595     } else {
596         complete.none();
597     }
598 }
599 
600 
completeObjectPath(Completion & complete,Object * rootObject,bool attributes,function<bool (Attribute *)> attributeFilter)601 void MetaCommands::completeObjectPath(Completion& complete, Object* rootObject,
602                                       bool attributes,
603                                       function<bool(Attribute*)> attributeFilter)
604 {
605     ArgList objectPathArgs = std::get<0>(Object::splitPath(complete.needle()));
606     string objectPath = objectPathArgs.join(OBJECT_PATH_SEPARATOR);
607     if (!objectPath.empty()) {
608         objectPath += OBJECT_PATH_SEPARATOR;
609     }
610     Object* object = rootObject->child(objectPathArgs);
611     if (!object) {
612         return;
613     }
614     if (attributes) {
615         for (auto& it : object->attributes()) {
616             if (attributeFilter && !attributeFilter(it.second)) {
617                 continue;
618             }
619             complete.full(objectPath + it.first);
620         }
621     }
622     for (auto& it : object->children()) {
623         complete.partial(objectPath + it.first + OBJECT_PATH_SEPARATOR);
624     }
625 }
626 
completeObjectPath(Completion & complete,bool attributes,function<bool (Attribute *)> attributeFilter)627 void MetaCommands::completeObjectPath(Completion& complete, bool attributes,
628                                       function<bool(Attribute*)> attributeFilter)
629 {
630     MetaCommands::completeObjectPath(complete, &root, attributes, attributeFilter);
631 }
632 
completeAttributePath(Completion & complete)633 void MetaCommands::completeAttributePath(Completion& complete) {
634     completeObjectPath(complete, true);
635 }
636 
helpCommand(Input input,Output output)637 int MetaCommands::helpCommand(Input input, Output output)
638 {
639     string needle;
640     ArgParse args = ArgParse().mandatory(needle);
641     if (args.parsingAllFails(input, output)) {
642         return args.exitCode();
643     }
644     function<string(string)> header =
645             [] (const string& text) {
646         string buf = text + "\n";
647         for (size_t i = 0; i < text.size(); i++) {
648             buf += "-";
649         }
650         buf += "\n";
651         return buf;
652     };
653     // drop trailing '.'
654     if (!needle.empty() && *needle.rbegin() == '.') {
655         needle.pop_back();
656     }
657     // split the needle into the path to the owning object
658     // and the name of the entry.
659     auto path = Object::splitPath(needle);
660     Object* parent = root.child(path.first, output);
661     const HasDocumentation* childDoc = nullptr;
662     Object* object = nullptr;
663     if (parent) {
664         object = parent->child(path.second);
665     }
666     if (needle.empty()) {
667         object = &root;
668     }
669     bool helpFound = false;
670     if (parent) {
671         Attribute* attribute = parent->attribute(path.second);
672         if (attribute) {
673             helpFound = true;
674             output << header("Attribute \'" + path.second + "\' of \'"
675                              + path.first.join() + "\'");
676             output << endl; // empty line
677             output << "Type: " << Entity::typestr(attribute->type()) << endl;
678             output << "Current value: " << attribute->str() << endl;
679             output << endl; // empty line
680             const string& doc = attribute->doc();
681             if (!doc.empty()) {
682                 output << doc;
683                 output << endl;
684                 output << endl; // empty line
685             }
686         }
687         // only print documentation on the entry if there
688         // is anything particular to be said.
689         childDoc = parent->childDoc(path.second);
690         if (childDoc && !childDoc->doc().empty()) {
691             helpFound = true;
692             output << header("Entry \'" + path.second + "\' of \'"
693                              + path.first.join() + "\'");
694             output << childDoc->doc() << endl;
695             output << endl; // empty line
696         }
697     }
698     if (object || childDoc) {
699         helpFound = true;
700         output << header("Object \'" + needle + "\'");
701     }
702     if (childDoc && !object) {
703         // if the entry may exist at some time but not at the
704         // moment then make the user aware of this
705         output << "(Entry does not exist at the moment)" << endl;
706     } else if (object) {
707         object->ls(output);
708     }
709     if (!helpFound) {
710         output << "No help found for \'" << needle << "\'" << endl;
711         return HERBST_INVALID_ARGUMENT;
712     }
713     return 0;
714 }
715 
helpCompletion(Completion & complete)716 void MetaCommands::helpCompletion(Completion& complete)
717 {
718     if (complete == 0) {
719         completeAttributePath(complete);
720     } else {
721         complete.none();
722     }
723 }
724 
get_attr_complete(Completion & complete)725 void MetaCommands::get_attr_complete(Completion& complete) {
726     if (complete == 0) {
727         completeAttributePath(complete);
728     } else {
729         complete.none();
730     }
731 }
732 
set_attr_complete(Completion & complete)733 void MetaCommands::set_attr_complete(Completion& complete) {
734     if (complete == 0) {
735         completeObjectPath(complete, true,
736             [](Attribute* a) { return a->writable(); } );
737     } else if (complete == 1) {
738         Attribute* a = root.deepAttribute(complete[0]);
739         if (a) {
740             a->complete(complete);
741         }
742     } else {
743         complete.none();
744     }
745 }
746 
attr_complete(Completion & complete)747 void MetaCommands::attr_complete(Completion& complete)
748 {
749     if (complete == 0) {
750         completeAttributePath(complete);
751     } else if (complete == 1) {
752         Attribute* a = root.deepAttribute(complete[0]);
753         if (!a) {
754             return;
755         }
756         if (a->writable()) {
757             a->complete(complete);
758         } else {
759             complete.none();
760         }
761     } else {
762         complete.none();
763     }
764 }
765 
tryCommand(Input input,Output output)766 int MetaCommands::tryCommand(Input input, Output output) {
767     Commands::call(input.fromHere(), output); // pass output
768     return 0; // ignore exit code
769 }
770 
silentCommand(Input input,Output output)771 int MetaCommands::silentCommand(Input input, Output output) {
772     stringstream dummyOutput;
773     // drop output but pass exit code
774     return Commands::call(input.fromHere(), dummyOutput);
775 }
776 
negateCommand(Input input,Output output)777 int MetaCommands::negateCommand(Input input, Output output)
778 {
779     return ! Commands::call(input.fromHere(), output);
780 }
781 
completeCommandShifted1(Completion & complete)782 void MetaCommands::completeCommandShifted1(Completion& complete) {
783     complete.completeCommands(0);
784 }
785 
echoCommand(Input input,Output output)786 int MetaCommands::echoCommand(Input input, Output output)
787 {
788     string token;
789     bool first = true;
790     while (input >> token) {
791         output << (first ? "" : " ") << token;
792         first = false;
793     }
794     output << endl;
795     return 0;
796 }
797 
798 
setenvCommand(Input input,Output output)799 int MetaCommands::setenvCommand(Input input, Output output) {
800     string name, value;
801     if (!(input >> name >> value)) {
802         return HERBST_NEED_MORE_ARGS;
803     }
804     if (setenv(name.c_str(), value.c_str(), 1) != 0) {
805         output << input.command()
806                << ": Could not set environment variable: "
807                << strerror(errno) << endl;
808         return HERBST_UNKNOWN_ERROR;
809     }
810     return 0;
811 }
812 
setenvCompletion(Completion & complete)813 void MetaCommands::setenvCompletion(Completion& complete) {
814     if (complete == 0) {
815         return completeEnvName(complete);
816     } else if (complete == 1) {
817         // no completion for the value
818     } else {
819         complete.none();
820     }
821 }
822 
823 //! a wraper around setenv with the usual 'export' syntax in posix
exportEnvCommand(Input input,Output output)824 int MetaCommands::exportEnvCommand(Input input, Output output)
825 {
826     string arg;
827     if (!(input >> arg )) {
828         return HERBST_NEED_MORE_ARGS;
829     }
830     auto pos = arg.find("=");
831     if (pos >= arg.size()) {
832         return HERBST_NEED_MORE_ARGS;
833     }
834     // if "=" has been found in the arg, split it there
835     auto newInput = Input(input.command(), {arg.substr(0, pos), arg.substr(pos + 1)});
836     return setenvCommand(newInput, output);
837 }
838 
exportEnvCompletion(Completion & complete)839 void MetaCommands::exportEnvCompletion(Completion &complete)
840 {
841     for (char** env = environ; *env; ++env) {
842         vector<string> chunks = ArgList::split(*env, '=');
843         if (!chunks.empty()) {
844             complete.partial(chunks[0] + "=");
845         }
846     }
847 }
848 
getenvCommand(Input input,Output output)849 int MetaCommands::getenvCommand(Input input, Output output) {
850     string name;
851     if (!(input >> name)) {
852         return HERBST_NEED_MORE_ARGS;
853     }
854     char* envvar = getenv(name.c_str());
855     if (!envvar) {
856         return HERBST_ENV_UNSET;
857     }
858     output << envvar << endl;
859     return 0;
860 }
861 
862 //! completion for unsetenv and getenv
getenvUnsetenvCompletion(Completion & complete)863 void MetaCommands::getenvUnsetenvCompletion(Completion& complete) {
864     if (complete == 0) {
865         return completeEnvName(complete);
866     } else {
867         complete.none();
868     }
869 }
870 
unsetenvCommand(Input input,Output output)871 int MetaCommands::unsetenvCommand(Input input, Output output) {
872     string name;
873     if (!(input >> name)) {
874         return HERBST_NEED_MORE_ARGS;
875     }
876     if (unsetenv(name.c_str()) != 0) {
877         output << input.command()
878                << ": Could not unset environment variable: "
879                << strerror(errno) << endl;
880         return HERBST_UNKNOWN_ERROR;
881     }
882     return 0;
883 }
884 
completeEnvName(Completion & complete)885 void MetaCommands::completeEnvName(Completion& complete) {
886     for (char** env = environ; *env; ++env) {
887         vector<string> chunks = ArgList::split(*env, '=');
888         if (!chunks.empty()) {
889             complete.full(chunks[0]);
890         }
891     }
892 }
893 
chainCommand(Input input,Output output)894 int MetaCommands::chainCommand(Input input, Output output)
895 {
896     vector<vector<string>> commands = splitCommandList(input.toVector());
897     int returnCode = 0;
898     // the condition that has to be fulfilled if we want to continue
899     // execuding commands. the default (for 'chain') is to always continue
900     function<bool(int)> conditionContinue = [](int) { return true; };
901     if (input.command() == "and") {
902         // continue executing commands while they are successful
903         conditionContinue = [](int code) { return code == 0; };
904     }
905     if (input.command() == "or") {
906         returnCode = 1;
907         // continue executing commands while they are failing
908         conditionContinue = [](int code) { return code >= 1; };
909     }
910     for (auto& cmd : commands) {
911         if (cmd.empty()) {
912             // if command range is empty, do nothing
913             continue;
914         }
915         Input cmdinput = Input(cmd[0], cmd.begin() + 1, cmd.end());
916         returnCode = Commands::call(cmdinput, output);
917         if (!conditionContinue(returnCode)) {
918             break;
919         }
920     }
921     return returnCode;
922 }
923 
chainCompletion(Completion & complete)924 void MetaCommands::chainCompletion(Completion& complete)
925 {
926     if (complete == 0) {
927         // no completion for the separator
928     } else {
929         size_t lastsep = 0;
930         for (size_t i = 1; i < complete.needleIndex(); i++) {
931             if (complete[i] == complete[0]) {
932                 lastsep = i;
933             }
934         }
935         if (complete > lastsep + 1) {
936             complete.full(complete[0]);
937         }
938         complete.completeCommands(lastsep + 1);
939     }
940 }
941 
942 
splitCommandList(ArgList::Container input)943 vector<vector<string>> MetaCommands::splitCommandList(ArgList::Container input) {
944     vector<vector<string>> res;
945     if (input.empty()) {
946         return res;
947     }
948     vector<string> current;
949     string separator = input[0];
950     for (size_t i = 1; i < input.size(); i++) {
951         if (input[i] == separator) {
952             res.push_back(current);
953             current = {};
954         } else {
955             current.push_back(input[i]);
956         }
957     }
958     res.push_back(current);
959     return res;
960 }
961 
962