| 3646 | /* ── Architecture aspect implementations ───────────────────────── */ |
| 3647 | |
| 3648 | static int arch_languages(cbm_store_t *s, const char *project, const char *path, |
| 3649 | cbm_architecture_info_t *out) { |
| 3650 | char norm[CBM_SZ_512]; |
| 3651 | char like[CBM_SZ_512]; |
| 3652 | bool scoped = arch_path_prepare(path, norm, sizeof(norm), like, sizeof(like)); |
| 3653 | char sqlbuf[ST_SQL_BUF]; |
| 3654 | const char *base = "SELECT file_path FROM nodes WHERE project=?1 AND label='File'"; |
| 3655 | if (scoped) { |
| 3656 | snprintf(sqlbuf, sizeof(sqlbuf), "%s%s", base, arch_path_scope_sql()); |
| 3657 | } else { |
| 3658 | snprintf(sqlbuf, sizeof(sqlbuf), "%s", base); |
| 3659 | } |
| 3660 | sqlite3_stmt *stmt = NULL; |
| 3661 | if (sqlite3_prepare_v2(s->db, sqlbuf, CBM_NOT_FOUND, &stmt, NULL) != SQLITE_OK) { |
| 3662 | store_set_error_sqlite(s, "arch_languages"); |
| 3663 | return CBM_STORE_ERR; |
| 3664 | } |
| 3665 | bind_text(stmt, SKIP_ONE, project); |
| 3666 | if (scoped) { |
| 3667 | arch_bind_path_scope(stmt, ST_COL_2, ST_COL_3, norm, like); |
| 3668 | } |
| 3669 | |
| 3670 | /* Count per language using a simple parallel array */ |
| 3671 | const char *lang_names[CBM_SZ_64]; |
| 3672 | int lang_counts[CBM_SZ_64]; |
| 3673 | int nlang = 0; |
| 3674 | |
| 3675 | while (sqlite3_step(stmt) == SQLITE_ROW) { |
| 3676 | const char *fp = (const char *)sqlite3_column_text(stmt, 0); |
| 3677 | const char *ext = file_ext(fp); |
| 3678 | const char *lang = ext_to_lang(ext); |
| 3679 | if (!lang) { |
| 3680 | continue; |
| 3681 | } |
| 3682 | int found = ST_FOUND; |
| 3683 | for (int i = 0; i < nlang; i++) { |
| 3684 | if (strcmp(lang_names[i], lang) == 0) { |
| 3685 | found = i; |
| 3686 | break; |
| 3687 | } |
| 3688 | } |
| 3689 | if (found >= 0) { |
| 3690 | lang_counts[found]++; |
| 3691 | } else if (nlang < CBM_SZ_64) { |
| 3692 | lang_names[nlang] = lang; |
| 3693 | lang_counts[nlang] = SKIP_ONE; |
| 3694 | nlang++; |
| 3695 | } |
| 3696 | } |
| 3697 | sqlite3_finalize(stmt); |
| 3698 | |
| 3699 | /* Sort by count descending (simple insertion sort) */ |
| 3700 | for (int i = SKIP_ONE; i < nlang; i++) { |
| 3701 | int j = i; |
| 3702 | while (j > 0 && lang_counts[j] > lang_counts[j - SKIP_ONE]) { |
| 3703 | int tc = lang_counts[j]; |
| 3704 | lang_counts[j] = lang_counts[j - SKIP_ONE]; |
| 3705 | lang_counts[j - SKIP_ONE] = tc; |
no test coverage detected