style: 应用 clang-format
This commit is contained in:
1 parent
016a45bc6e
commit
d1612f1d31
65 files changed
+7288
-5983
No files matched your search
+60
-46
@@ -1,45 +1,50 @@
|
||||
/* confirm.c - 写操作确认 */
|
||||
#include "confirm.h"
|
||||
#include "i18n.h"
|
||||
#include "tools.h" /* TOOL_* 宏: 工具名只在那里定义一次 */
|
||||
#include "tools.h" /* TOOL_* 宏: 工具名只在那里定义一次 */
|
||||
#include "util.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BOLD "\033[1m"
|
||||
#define DIM "\033[2m"
|
||||
#define YEL "\033[33m"
|
||||
#define OFF "\033[0m"
|
||||
#define DIM "\033[2m"
|
||||
#define YEL "\033[33m"
|
||||
#define OFF "\033[0m"
|
||||
|
||||
static int g_enabled = 1;
|
||||
static ConfirmDescriber g_describer;
|
||||
static int g_color;
|
||||
static int g_turn = CONFIRM_ASK; /* 本轮的提问策略, 每轮复位 */
|
||||
static int g_turn = CONFIRM_ASK; /* 本轮的提问策略, 每轮复位 */
|
||||
|
||||
static const char *c(const char *code) { return g_color ? code : ""; }
|
||||
|
||||
/* 截断并压掉换行, 保持在一行内 */
|
||||
static void preview(const char *s, size_t max, char *out, size_t outlen)
|
||||
{
|
||||
if (!s) { snprintf(out, outlen, "%s", t("confirm.val.empty")); return; }
|
||||
static void preview(const char *s, size_t max, char *out, size_t outlen) {
|
||||
if (!s) {
|
||||
snprintf(out, outlen, "%s", t("confirm.val.empty"));
|
||||
return;
|
||||
}
|
||||
size_t n = 0;
|
||||
for (const char *p = s; *p && n + 1 < outlen; p++) {
|
||||
if (*p == '\n' || *p == '\r' || *p == '\t') {
|
||||
if (n + 1 < outlen) out[n++] = ' ';
|
||||
if (n + 1 < outlen)
|
||||
out[n++] = ' ';
|
||||
continue;
|
||||
}
|
||||
out[n++] = *p;
|
||||
if (n >= max) break;
|
||||
if (n >= max)
|
||||
break;
|
||||
}
|
||||
out[n] = '\0';
|
||||
if (s[n] && n >= max) {
|
||||
size_t l = strlen(out);
|
||||
if (l + 4 < outlen) snprintf(out + l, outlen - l, "...");
|
||||
if (l + 4 < outlen)
|
||||
snprintf(out + l, outlen - l, "...");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,77 +52,82 @@ static void preview(const char *s, size_t max, char *out, size_t outlen)
|
||||
* 同一份, 两处对"改的是哪几行"的说法必须一致. */
|
||||
|
||||
/* 把这次调用做什么讲清楚 —— 只说工具名等于没说, 用户没法判断 */
|
||||
static void describe(const char *name, const JsonValue *args)
|
||||
{
|
||||
static void describe(const char *name, const JsonValue *args) {
|
||||
if (g_describer) {
|
||||
char *text = g_describer(name, args);
|
||||
if (text) { fprintf(stderr, "%s", text); free(text); return; }
|
||||
if (text) {
|
||||
fprintf(stderr, "%s", text);
|
||||
free(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
char buf[256], num[32];
|
||||
const char *path = json_get_string(args, "path");
|
||||
|
||||
if (strcmp(name, TOOL_RUN_COMMAND) == 0) {
|
||||
preview(json_get_string(args, "command"), 200, buf, sizeof buf);
|
||||
fprintf(stderr, " %s %s%s%s\n", t("confirm.lbl.cmd"), c(BOLD), buf, c(OFF));
|
||||
fprintf(stderr, " %s %s%s%s\n", t("confirm.lbl.cmd"), c(BOLD), buf,
|
||||
c(OFF));
|
||||
} else if (strcmp(name, TOOL_WRITE_FILE) == 0) {
|
||||
const char *content = json_get_string(args, "content");
|
||||
preview(path, 160, buf, sizeof buf);
|
||||
snprintf(num, sizeof num, "%zu", content ? strlen(content) : 0);
|
||||
fprintf(stderr, " %s %s%s%s %s\n", t("confirm.lbl.write"), c(BOLD), buf, c(OFF),
|
||||
tf("confirm.note.overwrite", "bytes", num, NULL));
|
||||
fprintf(stderr, " %s %s%s%s %s\n", t("confirm.lbl.write"), c(BOLD),
|
||||
buf, c(OFF), tf("confirm.note.overwrite", "bytes", num, NULL));
|
||||
} else if (strcmp(name, TOOL_EDIT_FILE) == 0) {
|
||||
char lines[96];
|
||||
preview(path, 160, buf, sizeof buf);
|
||||
tool_lines_label(args, lines, sizeof lines);
|
||||
/* 行号必须打出来: 用户要确认的第一件事就是"改的是哪几行".
|
||||
/* 行号必须打出来: 用户要确认的第一件事就是"改的是哪几行".
|
||||
* 没给 line 就是全文件替换 (工具不拒绝它), 摘要里直说"全文件". */
|
||||
const char *scope = lines[0] ? lines : t("tool.call.all_lines");
|
||||
if (json_get_bool(args, "replace_all", 0))
|
||||
scope = tf("tool.call.scope_all", "scope", scope, NULL);
|
||||
fprintf(stderr, " %s %s\"%s\" [%s]%s\n", t("confirm.lbl.edit"),
|
||||
c(BOLD), buf, scope, c(OFF));
|
||||
/* 然后摆出**带行号**的新旧对照, 再问 Y/n —— 用户就是照着它决定放不放行.
|
||||
/* 然后摆出**带行号**的新旧对照, 再问 Y/n —— 用户就是照着它决定放不放行.
|
||||
* 旧版这里打的是"替换 <旧> -> <新>": 两边各截 60 字符、压成一行, 看不出
|
||||
* 到底动了几行、动的是哪几行, 长一点的改动更是只剩两个省略号. */
|
||||
tool_render_diff(stderr, args, " ", g_color);
|
||||
} else if (path) {
|
||||
preview(path, 160, buf, sizeof buf);
|
||||
fprintf(stderr, " %s %s%s%s\n", t("confirm.lbl.path"), c(BOLD), buf, c(OFF));
|
||||
fprintf(stderr, " %s %s%s%s\n", t("confirm.lbl.path"), c(BOLD), buf,
|
||||
c(OFF));
|
||||
}
|
||||
}
|
||||
|
||||
void confirm_set_describer(ConfirmDescriber fn)
|
||||
{
|
||||
g_describer = fn;
|
||||
}
|
||||
void confirm_set_describer(ConfirmDescriber fn) { g_describer = fn; }
|
||||
|
||||
void confirm_init(int enabled)
|
||||
{
|
||||
void confirm_init(int enabled) {
|
||||
g_enabled = enabled ? 1 : 0;
|
||||
g_color = isatty(STDERR_FILENO) ? 1 : 0;
|
||||
}
|
||||
|
||||
void confirm_set_enabled(int on) { g_enabled = on ? 1 : 0; }
|
||||
int confirm_enabled(void) { return g_enabled; }
|
||||
int confirm_enabled(void) { return g_enabled; }
|
||||
|
||||
void confirm_reset_turn(void) { g_turn = CONFIRM_ASK; }
|
||||
int confirm_turn_mode(void) { return g_turn; }
|
||||
int confirm_turn_mode(void) { return g_turn; }
|
||||
|
||||
int confirm_tool(const char *name, const JsonValue *args)
|
||||
{
|
||||
if (!g_enabled) return 1;
|
||||
int confirm_tool(const char *name, const JsonValue *args) {
|
||||
if (!g_enabled)
|
||||
return 1;
|
||||
|
||||
/* 本轮已经表过态就不再打扰. Always/neVer 只作用于这一轮:
|
||||
/* 本轮已经表过态就不再打扰. Always/neVer 只作用于这一轮:
|
||||
* 下一条用户消息进来时 agent_run 会把策略复位成 ASK. */
|
||||
if (g_turn == CONFIRM_ALWAYS) return 1;
|
||||
if (g_turn == CONFIRM_NEVER) return 0;
|
||||
if (g_turn == CONFIRM_ALWAYS)
|
||||
return 1;
|
||||
if (g_turn == CONFIRM_NEVER)
|
||||
return 0;
|
||||
/* 名字都没有就没什么可描述的, 也没有什么"改动"值得拦 —— 交给工具层
|
||||
* 去回一句"未知工具". strcmp(NULL, ...) 会直接崩. */
|
||||
if (!name) return 1;
|
||||
if (!name)
|
||||
return 1;
|
||||
|
||||
/* 非交互输入: 没有人能回答, 按默认值 (Y)放行.
|
||||
/* 非交互输入: 没有人能回答, 按默认值 (Y)放行.
|
||||
* 这也是"默认 Y"的自然推论 —— 问不到就等于不反对. */
|
||||
if (!isatty(STDIN_FILENO)) return 1;
|
||||
if (!isatty(STDIN_FILENO))
|
||||
return 1;
|
||||
|
||||
/* 提权命令会在参数里明说 elevated, describe() 已经把它打出来了 ——
|
||||
* 这里不需要额外的身份标注: 没有常驻提权状态, 一次命令一次身份. */
|
||||
@@ -133,28 +143,32 @@ int confirm_tool(const char *name, const JsonValue *args)
|
||||
errno = 0;
|
||||
if (!fgets(line, sizeof line, stdin)) {
|
||||
fputc('\n', stderr);
|
||||
/* 这里必须区分两种"读不到":
|
||||
/* 这里必须区分两种"读不到":
|
||||
*
|
||||
* EINTR 用户按了 Ctrl-C. 他是在说"别问了" —— 必须拒绝.
|
||||
* 真 EOF stdin 关了, 没人能回答 —— 才按默认值放行.
|
||||
* EINTR 用户按了 Ctrl-C. 他是在说"别问了" —— 必须拒绝.
|
||||
* 真 EOF stdin 关了, 没人能回答 —— 才按默认值放行.
|
||||
*
|
||||
* 原来两者走同一个分支、都返回 1 (允许), 于是"在确认提示上按
|
||||
* Ctrl-C"变成了"批准这次操作". run_command 侥幸自己查了中断
|
||||
* 标志才没执行, write_file / edit_file 没查, 文件照写.
|
||||
* 标志才没执行, write_file / edit_file 没查, 文件照写.
|
||||
*
|
||||
* 返回 0 之后 g_ligent_interrupt 仍是 1, agent_run 会把这一轮
|
||||
* 收掉 —— 和 Ctrl-C 在别处的语义 (中断当前请求)保持一致. */
|
||||
if (errno == EINTR) return 0;
|
||||
if (errno == EINTR)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
char *ans = str_trim(line);
|
||||
/* 回车 = Y; A/V 大写小写都收, 也收全词 */
|
||||
if (*ans == '\0' || strcmp(ans, "y") == 0 || strcmp(ans, "Y") == 0) return 1;
|
||||
if (strcmp(ans, "n") == 0 || strcmp(ans, "N") == 0) return 0;
|
||||
if (*ans == '\0' || strcmp(ans, "y") == 0 || strcmp(ans, "Y") == 0)
|
||||
return 1;
|
||||
if (strcmp(ans, "n") == 0 || strcmp(ans, "N") == 0)
|
||||
return 0;
|
||||
if (strcmp(ans, "a") == 0 || strcmp(ans, "A") == 0 ||
|
||||
strcasecmp(ans, "always") == 0) {
|
||||
g_turn = CONFIRM_ALWAYS;
|
||||
fprintf(stderr, " %s%s%s\n", c(DIM), t("confirm.always"), c(OFF));
|
||||
fprintf(stderr, " %s%s%s\n", c(DIM), t("confirm.always"),
|
||||
c(OFF));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(ans, "v") == 0 || strcmp(ans, "V") == 0 ||
|
||||
|
||||
Reference in new issue
Block a user