kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

main.c (20243B)


      1 #include <kit/config.h>
      2 
      3 #include "driver.h"
      4 
      5 /* Multi-call dispatch. Looks at argv[0]'s basename for a tool name; if argv[0]
      6  * is the bare "kit" binary, falls back to argv[1].
      7  *
      8  * Tool availability is centralized here. The Makefile uses the same
      9  * KIT_TOOL_*_ENABLED flags to drop driver/<tool>.c objects, so disabled
     10  * tools do not need #if checks scattered through each tool implementation.
     11  */
     12 
     13 typedef int (*DriverToolMain)(int argc, char** argv);
     14 typedef int (*DriverToolMainEx)(int argc, char** argv,
     15                                 const KitDriverExtension*);
     16 typedef void (*DriverToolHelp)(void);
     17 
     18 typedef enum DriverHelpGroup {
     19   DRIVER_HELP_BUILD,
     20   DRIVER_HELP_BINARY,
     21   DRIVER_HELP_EXECUTION,
     22   DRIVER_HELP_DISTRIBUTION,
     23   DRIVER_HELP_DATA,
     24   DRIVER_HELP_KIT,
     25   DRIVER_HELP_GROUP_COUNT,
     26 } DriverHelpGroup;
     27 
     28 typedef struct DriverToolDesc {
     29   const char* name;
     30   DriverToolMain main;
     31   DriverToolMainEx main_ex;
     32   DriverToolHelp help;
     33   const char* summary;
     34   /* Task-oriented presentation category for the top-level help inventory. */
     35   DriverHelpGroup help_group;
     36   /* DriverToolGroup bits; 0 => not in the default install set */
     37   unsigned groups;
     38 } DriverToolDesc;
     39 
     40 static int driver_version_command(int argc, char** argv);
     41 static void driver_help_version_command(void);
     42 
     43 static const DriverToolDesc driver_tools[] = {
     44 #if KIT_TOOL_CC_ENABLED
     45     {"cc", driver_cc, NULL, driver_help_cc,
     46      "Compile (and link) C sources, with cpp / dep-emit / ELF -shared modes",
     47      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     48 #endif
     49 #if KIT_TOOL_CHECK_ENABLED
     50     {"check", driver_check, NULL, driver_help_check,
     51      "Run C frontend checks without emitting code", DRIVER_HELP_BUILD,
     52      DRIVER_GROUP_OTHER},
     53 #endif
     54 #if KIT_TOOL_BUILD_EXE_ENABLED
     55     {"build-exe", driver_build_exe, driver_build_exe_ex, driver_help_build_exe,
     56      "Compile a polyglot source set and link an executable (in memory)",
     57      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     58 #endif
     59 #if KIT_TOOL_BUILD_LIB_ENABLED
     60     {"build-lib", driver_build_lib, driver_build_lib_ex, driver_help_build_lib,
     61      "Compile sources into a static .a or ELF shared library (in memory)",
     62      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     63 #endif
     64 #if KIT_TOOL_BUILD_OBJ_ENABLED
     65     {"build-obj", driver_build_obj, driver_build_obj_ex, driver_help_build_obj,
     66      "Compile sources to an object / asm / C / IR, or check (replaces compile)",
     67      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     68 #endif
     69 #if KIT_TOOL_BUILD_ENABLED
     70     {"build", driver_build, NULL, driver_help_build,
     71      "Resolve a target with the content-addressed build coordinator",
     72      DRIVER_HELP_BUILD, DRIVER_GROUP_OTHER},
     73 #endif
     74 #if KIT_TOOL_MAKE_ENABLED
     75     {"make", driver_make, NULL, driver_help_make,
     76      "Bring targets up to date from a makefile (POSIX make)",
     77      DRIVER_HELP_BUILD, DRIVER_GROUP_OTHER},
     78 #endif
     79 #if KIT_TOOL_INSTALL_ENABLED
     80     {"install", driver_install, NULL, driver_help_install,
     81      "Symlink the kit tools into a dir for drop-in toolchain use",
     82      DRIVER_HELP_KIT, DRIVER_GROUP_OTHER},
     83 #endif
     84 #if KIT_TOOL_CPP_ENABLED
     85     {"cpp", driver_cpp, NULL, driver_help_cpp,
     86      "Standalone C preprocessor (alias for `cc -E` minus link scaffold)",
     87      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     88 #endif
     89 #if KIT_TOOL_AS_ENABLED
     90     {"as", driver_as, NULL, driver_help_as,
     91      "Assemble a GAS-subset text source into a relocatable object",
     92      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     93 #endif
     94 #if KIT_TOOL_LD_ENABLED
     95     {"ld", driver_ld, NULL, driver_help_ld,
     96      "Link objects/archives into an executable or ELF shared library",
     97      DRIVER_HELP_BUILD, DRIVER_GROUP_TOOLCHAIN},
     98 #endif
     99 #if KIT_TOOL_AR_ENABLED
    100     {"ar", driver_ar, NULL, driver_help_ar,
    101      "Create / modify / list / extract POSIX `ar` archives", DRIVER_HELP_BINARY,
    102      DRIVER_GROUP_TOOLCHAIN},
    103 #endif
    104 #if KIT_TOOL_CPIO_ENABLED
    105     {"cpio", driver_cpio, NULL, driver_help_cpio,
    106      "Create / list / extract SVR4 newc cpio (initramfs) archives",
    107      DRIVER_HELP_BINARY, DRIVER_GROUP_TOOLCHAIN},
    108 #endif
    109 #if KIT_TOOL_RANLIB_ENABLED
    110     {"ranlib", driver_ranlib, NULL, driver_help_ranlib,
    111      "Refresh the symbol index of an `ar` archive", DRIVER_HELP_BINARY,
    112      DRIVER_GROUP_TOOLCHAIN},
    113 #endif
    114 #if KIT_TOOL_STRIP_ENABLED
    115     {"strip", driver_strip, NULL, driver_help_strip,
    116      "Drop debug sections and/or symbols from a .o or .a", DRIVER_HELP_BINARY,
    117      DRIVER_GROUP_TOOLCHAIN},
    118 #endif
    119 #if KIT_TOOL_OBJCOPY_ENABLED
    120     {"objcopy", driver_objcopy, NULL, driver_help_objcopy,
    121      "Copy and transform an object file (rename / remove / format)",
    122      DRIVER_HELP_BINARY, DRIVER_GROUP_TOOLCHAIN},
    123 #endif
    124 #if KIT_TOOL_IMAGE_ENABLED
    125     {"image", driver_image, NULL, driver_help_image,
    126      "Emit flat kernel/load images from linked objects", DRIVER_HELP_BINARY,
    127      DRIVER_GROUP_TOOLCHAIN},
    128 #endif
    129 #if KIT_TOOL_OBJDUMP_ENABLED
    130     {"objdump", driver_objdump, NULL, driver_help_objdump,
    131      "Dump sections, symbols, disassembly, hex, and relocations",
    132      DRIVER_HELP_BINARY, DRIVER_GROUP_TOOLCHAIN},
    133 #endif
    134 #if KIT_TOOL_RUN_ENABLED
    135     {"run", driver_run, driver_run_ex, driver_help_run,
    136      "JIT-compile inputs and invoke the entry symbol in-process",
    137      DRIVER_HELP_EXECUTION, DRIVER_GROUP_OTHER},
    138 #endif
    139 #if KIT_TOOL_DBG_ENABLED
    140     {"dbg", driver_dbg, driver_dbg_ex, driver_help_dbg,
    141      "Interactive JIT debugger (REPL on top of the JIT image)",
    142      DRIVER_HELP_EXECUTION, DRIVER_GROUP_OTHER},
    143 #endif
    144 #if KIT_TOOL_EMU_ENABLED
    145     {"emu", driver_emu, NULL, driver_help_emu, NULL, DRIVER_HELP_EXECUTION,
    146      DRIVER_GROUP_OTHER},
    147 #endif
    148 #if KIT_TOOL_NM_ENABLED
    149     {"nm", driver_nm, NULL, driver_help_nm, "List symbols from object files",
    150      DRIVER_HELP_BINARY, DRIVER_GROUP_TOOLCHAIN},
    151 #endif
    152 #if KIT_TOOL_SIZE_ENABLED
    153     {"size", driver_size, NULL, driver_help_size,
    154      "Display section sizes of object files", DRIVER_HELP_BINARY,
    155      DRIVER_GROUP_TOOLCHAIN},
    156 #endif
    157 #if KIT_TOOL_ADDR2LINE_ENABLED
    158     {"addr2line", driver_addr2line, NULL, driver_help_addr2line,
    159      "Translate addresses to file:line using debug info", DRIVER_HELP_BINARY,
    160      DRIVER_GROUP_TOOLCHAIN},
    161 #endif
    162 #if KIT_TOOL_SYMBOLIZE_ENABLED
    163     {"symbolize", driver_symbolize, NULL, driver_help_symbolize,
    164      "Annotate a kit backtrace stream with func at file:line",
    165      DRIVER_HELP_BINARY, DRIVER_GROUP_OTHER},
    166 #endif
    167 #if KIT_TOOL_STRINGS_ENABLED
    168     {"strings", driver_strings, NULL, driver_help_strings,
    169      "Print printable character sequences found in a file", DRIVER_HELP_BINARY,
    170      DRIVER_GROUP_TOOLCHAIN},
    171 #endif
    172 #if KIT_TOOL_CAS_ENABLED
    173     {"cas", driver_cas, NULL, driver_help_cas,
    174      "Store, inspect, verify, and materialize kit CAS blobs and trees",
    175      DRIVER_HELP_DISTRIBUTION, DRIVER_GROUP_OTHER},
    176 #endif
    177 #if KIT_TOOL_PKG_ENABLED
    178     {"pkg", driver_pkg, NULL, driver_help_pkg,
    179      "Bundle, sign, verify, and unpack distributable .kpkg packages",
    180      DRIVER_HELP_DISTRIBUTION, DRIVER_GROUP_OTHER},
    181 #endif
    182 #if KIT_TOOL_XXD_ENABLED
    183     {"xxd", driver_xxd, NULL, driver_help_xxd,
    184      "Hex dump any file (and reverse a dump back to binary)", DRIVER_HELP_DATA,
    185      DRIVER_GROUP_BYTEUTIL},
    186 #endif
    187 #if KIT_TOOL_CMP_ENABLED
    188     {"cmp", driver_cmp, NULL, driver_help_cmp, "Compare two files byte by byte",
    189      DRIVER_HELP_DATA, DRIVER_GROUP_BYTEUTIL},
    190 #endif
    191 #if KIT_TOOL_HASH_ENABLED
    192     {"hash", driver_hash, NULL, driver_help_hash,
    193      "Hash files with SHA-256, BLAKE2b, or CRC-32", DRIVER_HELP_DATA,
    194      DRIVER_GROUP_OTHER},
    195     {"sha256sum", driver_sha256sum, NULL, driver_help_sha256sum,
    196      "SHA-256 of files or stdin (sha256sum-compatible)", DRIVER_HELP_DATA,
    197      DRIVER_GROUP_BYTEUTIL},
    198     {"b2sum", driver_b2sum, NULL, driver_help_b2sum,
    199      "BLAKE2b-256 of files or stdin (b2sum-style)", DRIVER_HELP_DATA,
    200      DRIVER_GROUP_BYTEUTIL},
    201     {"crc32", driver_crc32, NULL, driver_help_crc32, "CRC-32 of files or stdin",
    202      DRIVER_HELP_DATA, DRIVER_GROUP_BYTEUTIL},
    203 #endif
    204 #if KIT_TOOL_COMPRESS_ENABLED
    205     {"compress", driver_compress, NULL, driver_help_compress,
    206      "Compress or decompress data (gzip, lz4 frame)", DRIVER_HELP_DATA,
    207      DRIVER_GROUP_OTHER},
    208     {"gzip", driver_gzip, NULL, driver_help_gzip,
    209      "Compress to gzip (.gz); -d to decompress", DRIVER_HELP_DATA,
    210      DRIVER_GROUP_BYTEUTIL},
    211     {"gunzip", driver_gunzip, NULL, driver_help_gunzip,
    212      "Decompress gzip (.gz) streams", DRIVER_HELP_DATA, DRIVER_GROUP_BYTEUTIL},
    213     {"lz4", driver_lz4, NULL, driver_help_lz4,
    214      "Compress to LZ4 frame (.lz4); -d to decompress", DRIVER_HELP_DATA,
    215      DRIVER_GROUP_BYTEUTIL},
    216     {"lz4c", driver_lz4c, NULL, driver_help_lz4c,
    217      "Compress to LZ4 frame (.lz4) (lz4 alias)", DRIVER_HELP_DATA,
    218      DRIVER_GROUP_BYTEUTIL},
    219 #endif
    220 #if KIT_TOOL_DISAS_ENABLED
    221     {"disas", driver_disas, NULL, driver_help_disas,
    222      "Disassemble raw machine-code bytes for a target arch", DRIVER_HELP_BINARY,
    223      DRIVER_GROUP_OTHER},
    224 #endif
    225 #if KIT_TOOL_MC_ENABLED
    226     {"mc", driver_mc, NULL, driver_help_mc,
    227      "Assemble one instruction and show its machine-code encoding",
    228      DRIVER_HELP_BINARY, DRIVER_GROUP_OTHER},
    229 #endif
    230 #if KIT_TOOL_GRAM_ENABLED
    231     {"gram", driver_gram, NULL, driver_help_gram,
    232      "Generate a C parser/lexer from an EBNF grammar", DRIVER_HELP_BUILD,
    233      DRIVER_GROUP_OTHER},
    234 #endif
    235 #if KIT_TOOL_TARGETS_ENABLED
    236     {"targets", driver_targets, NULL, driver_help_targets,
    237      "List and inspect compiled target profiles", DRIVER_HELP_KIT,
    238      DRIVER_GROUP_OTHER},
    239 #endif
    240     {"version", driver_version_command, NULL, driver_help_version_command,
    241      "Print canonical release/build/host identity", DRIVER_HELP_KIT, 0},
    242 #if KIT_TOOL_UPDATE_ENABLED
    243     {"update", driver_update, NULL, driver_help_update,
    244      "Verify and install a newer kit; manage installed versions",
    245      DRIVER_HELP_KIT, DRIVER_GROUP_OTHER},
    246 #endif
    247     {NULL, NULL, NULL, NULL, NULL, DRIVER_HELP_GROUP_COUNT, DRIVER_GROUP_OTHER},
    248 };
    249 
    250 unsigned driver_tool_count(void) {
    251   return (unsigned)(sizeof driver_tools / sizeof driver_tools[0]) - 1u;
    252 }
    253 
    254 const char* driver_tool_name(unsigned index) {
    255   if (index >= driver_tool_count()) return NULL;
    256   return driver_tools[index].name;
    257 }
    258 
    259 unsigned driver_tool_groups(unsigned index) {
    260   if (index >= driver_tool_count()) return 0u;
    261   return driver_tools[index].groups;
    262 }
    263 
    264 int driver_tool_public(unsigned index) {
    265   return index < driver_tool_count() && driver_tools[index].summary != NULL;
    266 }
    267 
    268 int driver_tool_find(const char* name) {
    269   unsigned i;
    270   if (!name) return -1;
    271   for (i = 0; i < driver_tool_count(); ++i) {
    272     if (driver_streq(name, driver_tools[i].name)) return (int)i;
    273   }
    274   return -1;
    275 }
    276 
    277 static const DriverToolDesc* find_tool(const char* name) {
    278   unsigned i;
    279   for (i = 0; i < driver_tool_count(); ++i) {
    280     if (driver_streq(name, driver_tools[i].name)) return &driver_tools[i];
    281   }
    282   return NULL;
    283 }
    284 
    285 static int dispatch(const char* name, int argc, char** argv,
    286                     const KitDriverExtension* ext) {
    287   const DriverToolDesc* tool = find_tool(name);
    288   if (tool) {
    289     /* `--version` is answered uniformly for every tool here, before the tool's
    290      * own argument parser runs, so it works for both the symlink form
    291      * (argv[0] == "cc ... --version") and the subcommand form
    292      * ("kit cc ... --version", which also routes through dispatch). The scan
    293      * stops at "--", so a flag forwarded to a JITed program / guest is safe.
    294      * `update` is exempt: there `--version <ver>` is a value-bearing flag
    295      * (flip to an installed version), not a banner query, so the uniform
    296      * shortcut would hijack it. */
    297     if (!driver_streq(tool->name, "update") &&
    298         driver_argv_wants_version(argc, argv)) {
    299       driver_print_version(tool->name);
    300       return 0;
    301     }
    302     if (tool->main_ex) return tool->main_ex(argc, argv, ext);
    303     return tool->main(argc, argv);
    304   }
    305   return -1;
    306 }
    307 
    308 /* If `name` matches a tool, print its help and return 0. Otherwise return
    309  * non-zero (caller reports the error). */
    310 static int print_tool_help(const char* name) {
    311   const DriverToolDesc* tool = find_tool(name);
    312   if (tool) {
    313     tool->help();
    314     return 0;
    315   }
    316   return -1;
    317 }
    318 
    319 static void report_unknown_tool(const char* name) {
    320   const char* candidates[64];
    321   DriverSuggestion suggestions[3];
    322   size_t candidate_count = 0;
    323   size_t nsuggestions;
    324   unsigned i;
    325   for (i = 0; i < driver_tool_count() && candidate_count < 64; ++i)
    326     candidates[candidate_count++] = driver_tools[i].name;
    327   nsuggestions = driver_suggest_values(name, candidates, candidate_count,
    328                                        suggestions, 3);
    329   if (nsuggestions)
    330     driver_errf("kit", "no such tool: %s; did you mean '%s'?", name,
    331                 suggestions[0].value);
    332   else
    333     driver_errf("kit", "no such tool: %s; run `kit --help` to list tools",
    334                 name);
    335 }
    336 
    337 int driver_is_help_flag(const char* arg) {
    338   if (!arg) return 0;
    339   return driver_streq(arg, "--help") || driver_streq(arg, "-help");
    340 }
    341 
    342 int driver_argv_wants_help(int argc, char** argv, int accept_short_h) {
    343   int i;
    344   for (i = 1; i < argc; ++i) {
    345     /* `--` ends the driver's own option scan: anything after it is
    346      * forwarded to the JITed program (run/dbg) or the guest (emu),
    347      * so a user's own `--help` to their program must not be hijacked. */
    348     if (driver_streq(argv[i], "--")) break;
    349     if (driver_is_help_flag(argv[i])) return 1;
    350     if (accept_short_h && driver_streq(argv[i], "-h")) return 1;
    351   }
    352   return 0;
    353 }
    354 
    355 int driver_argv_wants_version(int argc, char** argv) {
    356   /* Only the FIRST argument a tool receives counts as a version query, so a
    357    * sub-flag that legitimately spells --version (e.g. `pkg create --version
    358    * 2026.6.0`, where --version is the package version) is never hijacked. The
    359    * common forms `cc --version` and `kit cc --version` both arrive with
    360    * --version as argv[1] of the dispatched tool. */
    361   return argc >= 2 && driver_streq(argv[1], "--version");
    362 }
    363 
    364 void driver_print_version(const char* tool) {
    365   if (tool)
    366     driver_printf("kit %s %s (%s, %s)\n", tool, kit_version_string(),
    367                   kit_build_id(), kit_host_triple());
    368   else
    369     driver_printf("kit %s (%s, %s)\n", kit_version_string(), kit_build_id(),
    370                   kit_host_triple());
    371 }
    372 
    373 static void driver_help_version_command(void) {
    374   driver_printf(
    375       "kit version — print release/build/host identity\n"
    376       "\n"
    377       "USAGE\n"
    378       "  kit version\n"
    379       "  kit --version\n"
    380       "\n"
    381       "Both forms print byte-identical canonical version output. `version` is\n"
    382       "an internal discovery command and is not installed as a multicall alias.\n"
    383       "\n"
    384       "OPTIONS\n"
    385       "  -h, --help  show this help and exit\n"
    386       "\n"
    387       "EXIT CODES\n"
    388       "  0   success           2   bad usage\n");
    389 }
    390 
    391 static int driver_version_command(int argc, char** argv) {
    392   if (driver_argv_wants_help(argc, argv, 1)) {
    393     driver_help_version_command();
    394     return 0;
    395   }
    396   if (argc != 1) {
    397     driver_errf("version", "unexpected argument: %s", argv[1]);
    398     return 2;
    399   }
    400   driver_print_version(NULL);
    401   return 0;
    402 }
    403 
    404 static const char* driver_help_group_name(DriverHelpGroup group) {
    405   switch (group) {
    406     case DRIVER_HELP_BUILD:
    407       return "Compilation and build";
    408     case DRIVER_HELP_BINARY:
    409       return "Binary and object tools";
    410     case DRIVER_HELP_EXECUTION:
    411       return "Execution and debugging";
    412     case DRIVER_HELP_DISTRIBUTION:
    413       return "Distribution";
    414     case DRIVER_HELP_DATA:
    415       return "Data utilities";
    416     case DRIVER_HELP_KIT:
    417       return "Kit management";
    418     case DRIVER_HELP_GROUP_COUNT:
    419       break;
    420   }
    421   return NULL;
    422 }
    423 
    424 void driver_help_top(void) {
    425   unsigned group;
    426   unsigned i;
    427   driver_printf(
    428       "kit — standalone compilation, linking, execution, and binary toolkit\n"
    429       "\n"
    430       "USAGE\n"
    431       "  kit <tool> [args...]            multi-call dispatch by name\n"
    432       "  <tool> [args...]                when invoked via the tool's "
    433       "symlink\n"
    434       "  kit help [<tool>]               this help, or per-tool help\n"
    435       "  kit --version                   release/build/host identity\n"
    436       "\n"
    437       "TOOLS\n");
    438   for (group = 0; group < (unsigned)DRIVER_HELP_GROUP_COUNT; ++group) {
    439     int heading_printed = 0;
    440     for (i = 0; i < driver_tool_count(); ++i) {
    441       if (!driver_tool_public(i) ||
    442           driver_tools[i].help_group != (DriverHelpGroup)group)
    443         continue;
    444       if (!heading_printed) {
    445         driver_printf("\n  %s\n",
    446                       driver_help_group_name((DriverHelpGroup)group));
    447         heading_printed = 1;
    448       }
    449       driver_printf("    %-9s %s\n", driver_tools[i].name,
    450                     driver_tools[i].summary);
    451     }
    452   }
    453   driver_printf(
    454       "\n"
    455       "GETTING HELP\n"
    456       "  kit <tool> --help               full per-tool help (also -h, except "
    457       "objdump)\n"
    458       "  kit help <tool>                 same as `kit <tool> --help`\n"
    459       "  kit <tool> --version            tool identity (except update, where\n"
    460       "                                  --version VER selects a release)\n"
    461       "\n"
    462       "NO-ARGUMENT BEHAVIOR\n"
    463       "  Operand-oriented tools usually show help. Stream filters may read\n"
    464       "  stdin, dbg enters its REPL, and a few operational tools act directly.\n"
    465       "  Each tool's page documents its stdin and output conventions.\n"
    466       "\n"
    467       "OUTPUT AND ENVIRONMENT\n"
    468       "  Requested help and normal data go to stdout; diagnostics go to stderr\n"
    469       "  unless a tool page says otherwise. Per-tool pages list relevant\n"
    470       "  environment variables, defaults, and state directories.\n"
    471       "\n"
    472       "EXIT CODES\n"
    473       "  0   success\n"
    474       "  1   tool-reported error (compile, link, I/O, ...)\n"
    475       "  2   bad command-line usage\n"
    476       "  Execution tools may pass through another program exit status when\n"
    477       "  their own page says so.\n");
    478 }
    479 
    480 int kit_driver_main_ex(int argc, char** argv, const KitDriverExtension* ext) {
    481   const char* name;
    482   char normalized_name[64];
    483   int rc;
    484 
    485   if (argc < 1) return 2;
    486   name = driver_basename(argv[0]);
    487 
    488   /* Windows aliases use the executable suffix required by CreateProcess and
    489    * PATHEXT (cc.exe, ld.exe, ...), while the centralized tool table stores
    490    * suffix-free command names. Normalize only the final argv[0] component;
    491    * ordinary command arguments such as `kit install cc.exe` stay strict. */
    492   {
    493     size_t n = driver_strlen(name);
    494     if (n > 4u && n - 4u < sizeof(normalized_name) && name[n - 4u] == '.' &&
    495         (name[n - 3u] == 'e' || name[n - 3u] == 'E') &&
    496         (name[n - 2u] == 'x' || name[n - 2u] == 'X') &&
    497         (name[n - 1u] == 'e' || name[n - 1u] == 'E')) {
    498       driver_memcpy(normalized_name, name, n - 4u);
    499       normalized_name[n - 4u] = '\0';
    500       name = normalized_name;
    501     }
    502   }
    503 
    504   /* Multi-call form first: argv[0] is the tool name (e.g. installed as
    505    * a `cc` symlink). Help inside the tool is gated by the tool itself. */
    506   rc = dispatch(name, argc, argv, ext);
    507   if (rc != -1) return rc;
    508 
    509   /* From here on argv[0] was the bare "kit" binary. Apply the
    510    * top-level help / `help <tool>` routing before delegating. */
    511   if (argc < 2) {
    512     driver_help_top();
    513     return 0;
    514   }
    515 
    516   if (driver_is_help_flag(argv[1]) || driver_streq(argv[1], "-h")) {
    517     driver_help_top();
    518     return 0;
    519   }
    520 
    521   if (driver_streq(argv[1], "--version")) {
    522     driver_print_version(NULL);
    523     return 0;
    524   }
    525 
    526   if (driver_streq(argv[1], "help")) {
    527     if (argc < 3) {
    528       driver_help_top();
    529       return 0;
    530     }
    531     if (print_tool_help(argv[2]) == 0) return 0;
    532     report_unknown_tool(argv[2]);
    533     return 2;
    534   }
    535 
    536   {
    537     char* tool_name = argv[1];
    538     argv[1] = argv[0];
    539     rc = dispatch(tool_name, argc - 1, argv + 1, ext);
    540     argv[1] = tool_name;
    541     if (rc != -1) return rc;
    542   }
    543 
    544   report_unknown_tool(argv[1]);
    545   return 2;
    546 }
    547 
    548 int driver_main(int argc, char** argv) { return kit_driver_main_ex(argc, argv, NULL); }
    549 
    550 #ifdef KIT_DRIVER_EXT_REGISTER
    551 extern KitStatus KIT_DRIVER_EXT_REGISTER(KitFrontendRegistry*);
    552 #endif
    553 
    554 int main(int argc, char** argv) {
    555 #ifdef KIT_DRIVER_EXT_REGISTER
    556   KitDriverExtension ext;
    557   ext.register_frontends = KIT_DRIVER_EXT_REGISTER;
    558   return kit_driver_main_ex(argc, argv, &ext);
    559 #else
    560   return kit_driver_main_ex(argc, argv, NULL);
    561 #endif
    562 }