| 863 | } |
| 864 | |
| 865 | CBMLanguage cbm_language_for_filename(const char *filename) { |
| 866 | if (!filename || !filename[0]) { |
| 867 | return CBM_LANG_COUNT; |
| 868 | } |
| 869 | |
| 870 | /* Check special filenames first */ |
| 871 | for (size_t i = 0; i < FILENAME_TABLE_SIZE; i++) { |
| 872 | if (strcmp(FILENAME_TABLE[i].filename, filename) == 0) { |
| 873 | return FILENAME_TABLE[i].language; |
| 874 | } |
| 875 | } |
| 876 | |
| 877 | /* DotEnv variant filenames (".env.local", ".env.production", …): the |
| 878 | * filename starts with ".env." but its last "extension" (e.g. ".local") |
| 879 | * is not a real language extension. Match the dotenv convention used by |
| 880 | * pass_envscan/pass_infrascan (".env" exact, ".env." prefix, "*.env" |
| 881 | * suffix) so file-index routing agrees with direct extraction. */ |
| 882 | if (strncmp(filename, ".env.", SLEN(".env.")) == 0) { |
| 883 | return CBM_LANG_DOTENV; |
| 884 | } |
| 885 | |
| 886 | /* Fall back to extension-based lookup. |
| 887 | * For compound extensions (e.g. ".blade.php") defined in the user config, |
| 888 | * scan from the first dot in the basename toward the last, checking user |
| 889 | * config at each position. Built-in extensions use the last dot only. */ |
| 890 | const char *last_dot = strrchr(filename, '.'); |
| 891 | if (!last_dot) { |
| 892 | return CBM_LANG_COUNT; |
| 893 | } |
| 894 | |
| 895 | /* Probe compound extensions (e.g. ".blade.php") from the first dot toward |
| 896 | * the last. Built-in compounds are checked first so e.g. Laravel Blade |
| 897 | * templates map to Blade rather than the single-extension fallback (PHP); |
| 898 | * user config can still add more (#258). */ |
| 899 | static const struct { |
| 900 | const char *ext; |
| 901 | CBMLanguage lang; |
| 902 | } COMPOUND_EXT_TABLE[] = { |
| 903 | {".blade.php", CBM_LANG_BLADE}, |
| 904 | }; |
| 905 | const cbm_userconfig_t *ucfg = cbm_get_user_lang_config(); |
| 906 | const char *p = strchr(filename, '.'); |
| 907 | while (p && p < last_dot) { |
| 908 | for (size_t i = 0; i < sizeof(COMPOUND_EXT_TABLE) / sizeof(COMPOUND_EXT_TABLE[0]); i++) { |
| 909 | if (strcmp(p, COMPOUND_EXT_TABLE[i].ext) == 0) { |
| 910 | return COMPOUND_EXT_TABLE[i].lang; |
| 911 | } |
| 912 | } |
| 913 | if (ucfg) { |
| 914 | CBMLanguage lang = cbm_userconfig_lookup(ucfg, p); |
| 915 | if (lang != CBM_LANG_COUNT) { |
| 916 | return lang; |
| 917 | } |
| 918 | } |
| 919 | p = strchr(p + SKIP_ONE, '.'); |
| 920 | } |
| 921 | |
| 922 | /* Standard single-extension lookup (built-ins + user overrides). */ |
no test coverage detected