| 5476 | /* ── Architecture aspect implementations ───────────────────────── */ |
| 5477 | |
| 5478 | static int arch_languages(cbm_store_t *s, const char *project, const char *path, |
| 5479 | cbm_architecture_info_t *out) { |
| 5480 | char norm[CBM_SZ_512]; |
| 5481 | char like[CBM_SZ_512]; |
| 5482 | bool scoped = arch_path_prepare(path, norm, sizeof(norm), like, sizeof(like)); |
| 5483 | char sqlbuf[ST_SQL_BUF]; |
| 5484 | const char *base = "SELECT file_path FROM nodes WHERE project=?1 AND label='File'"; |
| 5485 | if (scoped) { |
| 5486 | snprintf(sqlbuf, sizeof(sqlbuf), "%s%s", base, arch_path_scope_sql()); |
| 5487 | } else { |
| 5488 | snprintf(sqlbuf, sizeof(sqlbuf), "%s", base); |
| 5489 | } |
| 5490 | sqlite3_stmt *stmt = NULL; |
| 5491 | if (sqlite3_prepare_v2(s->db, sqlbuf, CBM_NOT_FOUND, &stmt, NULL) != SQLITE_OK) { |
| 5492 | store_set_error_sqlite(s, "arch_languages"); |
| 5493 | return CBM_STORE_ERR; |
| 5494 | } |
| 5495 | bind_text(stmt, SKIP_ONE, project); |
| 5496 | if (scoped) { |
| 5497 | arch_bind_path_scope(stmt, ST_COL_2, ST_COL_3, norm, like); |
| 5498 | } |
| 5499 | |
| 5500 | /* Count per language using a simple parallel array */ |
| 5501 | const char *lang_names[CBM_SZ_64]; |
| 5502 | int lang_counts[CBM_SZ_64]; |
| 5503 | int nlang = 0; |
| 5504 | |
| 5505 | int scan_rc20; |
| 5506 | while ((scan_rc20 = sqlite3_step(stmt)) == SQLITE_ROW) { |
| 5507 | const char *fp = (const char *)sqlite3_column_text(stmt, 0); |
| 5508 | const char *ext = file_ext(fp); |
| 5509 | const char *lang = ext_to_lang(ext); |
| 5510 | if (!lang) { |
| 5511 | continue; |
| 5512 | } |
| 5513 | int found = ST_FOUND; |
| 5514 | for (int i = 0; i < nlang; i++) { |
| 5515 | if (strcmp(lang_names[i], lang) == 0) { |
| 5516 | found = i; |
| 5517 | break; |
| 5518 | } |
| 5519 | } |
| 5520 | if (found >= 0) { |
| 5521 | lang_counts[found]++; |
| 5522 | } else if (nlang < CBM_SZ_64) { |
| 5523 | lang_names[nlang] = lang; |
| 5524 | lang_counts[nlang] = SKIP_ONE; |
| 5525 | nlang++; |
| 5526 | } |
| 5527 | } |
| 5528 | if (scan_rc20 != SQLITE_DONE) { /* SCANCHK:20:stmt */ |
| 5529 | store_set_error_sqlite(s, "row scan aborted"); |
| 5530 | sqlite3_finalize(stmt); |
| 5531 | return CBM_STORE_ERR; |
| 5532 | } |
| 5533 | sqlite3_finalize(stmt); |
| 5534 | |
| 5535 | /* Sort by count descending (simple insertion sort) */ |
no test coverage detected