mirror of
https://github.com/Leonmmcoset/cleonos.git
synced 2026-04-21 10:40:00 +00:00
管道等一系列shell语法
This commit is contained in:
@@ -349,8 +349,8 @@ set(RAMDISK_ROOT_APPS)
|
||||
|
||||
set(USER_SHELL_COMMAND_APPS
|
||||
help ls cat pwd cd exec pid spawn wait sleep yield
|
||||
shutdown restart exit clear ansi memstat fsstat taskstat userstat
|
||||
shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm
|
||||
shutdown restart exit clear ansi ansitest memstat fsstat taskstat userstat
|
||||
shstat stats tty dmesg kbdstat mkdir touch write append cp mv grep rm
|
||||
)
|
||||
|
||||
foreach(SRC IN LISTS USER_APP_MAIN_SOURCES)
|
||||
|
||||
5
cleonos/c/apps/ansitest_main.c
Normal file
5
cleonos/c/apps/ansitest_main.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "shell/shell_internal.h"
|
||||
|
||||
int cleonos_app_main(void) {
|
||||
return ush_command_program_main("ansitest");
|
||||
}
|
||||
5
cleonos/c/apps/grep_main.c
Normal file
5
cleonos/c/apps/grep_main.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "shell/shell_internal.h"
|
||||
|
||||
int cleonos_app_main(void) {
|
||||
return ush_command_program_main("grep");
|
||||
}
|
||||
@@ -3,6 +3,22 @@
|
||||
#define USH_DMESG_DEFAULT 64ULL
|
||||
#define USH_DMESG_LINE_MAX 256ULL
|
||||
#define USH_COPY_MAX 65536U
|
||||
#define USH_PIPELINE_MAX_STAGES 8ULL
|
||||
#define USH_PIPE_CAPTURE_MAX USH_COPY_MAX
|
||||
|
||||
typedef struct ush_pipeline_stage {
|
||||
char text[USH_LINE_MAX];
|
||||
char cmd[USH_CMD_MAX];
|
||||
char arg[USH_ARG_MAX];
|
||||
char redirect_path[USH_PATH_MAX];
|
||||
int redirect_mode; /* 0: none, 1: >, 2: >> */
|
||||
} ush_pipeline_stage;
|
||||
|
||||
static const char *ush_pipeline_stdin_text = (const char *)0;
|
||||
static u64 ush_pipeline_stdin_len = 0ULL;
|
||||
static char ush_pipeline_capture_a[USH_PIPE_CAPTURE_MAX + 1U];
|
||||
static char ush_pipeline_capture_b[USH_PIPE_CAPTURE_MAX + 1U];
|
||||
|
||||
|
||||
static int ush_path_is_under_temp(const char *path) {
|
||||
if (path == (const char *)0) {
|
||||
@@ -111,7 +127,8 @@ static int ush_cmd_help(void) {
|
||||
ush_writeln("commands:");
|
||||
ush_writeln(" help");
|
||||
ush_writeln(" ls [-l] [-R] [path]");
|
||||
ush_writeln(" cat <file>");
|
||||
ush_writeln(" cat [file] (reads pipeline input when file omitted)");
|
||||
ush_writeln(" grep [-n] <pattern> [file]");
|
||||
ush_writeln(" pwd");
|
||||
ush_writeln(" cd [dir]");
|
||||
ush_writeln(" exec|run <path|name>");
|
||||
@@ -123,8 +140,8 @@ static int ush_cmd_help(void) {
|
||||
ush_writeln(" kbdstat");
|
||||
ush_writeln(" mkdir <dir> (/temp only)");
|
||||
ush_writeln(" touch <file> (/temp only)");
|
||||
ush_writeln(" write <file> <text> (/temp only)");
|
||||
ush_writeln(" append <file> <text> (/temp only)");
|
||||
ush_writeln(" write <file> <text> (/temp only, or from pipeline)");
|
||||
ush_writeln(" append <file> <text> (/temp only, or from pipeline)");
|
||||
ush_writeln(" cp <src> <dst> (dst /temp only)");
|
||||
ush_writeln(" mv <src> <dst> (/temp only)");
|
||||
ush_writeln(" rm <path> (/temp only)");
|
||||
@@ -136,6 +153,8 @@ static int ush_cmd_help(void) {
|
||||
ush_writeln(" shutdown / restart");
|
||||
ush_writeln(" exit [code]");
|
||||
ush_writeln(" rusttest / panic / elfloader (kernel shell only)");
|
||||
ush_writeln("pipeline/redirection: cmd1 | cmd2 | cmd3 > /temp/out.txt");
|
||||
ush_writeln("redirection append: cmd >> /temp/out.txt");
|
||||
ush_writeln("edit keys: Left/Right, Home/End, Up/Down history");
|
||||
return 1;
|
||||
}
|
||||
@@ -439,6 +458,11 @@ static int ush_cmd_cat(const ush_state *sh, const char *arg) {
|
||||
u64 got;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_len > 0ULL) {
|
||||
ush_write(ush_pipeline_stdin_text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ush_writeln("cat: file path required");
|
||||
return 0;
|
||||
}
|
||||
@@ -486,6 +510,207 @@ static int ush_cmd_cat(const ush_state *sh, const char *arg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ush_grep_write_u64_dec(u64 value) {
|
||||
char tmp[32];
|
||||
u64 len = 0ULL;
|
||||
|
||||
if (value == 0ULL) {
|
||||
ush_write_char('0');
|
||||
return;
|
||||
}
|
||||
|
||||
while (value > 0ULL && len < (u64)sizeof(tmp)) {
|
||||
tmp[len++] = (char)('0' + (value % 10ULL));
|
||||
value /= 10ULL;
|
||||
}
|
||||
|
||||
while (len > 0ULL) {
|
||||
len--;
|
||||
ush_write_char(tmp[len]);
|
||||
}
|
||||
}
|
||||
|
||||
static int ush_grep_line_has_pattern(const char *line, u64 line_len, const char *pattern, u64 pattern_len) {
|
||||
u64 i;
|
||||
|
||||
if (line == (const char *)0 || pattern == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pattern_len == 0ULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pattern_len > line_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i + pattern_len <= line_len; i++) {
|
||||
u64 j = 0ULL;
|
||||
|
||||
while (j < pattern_len && line[i + j] == pattern[j]) {
|
||||
j++;
|
||||
}
|
||||
|
||||
if (j == pattern_len) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 ush_grep_emit_matches(const char *input, u64 input_len, const char *pattern, int with_line_number) {
|
||||
u64 matches = 0ULL;
|
||||
u64 line_no = 1ULL;
|
||||
u64 start = 0ULL;
|
||||
u64 i;
|
||||
u64 pattern_len;
|
||||
|
||||
if (input == (const char *)0 || pattern == (const char *)0) {
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
pattern_len = ush_strlen(pattern);
|
||||
|
||||
for (i = 0ULL; i <= input_len; i++) {
|
||||
if (i == input_len || input[i] == '\n') {
|
||||
u64 line_len = i - start;
|
||||
|
||||
if (ush_grep_line_has_pattern(&input[start], line_len, pattern, pattern_len) != 0) {
|
||||
u64 j;
|
||||
|
||||
matches++;
|
||||
|
||||
if (with_line_number != 0) {
|
||||
ush_grep_write_u64_dec(line_no);
|
||||
ush_write(":");
|
||||
}
|
||||
|
||||
for (j = 0ULL; j < line_len; j++) {
|
||||
ush_write_char(input[start + j]);
|
||||
}
|
||||
|
||||
ush_write_char('\n');
|
||||
}
|
||||
|
||||
start = i + 1ULL;
|
||||
line_no++;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
static int ush_cmd_grep(const ush_state *sh, const char *arg) {
|
||||
char first[USH_PATH_MAX];
|
||||
char second[USH_PATH_MAX];
|
||||
char third[USH_PATH_MAX];
|
||||
char path[USH_PATH_MAX];
|
||||
const char *rest = "";
|
||||
const char *rest2 = "";
|
||||
const char *pattern = (const char *)0;
|
||||
const char *file_arg = (const char *)0;
|
||||
const char *input = (const char *)0;
|
||||
u64 input_len = 0ULL;
|
||||
u64 size;
|
||||
u64 got;
|
||||
int with_line_number = 0;
|
||||
static char file_buf[USH_COPY_MAX + 1U];
|
||||
|
||||
if (sh == (const ush_state *)0 || arg == (const char *)0 || arg[0] == '\0') {
|
||||
ush_writeln("grep: usage grep [-n] <pattern> [file]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) {
|
||||
ush_writeln("grep: usage grep [-n] <pattern> [file]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_streq(first, "-n") != 0) {
|
||||
with_line_number = 1;
|
||||
|
||||
if (ush_split_first_and_rest(rest, second, (u64)sizeof(second), &rest2) == 0) {
|
||||
ush_writeln("grep: usage grep [-n] <pattern> [file]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pattern = second;
|
||||
rest = rest2;
|
||||
} else {
|
||||
pattern = first;
|
||||
}
|
||||
|
||||
if (rest != (const char *)0 && rest[0] != '\0') {
|
||||
if (ush_split_first_and_rest(rest, third, (u64)sizeof(third), &rest2) == 0) {
|
||||
ush_writeln("grep: usage grep [-n] <pattern> [file]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
file_arg = third;
|
||||
|
||||
if (rest2 != (const char *)0 && rest2[0] != '\0') {
|
||||
ush_writeln("grep: usage grep [-n] <pattern> [file]");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern == (const char *)0 || pattern[0] == '\0') {
|
||||
ush_writeln("grep: pattern required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file_arg != (const char *)0) {
|
||||
if (ush_resolve_path(sh, file_arg, path, (u64)sizeof(path)) == 0) {
|
||||
ush_writeln("grep: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_stat_type(path) != 1ULL) {
|
||||
ush_writeln("grep: file not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = cleonos_sys_fs_stat_size(path);
|
||||
|
||||
if (size == (u64)-1) {
|
||||
ush_writeln("grep: failed to stat file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size > (u64)USH_COPY_MAX) {
|
||||
ush_writeln("grep: file too large for user buffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 0ULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
got = cleonos_sys_fs_read(path, file_buf, size);
|
||||
|
||||
if (got == 0ULL || got != size) {
|
||||
ush_writeln("grep: read failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
file_buf[got] = '\0';
|
||||
input = file_buf;
|
||||
input_len = got;
|
||||
} else {
|
||||
if (ush_pipeline_stdin_text == (const char *)0) {
|
||||
ush_writeln("grep: file path required (or pipeline input)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
input = ush_pipeline_stdin_text;
|
||||
input_len = ush_pipeline_stdin_len;
|
||||
}
|
||||
|
||||
(void)ush_grep_emit_matches(input, input_len, pattern, with_line_number);
|
||||
return 1;
|
||||
}
|
||||
static int ush_cmd_pwd(const ush_state *sh) {
|
||||
ush_writeln(sh->cwd);
|
||||
return 1;
|
||||
@@ -989,7 +1214,7 @@ static int ush_cmd_touch(const ush_state *sh, const char *arg) {
|
||||
static int ush_cmd_write(const ush_state *sh, const char *arg) {
|
||||
char path_arg[USH_PATH_MAX];
|
||||
char abs_path[USH_PATH_MAX];
|
||||
const char *payload;
|
||||
const char *payload = (const char *)0;
|
||||
u64 payload_len;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
@@ -1012,7 +1237,16 @@ static int ush_cmd_write(const ush_state *sh, const char *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (payload == (const char *)0 || payload[0] == '\0') {
|
||||
if (ush_pipeline_stdin_text == (const char *)0) {
|
||||
ush_writeln("write: usage write <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
payload = ush_pipeline_stdin_text;
|
||||
payload_len = ush_pipeline_stdin_len;
|
||||
} else {
|
||||
payload_len = ush_strlen(payload);
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_write(abs_path, payload, payload_len) == 0ULL) {
|
||||
ush_writeln("write: failed");
|
||||
@@ -1025,7 +1259,7 @@ static int ush_cmd_write(const ush_state *sh, const char *arg) {
|
||||
static int ush_cmd_append(const ush_state *sh, const char *arg) {
|
||||
char path_arg[USH_PATH_MAX];
|
||||
char abs_path[USH_PATH_MAX];
|
||||
const char *payload;
|
||||
const char *payload = (const char *)0;
|
||||
u64 payload_len;
|
||||
|
||||
if (arg == (const char *)0 || arg[0] == '\0') {
|
||||
@@ -1048,7 +1282,16 @@ static int ush_cmd_append(const ush_state *sh, const char *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (payload == (const char *)0 || payload[0] == '\0') {
|
||||
if (ush_pipeline_stdin_text == (const char *)0) {
|
||||
ush_writeln("append: usage append <file> <text>");
|
||||
return 0;
|
||||
}
|
||||
payload = ush_pipeline_stdin_text;
|
||||
payload_len = ush_pipeline_stdin_len;
|
||||
} else {
|
||||
payload_len = ush_strlen(payload);
|
||||
}
|
||||
|
||||
if (cleonos_sys_fs_append(abs_path, payload, payload_len) == 0ULL) {
|
||||
ush_writeln("append: failed");
|
||||
@@ -1214,35 +1457,35 @@ static int ush_cmd_not_supported(const char *name, const char *why) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ush_execute_line(ush_state *sh, const char *line) {
|
||||
char line_buf[USH_LINE_MAX];
|
||||
char cmd[USH_CMD_MAX];
|
||||
char arg[USH_ARG_MAX];
|
||||
u64 i = 0ULL;
|
||||
static int ush_execute_single_command(ush_state *sh,
|
||||
const char *cmd,
|
||||
const char *arg,
|
||||
int allow_external,
|
||||
int *out_known,
|
||||
int *out_success) {
|
||||
int known = 1;
|
||||
int success = 0;
|
||||
|
||||
if (sh == (ush_state *)0 || line == (const char *)0) {
|
||||
return;
|
||||
if (out_known != (int *)0) {
|
||||
*out_known = 1;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
|
||||
line_buf[i] = line[i];
|
||||
i++;
|
||||
if (out_success != (int *)0) {
|
||||
*out_success = 0;
|
||||
}
|
||||
|
||||
line_buf[i] = '\0';
|
||||
ush_trim_line(line_buf);
|
||||
|
||||
if (line_buf[0] == '\0' || line_buf[0] == '#') {
|
||||
return;
|
||||
if (sh == (ush_state *)0 || cmd == (const char *)0 || cmd[0] == '\0') {
|
||||
if (out_known != (int *)0) {
|
||||
*out_known = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ush_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
|
||||
ush_trim_line(arg);
|
||||
|
||||
if (ush_try_exec_external(sh, cmd, arg, &success) != 0) {
|
||||
goto finalize_stats;
|
||||
if (allow_external != 0 && ush_try_exec_external(sh, cmd, arg, &success) != 0) {
|
||||
if (out_success != (int *)0) {
|
||||
*out_success = success;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ush_streq(cmd, "help") != 0) {
|
||||
@@ -1251,6 +1494,8 @@ void ush_execute_line(ush_state *sh, const char *line) {
|
||||
success = ush_cmd_ls(sh, arg);
|
||||
} else if (ush_streq(cmd, "cat") != 0) {
|
||||
success = ush_cmd_cat(sh, arg);
|
||||
} else if (ush_streq(cmd, "grep") != 0) {
|
||||
success = ush_cmd_grep(sh, arg);
|
||||
} else if (ush_streq(cmd, "pwd") != 0) {
|
||||
success = ush_cmd_pwd(sh);
|
||||
} else if (ush_streq(cmd, "cd") != 0) {
|
||||
@@ -1319,7 +1564,320 @@ void ush_execute_line(ush_state *sh, const char *line) {
|
||||
ush_writeln("unknown command; type 'help'");
|
||||
}
|
||||
|
||||
finalize_stats:
|
||||
if (out_known != (int *)0) {
|
||||
*out_known = known;
|
||||
}
|
||||
|
||||
if (out_success != (int *)0) {
|
||||
*out_success = success;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ush_pipeline_set_stdin(const char *text, u64 len) {
|
||||
ush_pipeline_stdin_text = text;
|
||||
ush_pipeline_stdin_len = len;
|
||||
}
|
||||
|
||||
static int ush_pipeline_has_meta(const char *line) {
|
||||
u64 i = 0ULL;
|
||||
|
||||
if (line == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (line[i] != '\0') {
|
||||
if (line[i] == '|' || line[i] == '>') {
|
||||
return 1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ush_pipeline_parse_stage(ush_pipeline_stage *stage, const char *segment_text) {
|
||||
char work[USH_LINE_MAX];
|
||||
char path_part[USH_PATH_MAX];
|
||||
const char *path_rest = "";
|
||||
u64 i;
|
||||
i64 op_pos = -1;
|
||||
int op_mode = 0;
|
||||
|
||||
if (stage == (ush_pipeline_stage *)0 || segment_text == (const char *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ush_copy(work, (u64)sizeof(work), segment_text);
|
||||
ush_trim_line(work);
|
||||
|
||||
if (work[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0ULL; work[i] != '\0'; i++) {
|
||||
if (work[i] == '>') {
|
||||
if (op_pos >= 0) {
|
||||
ush_writeln("pipe: multiple redirections in one stage are not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
op_pos = (i64)i;
|
||||
|
||||
if (work[i + 1ULL] == '>') {
|
||||
op_mode = 2;
|
||||
i++;
|
||||
} else {
|
||||
op_mode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage->redirect_mode = 0;
|
||||
stage->redirect_path[0] = '\0';
|
||||
|
||||
if (op_pos >= 0) {
|
||||
char *path_src;
|
||||
|
||||
work[(u64)op_pos] = '\0';
|
||||
path_src = &work[(u64)op_pos + ((op_mode == 2) ? 2ULL : 1ULL)];
|
||||
|
||||
ush_trim_line(work);
|
||||
ush_trim_line(path_src);
|
||||
|
||||
if (path_src[0] == '\0') {
|
||||
ush_writeln("pipe: redirection path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_split_first_and_rest(path_src, path_part, (u64)sizeof(path_part), &path_rest) == 0) {
|
||||
ush_writeln("pipe: redirection path required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path_rest != (const char *)0 && path_rest[0] != '\0') {
|
||||
ush_writeln("pipe: redirection path cannot contain spaces");
|
||||
return 0;
|
||||
}
|
||||
|
||||
stage->redirect_mode = op_mode;
|
||||
ush_copy(stage->redirect_path, (u64)sizeof(stage->redirect_path), path_part);
|
||||
}
|
||||
|
||||
ush_copy(stage->text, (u64)sizeof(stage->text), work);
|
||||
ush_parse_line(work, stage->cmd, (u64)sizeof(stage->cmd), stage->arg, (u64)sizeof(stage->arg));
|
||||
ush_trim_line(stage->arg);
|
||||
|
||||
if (stage->cmd[0] == '\0') {
|
||||
ush_writeln("pipe: empty command stage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_pipeline_parse(const char *line,
|
||||
ush_pipeline_stage *stages,
|
||||
u64 max_stages,
|
||||
u64 *out_stage_count) {
|
||||
char segment[USH_LINE_MAX];
|
||||
u64 i = 0ULL;
|
||||
u64 seg_pos = 0ULL;
|
||||
u64 stage_count = 0ULL;
|
||||
|
||||
if (line == (const char *)0 || stages == (ush_pipeline_stage *)0 || max_stages == 0ULL || out_stage_count == (u64 *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_stage_count = 0ULL;
|
||||
|
||||
for (;;) {
|
||||
char ch = line[i];
|
||||
|
||||
if (ch == '|' || ch == '\0') {
|
||||
segment[seg_pos] = '\0';
|
||||
|
||||
if (stage_count >= max_stages) {
|
||||
ush_writeln("pipe: too many stages");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ush_pipeline_parse_stage(&stages[stage_count], segment) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stage_count++;
|
||||
seg_pos = 0ULL;
|
||||
|
||||
if (ch == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seg_pos + 1ULL >= (u64)sizeof(segment)) {
|
||||
ush_writeln("pipe: stage text too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
segment[seg_pos++] = ch;
|
||||
i++;
|
||||
}
|
||||
|
||||
*out_stage_count = stage_count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_pipeline_write_redirect(const ush_state *sh, const ush_pipeline_stage *stage, const char *data, u64 len) {
|
||||
char abs_path[USH_PATH_MAX];
|
||||
u64 ok;
|
||||
|
||||
if (sh == (const ush_state *)0 || stage == (const ush_pipeline_stage *)0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stage->redirect_mode == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ush_resolve_path(sh, stage->redirect_path, abs_path, (u64)sizeof(abs_path)) == 0) {
|
||||
ush_writeln("redirect: invalid path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stage->redirect_mode == 1) {
|
||||
ok = cleonos_sys_fs_write(abs_path, data, len);
|
||||
} else {
|
||||
ok = cleonos_sys_fs_append(abs_path, data, len);
|
||||
}
|
||||
|
||||
if (ok == 0ULL) {
|
||||
ush_writeln("redirect: write failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ush_execute_pipeline(ush_state *sh,
|
||||
const char *line,
|
||||
int *out_known,
|
||||
int *out_success) {
|
||||
ush_pipeline_stage stages[USH_PIPELINE_MAX_STAGES];
|
||||
u64 stage_count = 0ULL;
|
||||
u64 i;
|
||||
const char *pipe_in = (const char *)0;
|
||||
u64 pipe_in_len = 0ULL;
|
||||
char *capture_out = ush_pipeline_capture_a;
|
||||
u64 capture_len = 0ULL;
|
||||
int known = 1;
|
||||
int success = 1;
|
||||
|
||||
if (out_known != (int *)0) {
|
||||
*out_known = 1;
|
||||
}
|
||||
|
||||
if (out_success != (int *)0) {
|
||||
*out_success = 0;
|
||||
}
|
||||
|
||||
if (ush_pipeline_parse(line, stages, USH_PIPELINE_MAX_STAGES, &stage_count) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < stage_count; i++) {
|
||||
int stage_known = 1;
|
||||
int stage_success = 0;
|
||||
int mirror_to_tty = ((i + 1ULL) == stage_count && stages[i].redirect_mode == 0) ? 1 : 0;
|
||||
|
||||
if (i + 1ULL < stage_count && stages[i].redirect_mode != 0) {
|
||||
ush_writeln("pipe: redirection is only supported on final stage");
|
||||
known = 1;
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ush_pipeline_set_stdin(pipe_in, pipe_in_len);
|
||||
ush_output_capture_begin(capture_out, (u64)USH_PIPE_CAPTURE_MAX + 1ULL, mirror_to_tty);
|
||||
(void)ush_execute_single_command(sh, stages[i].cmd, stages[i].arg, 0, &stage_known, &stage_success);
|
||||
capture_len = ush_output_capture_end();
|
||||
|
||||
if (ush_output_capture_truncated() != 0) {
|
||||
ush_writeln("[pipe] captured output truncated");
|
||||
}
|
||||
|
||||
if (stage_known == 0) {
|
||||
known = 0;
|
||||
}
|
||||
|
||||
if (stage_success == 0) {
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stages[i].redirect_mode != 0) {
|
||||
if (ush_pipeline_write_redirect(sh, &stages[i], capture_out, capture_len) == 0) {
|
||||
success = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pipe_in = capture_out;
|
||||
pipe_in_len = capture_len;
|
||||
capture_out = (capture_out == ush_pipeline_capture_a) ? ush_pipeline_capture_b : ush_pipeline_capture_a;
|
||||
}
|
||||
|
||||
ush_pipeline_set_stdin((const char *)0, 0ULL);
|
||||
|
||||
if (out_known != (int *)0) {
|
||||
*out_known = known;
|
||||
}
|
||||
|
||||
if (out_success != (int *)0) {
|
||||
*out_success = success;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ush_execute_line(ush_state *sh, const char *line) {
|
||||
char line_buf[USH_LINE_MAX];
|
||||
char cmd[USH_CMD_MAX];
|
||||
char arg[USH_ARG_MAX];
|
||||
u64 i = 0ULL;
|
||||
int known = 1;
|
||||
int success = 0;
|
||||
|
||||
if (sh == (ush_state *)0 || line == (const char *)0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (line[i] != '\0' && i + 1ULL < (u64)sizeof(line_buf)) {
|
||||
line_buf[i] = line[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
line_buf[i] = '\0';
|
||||
ush_trim_line(line_buf);
|
||||
|
||||
if (line_buf[0] == '\0' || line_buf[0] == '#') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ush_pipeline_has_meta(line_buf) != 0) {
|
||||
if (ush_execute_pipeline(sh, line_buf, &known, &success) == 0) {
|
||||
known = 1;
|
||||
success = 0;
|
||||
}
|
||||
} else {
|
||||
ush_parse_line(line_buf, cmd, (u64)sizeof(cmd), arg, (u64)sizeof(arg));
|
||||
ush_trim_line(arg);
|
||||
(void)ush_execute_single_command(sh, cmd, arg, 1, &known, &success);
|
||||
}
|
||||
|
||||
sh->cmd_total++;
|
||||
|
||||
if (success != 0) {
|
||||
@@ -1332,6 +1890,3 @@ finalize_stats:
|
||||
sh->cmd_unknown++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg
|
||||
void ush_write(const char *text);
|
||||
void ush_write_char(char ch);
|
||||
void ush_writeln(const char *text);
|
||||
void ush_output_capture_begin(char *buffer, u64 buffer_size, int mirror_to_tty);
|
||||
u64 ush_output_capture_end(void);
|
||||
int ush_output_capture_truncated(void);
|
||||
void ush_prompt(const ush_state *sh);
|
||||
void ush_write_hex_u64(u64 value);
|
||||
void ush_print_kv_hex(const char *label, u64 value);
|
||||
|
||||
@@ -219,6 +219,82 @@ void ush_parse_line(const char *line, char *out_cmd, u64 cmd_size, char *out_arg
|
||||
out_arg[arg_pos] = '\0';
|
||||
}
|
||||
|
||||
static char *ush_out_capture_buffer = (char *)0;
|
||||
static u64 ush_out_capture_capacity = 0ULL;
|
||||
static u64 ush_out_capture_length = 0ULL;
|
||||
static int ush_out_capture_active = 0;
|
||||
static int ush_out_capture_mirror_tty = 1;
|
||||
static int ush_out_capture_truncated = 0;
|
||||
|
||||
static void ush_output_capture_append(const char *text, u64 len) {
|
||||
u64 writable;
|
||||
u64 i;
|
||||
|
||||
if (ush_out_capture_active == 0 || text == (const char *)0 || len == 0ULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ush_out_capture_buffer == (char *)0 || ush_out_capture_capacity == 0ULL) {
|
||||
ush_out_capture_truncated = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ush_out_capture_length + 1ULL >= ush_out_capture_capacity) {
|
||||
ush_out_capture_truncated = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
writable = (ush_out_capture_capacity - 1ULL) - ush_out_capture_length;
|
||||
|
||||
if (len > writable) {
|
||||
len = writable;
|
||||
ush_out_capture_truncated = 1;
|
||||
}
|
||||
|
||||
for (i = 0ULL; i < len; i++) {
|
||||
ush_out_capture_buffer[ush_out_capture_length + i] = text[i];
|
||||
}
|
||||
|
||||
ush_out_capture_length += len;
|
||||
ush_out_capture_buffer[ush_out_capture_length] = '\0';
|
||||
}
|
||||
|
||||
void ush_output_capture_begin(char *buffer, u64 buffer_size, int mirror_to_tty) {
|
||||
ush_out_capture_buffer = buffer;
|
||||
ush_out_capture_capacity = buffer_size;
|
||||
ush_out_capture_length = 0ULL;
|
||||
ush_out_capture_active = 1;
|
||||
ush_out_capture_mirror_tty = (mirror_to_tty != 0) ? 1 : 0;
|
||||
ush_out_capture_truncated = 0;
|
||||
|
||||
if (ush_out_capture_buffer != (char *)0 && ush_out_capture_capacity > 0ULL) {
|
||||
ush_out_capture_buffer[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
u64 ush_output_capture_end(void) {
|
||||
u64 captured = ush_out_capture_length;
|
||||
|
||||
if (ush_out_capture_buffer != (char *)0 && ush_out_capture_capacity > 0ULL) {
|
||||
if (ush_out_capture_length >= ush_out_capture_capacity) {
|
||||
ush_out_capture_length = ush_out_capture_capacity - 1ULL;
|
||||
}
|
||||
ush_out_capture_buffer[ush_out_capture_length] = '\0';
|
||||
}
|
||||
|
||||
ush_out_capture_buffer = (char *)0;
|
||||
ush_out_capture_capacity = 0ULL;
|
||||
ush_out_capture_length = 0ULL;
|
||||
ush_out_capture_active = 0;
|
||||
ush_out_capture_mirror_tty = 1;
|
||||
|
||||
return captured;
|
||||
}
|
||||
|
||||
int ush_output_capture_truncated(void) {
|
||||
return ush_out_capture_truncated;
|
||||
}
|
||||
|
||||
void ush_write(const char *text) {
|
||||
u64 len;
|
||||
|
||||
@@ -232,10 +308,26 @@ void ush_write(const char *text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ush_out_capture_active != 0) {
|
||||
ush_output_capture_append(text, len);
|
||||
|
||||
if (ush_out_capture_mirror_tty == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(void)cleonos_sys_tty_write(text, len);
|
||||
}
|
||||
|
||||
void ush_write_char(char ch) {
|
||||
if (ush_out_capture_active != 0) {
|
||||
ush_output_capture_append(&ch, 1ULL);
|
||||
|
||||
if (ush_out_capture_mirror_tty == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(void)cleonos_sys_tty_write_char(ch);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user