| 1308 | } |
| 1309 | |
| 1310 | char *cbm_fqn_folder(CBMArena *a, const char *project, const char *rel_dir) { |
| 1311 | // project.dir1.dir2 |
| 1312 | size_t proj_len = strlen(project); |
| 1313 | size_t dir_len = strlen(rel_dir); |
| 1314 | size_t max_len = proj_len + SKIP_ONE + dir_len + SKIP_ONE; |
| 1315 | char *buf = (char *)cbm_arena_alloc(a, max_len); |
| 1316 | if (!buf) { |
| 1317 | return NULL; |
| 1318 | } |
| 1319 | |
| 1320 | char *out = buf; |
| 1321 | memcpy(out, project, proj_len); |
| 1322 | out += proj_len; |
| 1323 | |
| 1324 | if (dir_len > 0 && !(dir_len == SKIP_ONE && rel_dir[0] == '.')) { |
| 1325 | const char *start = rel_dir; |
| 1326 | const char *end_ptr = rel_dir + dir_len; |
| 1327 | while (start < end_ptr) { |
| 1328 | const char *slash = (const char *)memchr(start, '/', end_ptr - start); |
| 1329 | const char *part_end = slash ? slash : end_ptr; |
| 1330 | size_t part_len = (size_t)(part_end - start); |
| 1331 | if (part_len > 0) { |
| 1332 | *out++ = '.'; |
| 1333 | memcpy(out, start, part_len); |
| 1334 | out += part_len; |
| 1335 | } |
| 1336 | start = part_end + SKIP_ONE; |
| 1337 | } |
| 1338 | } |
| 1339 | *out = '\0'; |
| 1340 | return buf; |
| 1341 | } |
| 1342 | |
| 1343 | /* ── String literal classifier ──────────────────────────────────── */ |
| 1344 |
no test coverage detected