LCOV - code coverage report
Current view: top level - src/command - run.c Hit Total Coverage
Test: dte Lines: 60 66 90.9 %
Date: 2023-03-25 22:04:15

          Line data    Source code
       1             : #include "run.h"
       2             : #include "args.h"
       3             : #include "parse.h"
       4             : #include "change.h"
       5             : #include "config.h"
       6             : #include "error.h"
       7             : #include "util/debug.h"
       8             : #include "util/ptr-array.h"
       9             : #include "util/xmalloc.h"
      10             : 
      11             : const Command *current_command;
      12             : 
      13             : static bool run_commands(CommandRunner *runner, const PointerArray *array);
      14             : 
      15        4284 : static bool run_command(CommandRunner *runner, char **av)
      16             : {
      17        4284 :     const CommandSet *cmds = runner->cmds;
      18        4284 :     const Command *cmd = cmds->lookup(av[0]);
      19        4284 :     if (!cmd) {
      20          10 :         const char *name = av[0];
      21          10 :         if (!runner->lookup_alias) {
      22           0 :             return error_msg("No such command: %s", name);
      23             :         }
      24             : 
      25          10 :         const char *alias_value = runner->lookup_alias(name, runner->userdata);
      26          10 :         if (unlikely(!alias_value)) {
      27           1 :             return error_msg("No such command or alias: %s", name);
      28             :         }
      29             : 
      30           9 :         PointerArray array = PTR_ARRAY_INIT;
      31           9 :         CommandParseError err = parse_commands(runner, &array, alias_value);
      32           9 :         if (unlikely(err != CMDERR_NONE)) {
      33           0 :             const char *err_msg = command_parse_error_to_string(err);
      34           0 :             ptr_array_free(&array);
      35           0 :             return error_msg("Parsing alias %s: %s", name, err_msg);
      36             :         }
      37             : 
      38             :         // Remove NULL
      39           9 :         array.count--;
      40             : 
      41          37 :         for (size_t i = 1; av[i]; i++) {
      42          28 :             ptr_array_append(&array, xstrdup(av[i]));
      43             :         }
      44           9 :         ptr_array_append(&array, NULL);
      45             : 
      46           9 :         bool r = run_commands(runner, &array);
      47           9 :         ptr_array_free(&array);
      48           9 :         return r;
      49             :     }
      50             : 
      51        4274 :     if (unlikely(current_config.file && !cmd->allow_in_rc)) {
      52           0 :         return error_msg("Command %s not allowed in config file", cmd->name);
      53             :     }
      54             : 
      55             :     // Record command in macro buffer, if recording (this needs to be done
      56             :     // before parse_args() mutates the array)
      57        4274 :     if (runner->allow_recording && runner->cmds->macro_record) {
      58           5 :         runner->cmds->macro_record(cmd, av + 1, runner->userdata);
      59             :     }
      60             : 
      61             :     // By default change can't be merged with previous one.
      62             :     // Any command can override this by calling begin_change() again.
      63        4274 :     begin_change(CHANGE_MERGE_NONE);
      64             : 
      65        4274 :     CommandArgs a = cmdargs_new(av + 1);
      66        4274 :     current_command = cmd;
      67        4274 :     bool r = likely(parse_args(cmd, &a)) && cmd->cmd(runner->userdata, &a);
      68        4274 :     current_command = NULL;
      69             : 
      70        4274 :     end_change();
      71        4274 :     return r;
      72             : }
      73             : 
      74        5321 : static bool run_commands(CommandRunner *runner, const PointerArray *array)
      75             : {
      76        5321 :     if (unlikely(runner->recursion_count > 16)) {
      77           0 :         return error_msg("alias recursion overflow");
      78             :     }
      79             : 
      80        5321 :     void **ptrs = array->ptrs;
      81        5321 :     size_t len = array->count;
      82        5321 :     size_t nfailed = 0;
      83        5321 :     BUG_ON(len == 0);
      84        5321 :     BUG_ON(ptrs[len - 1] != NULL);
      85        5321 :     runner->recursion_count++;
      86             : 
      87       10667 :     for (size_t s = 0, e = 0; s < len; ) {
      88             :         // Iterate over strings, until a terminating NULL is encountered
      89       20779 :         while (ptrs[e]) {
      90       15433 :             e++;
      91       20779 :             BUG_ON(e >= len);
      92             :         }
      93             : 
      94             :         // If the value of `e` (end) changed, there's a run of at least
      95             :         // 1 string, which is a command followed by 0 or more arguments
      96        5346 :         if (e != s) {
      97        4284 :             if (!run_command(runner, (char**)ptrs + s)) {
      98          87 :                 nfailed++;
      99             :             }
     100             :         }
     101             : 
     102             :         // Skip past the NULL, onto the next command (if any)
     103        5346 :         s = ++e;
     104             :     }
     105             : 
     106        5321 :     runner->recursion_count--;
     107        5321 :     return (nfailed == 0);
     108             : }
     109             : 
     110        5314 : bool handle_command(CommandRunner *runner, const char *cmd)
     111             : {
     112        5314 :     BUG_ON(runner->recursion_count != 0);
     113        5314 :     PointerArray array = PTR_ARRAY_INIT;
     114        5314 :     CommandParseError err = parse_commands(runner, &array, cmd);
     115        5314 :     bool r;
     116        5314 :     if (likely(err == CMDERR_NONE)) {
     117        5312 :         r = run_commands(runner, &array);
     118        5312 :         BUG_ON(runner->recursion_count != 0);
     119             :     } else {
     120           2 :         const char *str = command_parse_error_to_string(err);
     121           2 :         error_msg("Command syntax error: %s", str);
     122           2 :         r = false;
     123             :     }
     124        5314 :     ptr_array_free(&array);
     125        5314 :     return r;
     126             : }

Generated by: LCOV version 1.14