| 1333 | } |
| 1334 | |
| 1335 | int cbm_upsert_instructions(const char *path, const char *content) { |
| 1336 | if (!path || !content) { |
| 1337 | return CLI_ERR; |
| 1338 | } |
| 1339 | |
| 1340 | size_t existing_len = 0; |
| 1341 | char *existing = read_file_str(path, &existing_len); |
| 1342 | |
| 1343 | /* Build the marker-wrapped section */ |
| 1344 | size_t section_len = strlen(CMM_MARKER_START) + CLI_SKIP_ONE + strlen(content) + |
| 1345 | strlen(CMM_MARKER_END) + CLI_SKIP_ONE; |
| 1346 | char *section = malloc(section_len + CLI_SKIP_ONE); |
| 1347 | if (!section) { |
| 1348 | free(existing); |
| 1349 | return CLI_ERR; |
| 1350 | } |
| 1351 | snprintf(section, section_len + SKIP_ONE, "%s\n%s%s\n", CMM_MARKER_START, content, |
| 1352 | CMM_MARKER_END); |
| 1353 | |
| 1354 | if (!existing) { |
| 1355 | /* File doesn't exist — create with just the section */ |
| 1356 | int rc = write_file_str(path, section); |
| 1357 | free(section); |
| 1358 | return rc; |
| 1359 | } |
| 1360 | |
| 1361 | /* Check if markers already exist */ |
| 1362 | char *start = strstr(existing, CMM_MARKER_START); |
| 1363 | char *end = start ? strstr(start, CMM_MARKER_END) : NULL; |
| 1364 | |
| 1365 | char *result; |
| 1366 | if (start && end) { |
| 1367 | /* Replace between markers (including markers themselves) */ |
| 1368 | end += strlen(CMM_MARKER_END); |
| 1369 | /* Skip trailing newline after end marker */ |
| 1370 | if (*end == '\n') { |
| 1371 | end++; |
| 1372 | } |
| 1373 | |
| 1374 | size_t prefix_len = (size_t)(start - existing); |
| 1375 | size_t suffix_len = strlen(end); |
| 1376 | size_t new_len = prefix_len + strlen(section) + suffix_len; |
| 1377 | result = malloc(new_len + CLI_SKIP_ONE); |
| 1378 | if (!result) { |
| 1379 | free(existing); |
| 1380 | free(section); |
| 1381 | return CLI_ERR; |
| 1382 | } |
| 1383 | memcpy(result, existing, prefix_len); |
| 1384 | memcpy(result + prefix_len, section, strlen(section)); |
| 1385 | memcpy(result + prefix_len + strlen(section), end, suffix_len); |
| 1386 | result[new_len] = '\0'; |
| 1387 | } else { |
| 1388 | /* Append section */ |
| 1389 | size_t new_len = existing_len + CLI_SKIP_ONE + strlen(section); |
| 1390 | if (new_len > (size_t)CLI_MB_10 * CLI_MB_FACTOR) { /* 10 MB safety cap */ |
| 1391 | free(existing); |
| 1392 | free(section); |
no test coverage detected