Detect language for a file, handling .m disambiguation and JSON filtering. */
| 687 | |
| 688 | /* Detect language for a file, handling .m disambiguation and JSON filtering. */ |
| 689 | static CBMLanguage detect_file_language(const char *entry_name, const char *abs_path) { |
| 690 | CBMLanguage lang = cbm_language_for_filename(entry_name); |
| 691 | if (lang == CBM_LANG_COUNT) { |
| 692 | return CBM_LANG_COUNT; |
| 693 | } |
| 694 | /* Special: .m files need content-based disambiguation */ |
| 695 | const char *dot = strrchr(entry_name, '.'); |
| 696 | if (dot && strcmp(dot, ".m") == 0) { |
| 697 | lang = cbm_disambiguate_m(abs_path); |
| 698 | } |
| 699 | /* Special: .cls is shared by ObjectScript UDL and Apex */ |
| 700 | if (dot && strcmp(dot, ".cls") == 0) { |
| 701 | lang = cbm_disambiguate_cls(abs_path); |
| 702 | } |
| 703 | /* Special: .inc is shared by BitBake and ObjectScript include files */ |
| 704 | if (dot && strcmp(dot, ".inc") == 0) { |
| 705 | lang = cbm_disambiguate_inc(abs_path); |
| 706 | } |
| 707 | /* Special: ObjectScript Studio Export XML (<Export generator="...">) is |
| 708 | * detected by content; otherwise .xml stays XML. */ |
| 709 | if (lang == CBM_LANG_XML) { |
| 710 | FILE *xf = cbm_fopen(abs_path, "r"); |
| 711 | if (xf) { |
| 712 | char xbuf[CBM_SZ_256]; |
| 713 | size_t xn = fread(xbuf, SKIP_ONE, sizeof(xbuf) - SKIP_ONE, xf); |
| 714 | (void)fclose(xf); |
| 715 | xbuf[xn] = '\0'; |
| 716 | if (strstr(xbuf, "<Export generator=")) { |
| 717 | return CBM_LANG_OBJECTSCRIPT_EXPORT; |
| 718 | } |
| 719 | } |
| 720 | } |
| 721 | /* Check ignored JSON files */ |
| 722 | if (lang == CBM_LANG_JSON && str_in_list(entry_name, IGNORED_JSON_FILES)) { |
| 723 | return CBM_LANG_COUNT; |
| 724 | } |
| 725 | return lang; |
| 726 | } |
| 727 | |
| 728 | /* UTF-8-safe stat: wide API on Windows, regular stat on POSIX. */ |
| 729 | static int wide_stat(const char *path, struct stat *st) { |
no test coverage detected