diff --git a/src/common/shell/commander/commander.c b/src/common/shell/commander/commander.c new file mode 100644 index 0000000..f67ce37 --- /dev/null +++ b/src/common/shell/commander/commander.c @@ -0,0 +1,127 @@ +#include "commander.h" +#include "aliases.h" +#include "shell_utils.h" +#include +#include +#include +#include +#include + +#define CMD_BUF_LEN 8192 +#define OUT_BUF_LEN 4096 + +internal inline CMDError build_command_from_args(char *cmd, u64 buf_len, + va_list args); +internal inline CMDResult execute_command(const char *cmd, + CMDOutHandling out_handling, + char *out_buf, u64 buf_size); +internal inline CMDError get_command_output(FILE *fp, + CMDOutHandling out_handling, + char *out_buf, u64 buf_size); +internal inline CMDError get_output_status(FILE *fp, i32 *status_out); + +// clang-format off +CMDResult run_command(CMDOutHandling out_handling, char *out_buf, u64 buf_size, ...) { // clang-format on + va_list args; + va_start(args, buf_size); + + char cmd[CMD_BUF_LEN] = {0}; + CMDError err = build_command_from_args(cmd, CMD_BUF_LEN, args); + if (err > SHELL_ERR_NO_ERROR) { + return CMD_NO_EXIT(err); + } + + va_end(args); + + return execute_command(cmd, out_handling, out_buf, buf_size); +} + +internal inline CMDError build_command_from_args(char *cmd, u64 buf_len, + va_list args) { + u64 size = 0; + u64 arg_len = 0; + + const char *arg; + while ((arg = va_arg(args, const char *))) { + arg_len = strlen(arg); + if (arg_len >= buf_len - size) { + return SHELL_ERR_CMD_BUF_FULL; + } + + strcat(cmd, arg); + cmd[size + arg_len] = ' '; + + size += arg_len + 1; + } + + return SHELL_ERR_NO_ERROR; +} + +internal inline CMDResult execute_command(const char *cmd, + CMDOutHandling out_handling, + char *out_buf, u64 buf_size) { + FILE *fp = wapp_shell_utils_popen(cmd, "r"); + if (!fp) { + return CMD_NO_EXIT(SHELL_ERR_PROC_START_FAIL); + } + + CMDError err = get_command_output(fp, out_handling, out_buf, buf_size); + if (err > SHELL_ERR_NO_ERROR) { + return CMD_NO_EXIT(err); + } + + i32 st = EXIT_SUCCESS; + err = get_output_status(fp, &st); + if (err > SHELL_ERR_NO_ERROR) { + return CMD_NO_EXIT(err); + } + + return (CMDResult){ + .exited = true, + .exit_code = st, + .error = SHELL_ERR_NO_ERROR, + }; +} + +internal inline CMDError get_command_output(FILE *fp, + CMDOutHandling out_handling, + char *out_buf, u64 buf_size) { + char out[OUT_BUF_LEN] = {0}; + u64 max_out_length = OUT_BUF_LEN - 1; + + u64 buf_filled = 0; + while (fgets(out, max_out_length, fp)) { + if (out_handling == SHELL_OUTPUT_CAPTURE && out_buf != NULL) { + buf_filled += strlen(out); + if (buf_filled >= buf_size) { + return SHELL_ERR_OUT_BUF_FULL; + } + + strcat(out_buf, out); + } else { + printf("%s", out); + } + } + + return SHELL_ERR_NO_ERROR; +} + +internal inline CMDError get_output_status(FILE *fp, i32 *status_out) { +#ifdef WAPP_PLATFORM_WINDOWS + if (!feof(fp)) { + return SHELL_ERR_PROC_EXIT_FAIL; + } +#endif /* ifdef WAPP_PLATFORM_WINDOWS */ + + *status_out = wapp_shell_utils_pclose(fp); + +#ifdef WAPP_PLATFORM_POSIX + if (!WIFEXITED(*status_out)) { + return SHELL_ERR_PROC_EXIT_FAIL; + } + + *status_out = WEXITSTATUS(*status_out); +#endif /* ifdef WAPP_PLATFORM_WINDOWS */ + + return SHELL_ERR_NO_ERROR; +} diff --git a/src/common/shell/commander/commander.h b/src/common/shell/commander/commander.h new file mode 100644 index 0000000..8c9051a --- /dev/null +++ b/src/common/shell/commander/commander.h @@ -0,0 +1,45 @@ +#ifndef COMMANDER_H +#define COMMANDER_H + +#include "aliases.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define CMD_NO_EXIT(ERR) \ + ((CMDResult){.exited = false, .exit_code = EXIT_FAILURE}) +#define wapp_shell_commander_execute(HANDLE_OUTPUT, OUT_BUF, BUF_SIZE, ...) \ + run_command(HANDLE_OUTPUT, OUT_BUF, BUF_SIZE, __VA_ARGS__, NULL) + +typedef enum { + SHELL_OUTPUT_PRINT, + SHELL_OUTPUT_CAPTURE, +} CMDOutHandling; + +typedef enum { + SHELL_ERR_NO_ERROR, + SHELL_ERR_CMD_BUF_FULL, + SHELL_ERR_PROC_START_FAIL, + SHELL_ERR_OUT_BUF_FULL, + SHELL_ERR_PROC_EXIT_FAIL, +} CMDError; + +typedef struct commander_result CMDResult; +struct commander_result { + bool exited; + int exit_code; + CMDError error; +}; + +// clang-format off +CMDResult run_command(CMDOutHandling out_handling, char *out_buf, u64 buf_size, ...); +// clang-format on + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !COMMANDER_H