1 #include "command.h"
2 
3 #include <cstdio>
4 #include <cstring>
5 #include <sstream>
6 
7 #include "client.h"
8 #include "completion.h"
9 #include "hlwmcommon.h"
10 #include "ipc-protocol.h"
11 #include "monitor.h"
12 #include "monitormanager.h"
13 #include "root.h"
14 #include "tag.h"
15 #include "utils.h"
16 
17 using std::endl;
18 using std::function;
19 using std::shared_ptr;
20 using std::string;
21 using std::to_string;
22 using std::unique_ptr;
23 using std::vector;
24 
25 static void try_complete(const char* needle, string to_check, Output output);
26 static void try_complete(const char* needle, const char* to_check, Output output);
27 
28 static void complete_against_tags(int argc, char** argv, int pos, Output output);
29 static void complete_against_monitors(int argc, char** argv, int pos, Output output);
30 static void complete_against_winids(int argc, char** argv, int pos, Output output);
31 static void complete_merge_tag(int argc, char** argv, int pos, Output output);
32 static int complete_against_commands(int argc, char** argv, int position, Output output);
33 
34 // if the current completion needs shell quoting and other shell specific
35 // behaviour
36 static bool g_shell_quoting = false;
37 
38 static const char* completion_directions[]    = { "left", "right", "down", "up",nullptr};
39 static const char* completion_special_winids[]= { "urgent", "", nullptr };
40 static const char* completion_use_index_args[]= { "--skip-visible", nullptr };
41 static const char* completion_pm_one[]= { "+1", "-1", nullptr };
42 static const char* completion_split_modes[]= { "horizontal", "vertical", "left", "right", "top", "bottom", "explode", "auto", nullptr };
43 static const char* completion_split_ratios[]= {
44     "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", nullptr };
45 
no_completion(int,char **,int)46 static bool no_completion(int, char**, int) {
47     return false;
48 }
49 
50 static bool first_parameter_is_tag(int argc, char** argv, int pos);
51 
52 /* find out, if a command still expects a parameter at a certain index.
53  * only if this returns true, than a completion will be searched.
54  *
55  * if no match is found, then it defaults to "command still expects a
56  * parameter".
57  */
58 struct {
59     const char*   command; /* the first argument */
60     int     min_index;  /* rule will only be considered */
61                         /* if current pos >= min_index */
62     bool    (*function)(int argc, char** argv, int pos);
63 } g_parameter_expected[] = {
64     { "focus_nth",      2,  no_completion },
65     { "close",          2,  no_completion },
66     { "cycle",          2,  no_completion },
67     { "split",          4,  no_completion },
68     { "raise",          2,  no_completion },
69     { "jumpto",         2,  no_completion },
70     { "bring",          2,  no_completion },
71     { "focus_edge",     2,  no_completion },
72     { "shift_edge",     2,  no_completion },
73     { "cycle_monitor",  2,  no_completion },
74     { "focus_monitor",  2,  no_completion },
75     { "shift_to_monitor",2,  no_completion },
76     { "add",            2,  no_completion },
77     { "use",            2,  no_completion },
78     { "use_index",      3,  no_completion },
79     { "merge_tag",      3,  no_completion },
80     { "rename",         3,  no_completion },
81     { "move",           2,  no_completion },
82     { "move_index",     3,  no_completion },
83     { "add_monitor",    7,  no_completion },
84     { "remove_monitor", 2,  no_completion },
85     { "monitor_rect",   3,  no_completion },
86     { "pad",            6,  no_completion },
87     { "layout",         3,  no_completion },
88     { "dump",           3,  no_completion },
89     { "load",           3,  no_completion },
90     { "load",           2,  first_parameter_is_tag },
91     { "tag_status",     2,  no_completion },
92     { "object_tree",    2,  no_completion },
93 };
94 
95 enum IndexCompare {
96     LE, /* lower equal */
97     EQ, /* equal to */
98     GE, /* greater equal */
99 };
100 
101 /* list of completions, if a line matches, then it will be used, the order
102  * does not matter */
103 struct {
104     const char*   command;
105     IndexCompare  relation; /* defines how the index matches */
106     int     index;      /* which parameter to complete */
107                         /* command name is index = 0 */
108                         /* GE 0 matches any position */
109                         /* LE 3 matches position from 0 to 3 */
110     /* === various methods, how to complete === */
111     /* completion by function */
112     void (*function)(int argc, char** argv, int pos, Output output);
113     /* completion by a list of strings */
114     const char** list;
115 } g_completions[] = {
116     /* name , relation, index,  completion method                   */
117     { "add_monitor",    EQ, 2,  complete_against_tags, 0 },
118     { "bring",          EQ, 1,  nullptr, completion_special_winids },
119     { "bring",          EQ, 1,  complete_against_winids, 0 },
120     { "close",          EQ, 1,  complete_against_winids, 0 },
121     { "cycle",          EQ, 1,  nullptr, completion_pm_one },
122     { "cycle_monitor",  EQ, 1,  nullptr, completion_pm_one },
123     { "dump",           EQ, 1,  complete_against_tags, 0 },
124     { "layout",         EQ, 1,  complete_against_tags, 0 },
125     { "load",           EQ, 1,  complete_against_tags, 0 },
126     { "merge_tag",      EQ, 1,  complete_against_tags, 0 },
127     { "merge_tag",      EQ, 2,  complete_merge_tag, 0 },
128     { "move",           EQ, 1,  complete_against_tags, 0 },
129     { "move_index",     EQ, 2,  nullptr, completion_use_index_args },
130     { "rename",         EQ, 1,  complete_against_tags, 0 },
131     { "raise",          EQ, 1,  nullptr, completion_special_winids },
132     { "raise",          EQ, 1,  complete_against_winids, 0 },
133     { "jumpto",         EQ, 1,  nullptr, completion_special_winids },
134     { "jumpto",         EQ, 1,  complete_against_winids, 0 },
135     { "shift_edge",     EQ, 1,  nullptr, completion_directions },
136     { "split",          EQ, 1,  nullptr, completion_split_modes },
137     { "split",          EQ, 2,  nullptr, completion_split_ratios },
138     { "use",            EQ, 1,  complete_against_tags, 0 },
139     { "use_index",      EQ, 1,  nullptr, completion_pm_one },
140     { "use_index",      EQ, 2,  nullptr, completion_use_index_args },
141     { "focus_monitor",  EQ, 1,  complete_against_monitors, 0 },
142     { "shift_to_monitor",EQ, 1,  complete_against_monitors, 0 },
143     { "remove_monitor", EQ, 1,  complete_against_monitors, 0 },
144     { "name_monitor",   EQ, 1,  complete_against_monitors, 0 },
145     { "monitor_rect",   EQ, 1,  complete_against_monitors, 0 },
146     { "pad",            EQ, 1,  complete_against_monitors, 0 },
147     { "tag_status",     EQ, 1,  complete_against_monitors, 0 },
148 };
149 
150 // Implementation of CommandBinding
151 
CommandBinding(function<int (Output)> cmd)152 CommandBinding::CommandBinding(function<int(Output)> cmd)
153     : command([cmd](Input, Output output) { return cmd(output); })
__anonb96659db0402(Completion& c) 154     , completion_([](Completion& c) { c.none(); })
155 {}
156 
CommandBinding(function<int ()> cmd)157 CommandBinding::CommandBinding(function<int()> cmd)
158     : command([cmd](Input, Output) { return cmd(); })
__anonb96659db0602(Completion& c) 159     , completion_([](Completion& c) { c.none(); })
160 {}
161 
162 // Nearly all of the following can go away, if all C-style command functions
163 // have been migrated to int(Input, Output).
164 
165 /* Creates an ephemeral argv array from the given Input */
commandFromCFunc(function<int (int argc,char ** argv,Output output)> func)166 function <int(Input,Output)> CommandBinding::commandFromCFunc(
167         function <int(int argc, char**argv, Output output)> func) {
168     return [func](Input args, Output out) {
169         /* Note that instead of copying the arguments, we point to their
170          * original location here. This only works because Input stores its
171          * payload in shared pointers and other references to them are held
172          * until the command is finished.
173          */
174         shared_ptr<char*> argv(new char*[args.size() + 1],
175                 std::default_delete<char*[]>());
176 
177         // Most of the commands want a char**, not a const char**. Let's
178         // hope, they don't actually modify it.
179         argv.get()[0] = const_cast<char*>(args.command().c_str());
180         auto elem = args.begin();
181         for (size_t i = 0; i < args.size(); i++, elem++) {
182             argv.get()[i+1] = const_cast<char*>(elem->c_str());
183         }
184 
185         return func(args.size() + 1, argv.get(), out);
186     };
187 }
188 
CommandBinding(int func (int argc,const char ** argv,Output output))189 CommandBinding::CommandBinding(int func(int argc, const char** argv, Output output))
190     : command(commandFromCFunc([func](int argc, char** argv, Output out) {
191                 return func(argc, const_cast<const char**>(argv), out);
192             }))
193 {}
194 
CommandBinding(int func (int argc,char ** argv))195 CommandBinding::CommandBinding(int func(int argc, char** argv))
196     : command(commandFromCFunc([func](int argc, char **argv, Output) {
197                 return func(argc, argv);
198             }))
199 {}
200 
CommandBinding(int func (int argc,const char ** argv))201 CommandBinding::CommandBinding(int func(int argc, const char** argv))
202     : command(commandFromCFunc([func](int argc, char** argv, Output) {
203                 return func(argc, const_cast<const char**>(argv));
204             }))
205 {}
206 
207 
208 /** Complete the given list of arguments
209  */
complete(Completion & completion) const210 void CommandBinding::complete(Completion& completion) const {
211     if ((bool) completion_) {
212         completion_(completion);
213     }
214 }
215 
216 // Implementation of CommandTable
callCommand(Input args,Output out) const217 int CommandTable::callCommand(Input args, Output out) const {
218     if (args.command().empty()) {
219         // may happen if you call sprintf, but nothing afterwards
220         return HERBST_NEED_MORE_ARGS;
221     }
222 
223     const auto cmd = map.find(args.command());
224 
225     if (cmd == map.end()) {
226         out << "error: Command \"" << args.command() << "\" not found" << endl;
227         return HERBST_COMMAND_NOT_FOUND;
228     }
229 
230     return cmd->second(args, out);
231 }
232 
233 namespace Commands {
234     shared_ptr<const CommandTable> command_table;
235 }
236 
initialize(unique_ptr<const CommandTable> commands)237 void Commands::initialize(unique_ptr<const CommandTable> commands) {
238     if (!command_table) {
239         command_table = move(commands);
240     }
241     // TODO What do we do in the 'already initialized' case?
242 }
243 
call(Input args,Output out)244 int Commands::call(Input args, Output out) {
245     if (!command_table) {
246         return HERBST_COMMAND_NOT_FOUND;
247     }
248     return command_table->callCommand(args, out);
249 }
250 
commandExists(const string & commandName)251 bool Commands::commandExists(const string& commandName)
252 {
253     if (!command_table) {
254         return false;
255     }
256     return command_table->find(commandName) != command_table->end();
257 }
258 
get()259 shared_ptr<const CommandTable> Commands::get() {
260     if (!command_table) {
261         throw std::logic_error("CommandTable not initialized, but get() called.");
262     }
263     return command_table;
264 }
265 
complete(Completion & completion)266 void Commands::complete(Completion& completion) {
267     // wrap around complete_against_commands
268     // Once we have migrated all completions, we can implement command
269     // completion directly with the minimal interface that Completion provides.
270     // Then we can also unfriend this function from the Completion class.
271     char** argv = new char*[completion.args_.size() + 1];
272     argv[completion.args_.size()] = nullptr;
273     for (size_t i = 0; i < completion.args_.size(); i++) {
274         argv[i] = const_cast<char*>((completion.args_.begin() + i)->c_str());
275     }
276     int res = complete_against_commands(completion.args_.size(),
277                                         argv,
278                                         completion.index_,
279                                         completion.output_);
280     delete[] argv;
281     if (res == HERBST_NO_PARAMETER_EXPECTED) {
282         completion.none();
283     } else if (res != 0) {
284         completion.invalidArguments();
285     }
286 }
287 
list_commands(Output output)288 int list_commands(Output output)
289 {
290     for (auto cmd : *Commands::get()) {
291         output << cmd.first << endl;
292     }
293     return 0;
294 }
295 
try_complete_suffix(const char * needle,const char * to_check,const char * suffix,const char * prefix,Output output)296 static void try_complete_suffix(const char* needle, const char* to_check, const char* suffix,
297                                 const char* prefix, Output output)
298 {
299     bool matches = !needle;
300     if (!matches) {
301         matches = true; // set it to true if the loop successfully runs
302         // find the first difference between needle and to_check
303         for (int i = 0; true ; i++) {
304             // check if needle is a prefix of to_check
305             if (!needle[i]) {
306                 break;
307             }
308             // if the needle is longer than to_check, then needle isn't a
309             // correct prefix of to_check
310             if (!to_check[i]) {
311                 matches = false;
312                 break;
313             }
314             // only proceed if they are identical
315             if (to_check[i] != needle[i]) {
316                 matches = false;
317                 break;
318             }
319         }
320     }
321     if (matches) {
322         char* escaped = nullptr;
323         if (g_shell_quoting) {
324             escaped = posix_sh_escape(to_check);
325         }
326         char* prefix_escaped = nullptr;
327         if (prefix) {
328             if (g_shell_quoting) {
329                 prefix_escaped = posix_sh_escape(prefix);
330             }
331             output << (prefix_escaped ? prefix_escaped : prefix);
332         }
333         output << (escaped ? escaped : to_check);
334         free(escaped);
335         output << suffix;
336     }
337 }
338 
try_complete(const char * needle,string to_check,Output output)339 void try_complete(const char* needle, string to_check, Output output) {
340     try_complete(needle, to_check.c_str(), output);
341 }
342 
try_complete(const char * needle,const char * to_check,Output output)343 void try_complete(const char* needle, const char* to_check, Output output) {
344     const char* suffix = g_shell_quoting ? " \n" : "\n";
345     try_complete_suffix(needle, to_check, suffix, nullptr, output);
346 }
347 
complete_against_list(const char * needle,const char ** list,Output output)348 static void complete_against_list(const char* needle, const char** list, Output output) {
349     while (*list) {
350         const char* name = *list;
351         try_complete(needle, name, output);
352         list++;
353     }
354 }
355 
complete_against_tags(int argc,char ** argv,int pos,Output output)356 void complete_against_tags(int argc, char** argv, int pos, Output output) {
357     const char* needle;
358     if (pos >= argc) {
359         needle = "";
360     } else {
361         needle = argv[pos];
362     }
363     for (int i = 0; i < tag_get_count(); i++) {
364         const char* name = get_tag_by_index(i)->name->c_str();
365         try_complete(needle, name, output);
366     }
367 }
368 
complete_against_monitors(int argc,char ** argv,int pos,Output output)369 void complete_against_monitors(int argc, char** argv, int pos, Output output) {
370     const char* needle;
371     if (pos >= argc) {
372         needle = "";
373     } else {
374         needle = argv[pos];
375     }
376     // complete against relative indices
377     try_complete(needle, "-1", output);
378     try_complete(needle, "+1", output);
379     try_complete(needle, "+0", output);
380     for (auto m : *g_monitors) {
381         // complete against the absolute index
382         try_complete(needle, to_string(m->index()), output);
383         // complete against the name
384         if (m->name != "") {
385             try_complete(needle, m->name->c_str(), output);
386         }
387     }
388 }
389 
complete_against_winids(int argc,char ** argv,int pos,Output output)390 void complete_against_winids(int argc, char** argv, int pos, Output output) {
391     const char* needle;
392     if (pos >= argc) {
393         needle = "";
394     } else {
395         needle = argv[pos];
396     }
397     for (auto c : Root::common().clients()) {
398         char buf[100];
399         snprintf(buf, LENGTH(buf), "0x%lx", c.second->window_);
400         try_complete(needle, buf, output);
401     }
402 }
403 
complete_merge_tag(int argc,char ** argv,int pos,Output output)404 void complete_merge_tag(int argc, char** argv, int pos, Output output) {
405     const char* first = (argc > 1) ? argv[1] : "";
406     const char* needle;
407     if (pos >= argc) {
408         needle = "";
409     } else {
410         needle = argv[pos];
411     }
412     for (int i = 0; i < tag_get_count(); i++) {
413         const char* name = get_tag_by_index(i)->name->c_str();
414         if (!strcmp(name, first)) {
415             // merge target must not be equal to tag to remove
416             continue;
417         }
418         try_complete(needle, name, output);
419     }
420 }
421 
parameter_expected(int argc,char ** argv,int pos)422 static bool parameter_expected(int argc, char** argv, int pos) {
423     if (pos <= 0 || argc < 1) {
424         /* no parameter if there is no command */
425         return false;
426     }
427     for (size_t i = 0; i < LENGTH(g_parameter_expected)
428                     && g_parameter_expected[i].command; i++) {
429         if (pos < g_parameter_expected[i].min_index) {
430             continue;
431         }
432         if (!strcmp(g_parameter_expected[i].command, argv[0])) {
433             return g_parameter_expected[i].function(argc, argv, pos);
434         }
435     }
436     return true;
437 }
438 
complete_command(int argc,char ** argv,Output output)439 int complete_command(int argc, char** argv, Output output) {
440     // usage: complete POSITION command to complete ...
441     if (argc < 2) {
442         return HERBST_NEED_MORE_ARGS;
443     }
444     char* cmdname = argv[0];
445     g_shell_quoting = !strcmp(cmdname, "complete_shell");
446     // index must be between first and last arg of "command to complete ..."
447     int position = CLAMP(atoi(argv[1]), 0, argc-2);
448     (void)SHIFT(argc, argv);
449     (void)SHIFT(argc, argv);
450     if (g_shell_quoting) {
451         for (int i = 0; i < argc; i++) {
452             posix_sh_compress_inplace(argv[i]);
453         }
454     }
455     return complete_against_commands(argc, argv, position, output);
456 }
457 
complete_against_commands(int argc,char ** argv,int position,Output output)458 int complete_against_commands(int argc, char** argv, int position,
459                               Output output) {
460     // complete command
461     if (position == 0) {
462         char* str = (argc >= 1) ? argv[0] : nullptr;
463         for (auto cmd : *Commands::get()) {
464             // only check the first len bytes
465             try_complete(str, cmd.first.c_str(), output);
466         }
467         return 0;
468     }
469     // try to get completion from the command binding
470     string commandName = argv[0];
471     auto commandTable = Commands::get();
472     auto it = commandTable->find(commandName);
473     if (it == commandTable->end()) {
474         return HERBST_INVALID_ARGUMENT;
475     }
476     if (it->second.hasCompletion()) {
477         vector<string> arguments;
478         for (int i = 1; i < argc; i++) {
479             arguments.push_back(argv[i]);
480         }
481         // the new completion context has the command name removed
482         Completion completion(arguments, position - 1, g_shell_quoting, output);
483         it->second.complete(completion);
484         return completion.noParameterExpected() ?
485             HERBST_NO_PARAMETER_EXPECTED
486             : 0;
487     }
488     if (!parameter_expected(argc, argv, position)) {
489         return HERBST_NO_PARAMETER_EXPECTED;
490     }
491     if (argc >= 1) {
492         const char* cmd_str = (argc >= 1) ? argv[0] : "";
493         // complete parameters for commands
494         for (size_t i = 0; i < LENGTH(g_completions); i++) {
495             bool matches = false;
496             switch (g_completions[i].relation) {
497                 case LE: matches = position <= g_completions[i].index; break;
498                 case EQ: matches = position == g_completions[i].index; break;
499                 case GE: matches = position >= g_completions[i].index; break;
500             }
501             if (!matches
502                 || !g_completions[i].command
503                 || strcmp(cmd_str, g_completions[i].command) != 0) {
504                 continue;
505             }
506             const char* needle = (position < argc) ? argv[position] : "";
507             if (!needle) {
508                 needle = "";
509             }
510             // try to complete
511             if (g_completions[i].function) {
512                 g_completions[i].function(argc, argv, position, output);
513             }
514             if (g_completions[i].list) {
515                 complete_against_list(needle, g_completions[i].list,
516                                       output);
517             }
518         }
519     }
520     return 0;
521 }
522 
first_parameter_is_tag(int argc,char ** argv,int pos)523 static bool first_parameter_is_tag(int argc, char** argv, int pos) {
524     // only complete if first parameter is a valid tag
525     if (argc >= 2 && find_tag(argv[1]) && pos == 2) {
526         return true;
527     } else {
528         return false;
529     }
530 }
531