extractExecutable function

String? extractExecutable(
  1. String input
)

Extract the primary executable from a command string.

Handles env prefixes, sudo, nice, time, etc.

Implementation

String? extractExecutable(String input) {
  final trimmed = input.trim();
  if (trimmed.isEmpty) return null;

  final commands = extractCommands(trimmed);
  if (commands.isEmpty) return null;

  final cmd = commands.first;
  var exe = cmd.executable;

  // Skip through env-prefix commands.
  const prefixCommands = {
    'env',
    'sudo',
    'nice',
    'nohup',
    'time',
    'timeout',
    'strace',
    'ltrace',
    'ionice',
    'chrt',
    'taskset',
    'numactl',
    'command',
    'builtin',
  };

  if (prefixCommands.contains(exe)) {
    // Skip flags (starting with -), VAR=value env assignments, and known
    // flag arguments that take a numeric / string parameter (e.g. `nice -n 10`,
    // `timeout 5s`, `taskset -c 0`). Heuristic: also skip purely numeric
    // tokens, which are nearly always values for the previous flag, never
    // an executable name.
    var skipNext = false;
    for (final arg in cmd.arguments) {
      if (skipNext) {
        skipNext = false;
        continue;
      }
      if (arg.startsWith('-')) {
        // Single-letter flags like `-n`, `-c` typically take a value.
        if (RegExp(r'^-[A-Za-z]$').hasMatch(arg)) {
          skipNext = true;
        }
        continue;
      }
      // Skip env-var assignments like PATH=/usr/bin or FOO=bar.
      if (RegExp(r'^[A-Za-z_][A-Za-z0-9_]*=').hasMatch(arg)) {
        continue;
      }
      // Skip purely numeric / duration tokens (e.g. `5`, `10`, `5s`).
      if (RegExp(r'^\d+[smhd]?$').hasMatch(arg)) {
        continue;
      }
      return arg.split('/').last;
    }
  }

  // Skip env-var assignments that appear as the executable (shouldn't happen
  // after proper parsing, but be defensive).
  if (exe.contains('=') && !exe.startsWith('-')) {
    for (final arg in cmd.arguments) {
      if (!arg.contains('=') || arg.startsWith('-')) {
        return arg.split('/').last;
      }
    }
    return null;
  }

  return exe.split('/').last;
}