Create / -XXXXXX exclusively and return a stream on the * descriptor. On failure `path_out` is emptied so cleanup skips it. */
| 9744 | /* Create <scratch>/<basename>-XXXXXX exclusively and return a stream on the |
| 9745 | * descriptor. On failure `path_out` is emptied so cleanup skips it. */ |
| 9746 | static FILE *search_scratch_file(const char *dir, const char *basename, char *path_out, |
| 9747 | size_t path_sz) { |
| 9748 | path_out[0] = '\0'; |
| 9749 | int written = snprintf(path_out, path_sz, "%s/%s-XXXXXX", dir, basename); |
| 9750 | if (written <= 0 || (size_t)written >= path_sz) { |
| 9751 | path_out[0] = '\0'; |
| 9752 | return NULL; |
| 9753 | } |
| 9754 | int descriptor = cbm_mkstemp(path_out); |
| 9755 | if (descriptor < 0) { |
| 9756 | path_out[0] = '\0'; |
| 9757 | return NULL; |
| 9758 | } |
| 9759 | /* Binary mode: the file list uses an explicit per-platform record separator |
| 9760 | * (NUL for xargs -0, newline for PowerShell) that CRLF translation would |
| 9761 | * corrupt — the same reason the previous code opened it "wb". */ |
| 9762 | FILE *stream = mcp_fdopen(descriptor, "wb"); |
| 9763 | if (!stream) { |
| 9764 | (void)mcp_close(descriptor); |
| 9765 | (void)cbm_unlink(path_out); |
| 9766 | path_out[0] = '\0'; |
| 9767 | } |
| 9768 | return stream; |
| 9769 | } |
| 9770 | |
| 9771 | /* Anchored cleanup: removes both scratch files and the private directory. Safe |
| 9772 | * to call more than once and on any partially-initialised scratch, so every |
no test coverage detected