Skip to content

Commit 95ba846

Browse files
feat: add unique_file RAII wrapper for FILE* management
Add FileCloser functor and unique_file type alias (unique_ptr<FILE, FileCloser>) to io.hpp, along with open_file() helper. Refactor read_all() and write_all() to use RAII, eliminating manual fclose. Update tee.cpp to use unique_file vector instead of raw FILE* with manual cleanup loop.
1 parent e9c35e2 commit 95ba846

2 files changed

Lines changed: 26 additions & 21 deletions

File tree

include/cfbox/io.hpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <cstdio>
4+
#include <memory>
45
#include <string>
56
#include <string_view>
67
#include <vector>
@@ -9,20 +10,31 @@
910

1011
namespace cfbox::io {
1112

12-
inline auto read_all(std::string_view path) -> base::Result<std::string> {
13-
std::FILE* f = std::fopen(std::string{path}.c_str(), "rb");
13+
struct FileCloser {
14+
void operator()(std::FILE* f) const noexcept {
15+
if (f) std::fclose(f);
16+
}
17+
};
18+
using unique_file = std::unique_ptr<std::FILE, FileCloser>;
19+
20+
inline auto open_file(std::string_view path, const char* mode) -> base::Result<unique_file> {
21+
auto* f = std::fopen(std::string{path}.c_str(), mode);
1422
if (!f) {
1523
return std::unexpected(base::Error{errno, "cannot open file: " + std::string{path}});
1624
}
25+
return unique_file{f};
26+
}
1727

18-
std::fseek(f, 0, SEEK_END);
19-
long size = std::ftell(f);
20-
std::fseek(f, 0, SEEK_SET);
28+
inline auto read_all(std::string_view path) -> base::Result<std::string> {
29+
CFBOX_TRY(f, open_file(path, "rb"));
30+
31+
std::fseek(f->get(), 0, SEEK_END);
32+
long size = std::ftell(f->get());
33+
std::fseek(f->get(), 0, SEEK_SET);
2134

2235
std::string content(static_cast<std::size_t>(size), '\0');
23-
auto nread = std::fread(content.data(), 1, content.size(), f);
36+
auto nread = std::fread(content.data(), 1, content.size(), f->get());
2437
content.resize(nread);
25-
std::fclose(f);
2638
return content;
2739
}
2840

@@ -71,13 +83,8 @@ inline auto split_lines(const std::string& content) -> std::vector<std::string>
7183
}
7284

7385
inline auto write_all(std::string_view path, std::string_view data) -> base::Result<void> {
74-
std::FILE* f = std::fopen(std::string{path}.c_str(), "wb");
75-
if (!f) {
76-
return std::unexpected(
77-
base::Error{errno, "cannot open file for writing: " + std::string{path}});
78-
}
79-
auto written = std::fwrite(data.data(), 1, data.size(), f);
80-
std::fclose(f);
86+
CFBOX_TRY(f, open_file(path, "wb"));
87+
auto written = std::fwrite(data.data(), 1, data.size(), f->get());
8188
if (written != data.size()) {
8289
return std::unexpected(base::Error{errno, "write failed: " + std::string{path}});
8390
}

src/applets/tee.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <cfbox/args.hpp>
77
#include <cfbox/help.hpp>
8+
#include <cfbox/io.hpp>
89

910
namespace {
1011
constexpr cfbox::help::HelpEntry HELP = {
@@ -28,29 +29,26 @@ auto tee_main(int argc, char* argv[]) -> int {
2829
bool append = parsed.has('a');
2930
const auto& pos = parsed.positional();
3031

31-
std::vector<std::FILE*> files;
32+
std::vector<cfbox::io::unique_file> files;
3233
for (auto p : pos) {
3334
auto* f = std::fopen(std::string{p}.c_str(), append ? "ab" : "wb");
3435
if (!f) {
3536
std::fprintf(stderr, "cfbox tee: %s: %s\n", std::string{p}.c_str(), std::strerror(errno));
3637
} else {
37-
files.push_back(f);
38+
files.emplace_back(f);
3839
}
3940
}
4041

4142
char buf[4096];
4243
int rc = 0;
4344
while (auto n = std::fread(buf, 1, sizeof(buf), stdin)) {
4445
std::fwrite(buf, 1, n, stdout);
45-
for (auto* f : files) {
46-
if (std::fwrite(buf, 1, n, f) != n) {
46+
for (auto& f : files) {
47+
if (std::fwrite(buf, 1, n, f.get()) != n) {
4748
rc = 1;
4849
}
4950
}
5051
}
5152

52-
for (auto* f : files) {
53-
std::fclose(f);
54-
}
5553
return rc;
5654
}

0 commit comments

Comments
 (0)