resolve shell from PATH (fallback /bin/sh); rewrite write tool with lineOffset/byteOffset + delLines/delBytes
This commit is contained in:
@@ -15,6 +15,34 @@
|
||||
#include <vector>
|
||||
|
||||
/* ----------------------------- shell ---------------------------------- */
|
||||
/* Resolve the Bourne-again shell from the system PATH. Returns a static
|
||||
* buffer holding the absolute path to `bash`, or "/bin/sh" when bash cannot
|
||||
* be located anywhere on PATH. Never returns a hardcoded bash path. */
|
||||
static const char *resolve_shell() {
|
||||
static char buf[4096];
|
||||
const char *path = getenv("PATH");
|
||||
if (path && *path) {
|
||||
std::string p(path);
|
||||
size_t start = 0;
|
||||
while (start <= p.size()) {
|
||||
size_t end = p.find(':', start);
|
||||
std::string dir = (end == std::string::npos) ? p.substr(start)
|
||||
: p.substr(start, end - start);
|
||||
if (!dir.empty() && dir.back() != '/') dir += '/';
|
||||
std::string cand = dir + "bash";
|
||||
struct stat st;
|
||||
if (stat(cand.c_str(), &st) == 0 && S_ISREG(st.st_mode) &&
|
||||
(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) {
|
||||
snprintf(buf, sizeof buf, "%s", cand.c_str());
|
||||
return buf;
|
||||
}
|
||||
if (end == std::string::npos) break;
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
return "/bin/sh";
|
||||
}
|
||||
|
||||
std::string shell_with_workdir(std::string_view cmd, std::string_view workdir,
|
||||
int timeout, volatile int *interrupted) {
|
||||
std::string fcmd = filter_text(cmd);
|
||||
@@ -35,7 +63,9 @@ std::string shell_with_workdir(std::string_view cmd, std::string_view workdir,
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
if (!workdir.empty()) chdir(std::string(workdir).c_str());
|
||||
execl("/bin/bash", "bash", "-c", fcmd.c_str(), (char *)nullptr);
|
||||
const char *sh = resolve_shell();
|
||||
const char *shname = (strrchr(sh, '/') ? strrchr(sh, '/') + 1 : sh);
|
||||
execl(sh, shname, "-c", fcmd.c_str(), (char *)nullptr);
|
||||
_exit(127);
|
||||
}
|
||||
close(pipefd[1]);
|
||||
@@ -171,7 +201,38 @@ std::string read_file_or_dir(std::string_view path, int offset, int limit, int *
|
||||
}
|
||||
|
||||
/* ----------------------------- write ---------------------------------- */
|
||||
std::string write_file(std::string_view path, int offset, int del_bytes,
|
||||
/* Convert a 1-indexed line number to the byte offset of the start of that
|
||||
* line within `data`. Line 1 begins at byte 0; a line number past the end of
|
||||
* the file maps to data.size() (i.e. append at end of file). */
|
||||
static size_t line_to_byte_offset(std::string_view data, long line_no) {
|
||||
if (line_no <= 1) return 0;
|
||||
size_t pos = 0;
|
||||
long cur = 1;
|
||||
while (pos < data.size()) {
|
||||
if (data[pos] == '\n') {
|
||||
cur++;
|
||||
if (cur == line_no) return pos + 1;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/* Byte length of `nlines` complete lines beginning at byte offset `start`.
|
||||
* Counts the newlines terminating each line and stops at end of data. */
|
||||
static size_t lines_to_byte_len(std::string_view data, size_t start, long nlines) {
|
||||
if (nlines <= 0 || start > data.size()) return 0;
|
||||
size_t pos = start;
|
||||
long consumed = 0;
|
||||
while (pos < data.size() && consumed < nlines) {
|
||||
if (data[pos] == '\n') consumed++;
|
||||
pos++;
|
||||
}
|
||||
return pos - start;
|
||||
}
|
||||
|
||||
std::string write_file(std::string_view path, long line_offset, long byte_offset,
|
||||
long del_lines, long del_bytes,
|
||||
std::string_view content, int *err) {
|
||||
*err = 0;
|
||||
std::string p(path);
|
||||
@@ -179,8 +240,6 @@ std::string write_file(std::string_view path, int offset, int del_bytes,
|
||||
while (s < p.size() && (p[s] == ' ' || p[s] == '\t')) s++;
|
||||
p = p.substr(s);
|
||||
if (p.empty()) { *err = 1; return "[tool error: path is required]"; }
|
||||
if (offset < 0) offset = 0;
|
||||
if (del_bytes < 0) del_bytes = 0;
|
||||
|
||||
std::string data;
|
||||
struct stat st;
|
||||
@@ -198,11 +257,32 @@ std::string write_file(std::string_view path, int offset, int del_bytes,
|
||||
if (slash != std::string::npos) mkdir_p(std::string_view(p).substr(0, slash));
|
||||
else mkdir_p(".");
|
||||
}
|
||||
if ((size_t)offset > data.size()) {
|
||||
data.resize((size_t)offset, '\0');
|
||||
|
||||
/* Resolve insertion byte offset. Line-based takes precedence over byte-based
|
||||
* ("first and foremost line-based writing"); if neither is given the whole
|
||||
* file is overwritten by the caller. */
|
||||
size_t ins_off = 0;
|
||||
if (line_offset >= 0) {
|
||||
ins_off = line_to_byte_offset(data, line_offset);
|
||||
} else if (byte_offset >= 0) {
|
||||
ins_off = (size_t)byte_offset;
|
||||
if (ins_off > data.size()) data.resize(ins_off, '\0');
|
||||
} else {
|
||||
ins_off = 0;
|
||||
}
|
||||
size_t end_del = (size_t)offset + (size_t)del_bytes;
|
||||
size_t prefix_len = (size_t)offset;
|
||||
|
||||
/* Resolve deletion span. Line-based takes precedence over byte-based. */
|
||||
size_t del_len = 0;
|
||||
if (del_lines >= 0) {
|
||||
del_len = lines_to_byte_len(data, ins_off, del_lines);
|
||||
} else if (del_bytes >= 0) {
|
||||
del_len = (size_t)del_bytes;
|
||||
if (ins_off + del_len > data.size())
|
||||
del_len = (ins_off < data.size()) ? (data.size() - ins_off) : 0;
|
||||
}
|
||||
|
||||
size_t end_del = ins_off + del_len;
|
||||
size_t prefix_len = ins_off;
|
||||
size_t suffix_len = (end_del < data.size()) ? (data.size() - end_del) : 0;
|
||||
size_t clen = content.size();
|
||||
size_t newlen = prefix_len + clen + suffix_len;
|
||||
@@ -220,7 +300,7 @@ std::string write_file(std::string_view path, int offset, int del_bytes,
|
||||
|
||||
/* ----------------------------- offload -------------------------------- */
|
||||
std::string offload_tool_result(std::string_view res, int max_res,
|
||||
std::vector<std::string> &tmps) {
|
||||
std::vector<std::string> &tmps) {
|
||||
if (max_res <= 0) max_res = 65536;
|
||||
if (res.size() <= (size_t)max_res) return std::string(res);
|
||||
std::string dir = tool_res_dir();
|
||||
|
||||
Reference in New Issue
Block a user