write tool fixes

This commit is contained in:
Luxferre
2026-10-14 12:19:11 +03:00
parent 59c85e9217
commit 05bce7ca9c
4 changed files with 31 additions and 6 deletions
+2 -2
View File
@@ -197,8 +197,8 @@ int rune_width(int cp);
void mkdir_p(std::string_view dir); void mkdir_p(std::string_view dir);
std::string project_id(); std::string project_id();
std::string bantam_tmp_dir(); std::string bantam_tmp_dir(std::string_view sess_id = "");
std::string tool_res_dir(); std::string tool_res_dir(std::string_view sess_id = "");
std::string md5_hex(std::string_view data); std::string md5_hex(std::string_view data);
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
+6
View File
@@ -229,6 +229,8 @@ static int AL(Cfg &cfg, Messages &msgs, Usage &out, volatile int *interrupted, s
else if (!has_line && !has_byte && !has_del_lines && !has_del_bytes) { else if (!has_line && !has_byte && !has_del_lines && !has_del_bytes) {
/* No offset/deletion parameters: overwrite the whole file. */ /* No offset/deletion parameters: overwrite the whole file. */
std::string c = filter_text(cv); std::string c = filter_text(cv);
/* Ensure the written file ends with a single trailing newline. */
if (!c.empty() && c.back() != '\n') c.push_back('\n');
auto sl = fp.find_last_of('/'); auto sl = fp.find_last_of('/');
if (sl != std::string::npos) mkdir_p(std::string_view(fp).substr(0, sl)); if (sl != std::string::npos) mkdir_p(std::string_view(fp).substr(0, sl));
else mkdir_p("."); else mkdir_p(".");
@@ -236,6 +238,10 @@ static int AL(Cfg &cfg, Messages &msgs, Usage &out, volatile int *interrupted, s
else { res = sfmt("Successfully wrote %zu bytes to %s", c.size(), fp.c_str()); } else { res = sfmt("Successfully wrote %zu bytes to %s", c.size(), fp.c_str()); }
} else { } else {
int werr = 0; std::string c = filter_text(cv); int werr = 0; std::string c = filter_text(cv);
/* Line-based edits keep the text-file trailing-newline convention;
* byte-based edits are exact byte operations and must not have a
* newline injected. */
if (!(has_byte || has_del_bytes) && !c.empty() && c.back() != '\n') c.push_back('\n');
res = write_file(fp, line_off, byte_off, del_lines, del_bytes, c, &werr); res = write_file(fp, line_off, byte_off, del_lines, del_bytes, c, &werr);
if (werr) sty = 31; if (werr) sty = 31;
} }
+7
View File
@@ -291,6 +291,13 @@ std::string write_file(std::string_view path, long line_offset, long byte_offset
nd.append(data, 0, prefix_len); nd.append(data, 0, prefix_len);
nd.append(content); nd.append(content);
if (suffix_len) nd.append(data, end_del, suffix_len); if (suffix_len) nd.append(data, end_del, suffix_len);
/* Keep a single trailing newline for line-based / whole-file text writes.
* Byte-based edits are exact byte operations and must not be mutated with an
* injected newline, so the guard is skipped whenever a byte offset or byte
* deletion is actually in effect. */
bool byte_based = (line_offset < 0 && byte_offset >= 0) ||
(del_lines < 0 && del_bytes >= 0);
if (!byte_based && !nd.empty() && nd.back() != '\n') nd.push_back('\n');
if (!write_file_str(p, nd)) { if (!write_file_str(p, nd)) {
*err = 1; *err = 1;
return sfmt("[tool error: write %s: %s]", p.c_str(), strerror(errno)); return sfmt("[tool error: write %s: %s]", p.c_str(), strerror(errno));
+16 -4
View File
@@ -192,15 +192,27 @@ void mkdir_p(std::string_view dir) {
} }
mkdir(path.c_str(), 0755); mkdir(path.c_str(), 0755);
} }
std::string bantam_tmp_dir() { std::string bantam_tmp_dir(std::string_view sess_id) {
const char *tmp = std::getenv("TMPDIR"); const char *tmp = std::getenv("TMPDIR");
if (!tmp || !*tmp) tmp = "/tmp"; if (!tmp || !*tmp) tmp = "/tmp";
std::string p = std::string(tmp) + "/bantam"; std::string base(tmp);
/* Drop a single trailing slash so we never build a "//bantam" path (which
* some platforms treat as a special "super-root"); keep "/" itself intact. */
if (base.size() > 1 && base.back() == '/') base.pop_back();
std::string p = base;
if (p != "/") p += "/";
p += "bantam";
/* Segregate temporary artifacts per session so different projects/sessions
* never share or collide on scratch files. */
std::string sid(sess_id);
if (sid.empty()) sid = project_id();
if (!sid.empty()) { p += "/"; p += sid; }
mkdir_p(p); mkdir_p(p);
return p; return p;
} }
std::string tool_res_dir() {
std::string base = bantam_tmp_dir(); std::string tool_res_dir(std::string_view sess_id) {
std::string base = bantam_tmp_dir(sess_id);
std::string p = base + "/toolres"; std::string p = base + "/toolres";
mkdir_p(p); mkdir_p(p);
return p; return p;