From 16e971af0b691dc14b1e22cbc5ea3a68355a98dd Mon Sep 17 00:00:00 2001 From: Joshua Potter Date: Sat, 25 Nov 2023 10:37:41 -0700 Subject: [PATCH] Allow reading in STRING prompts. --- README.md | 2 -- include/evaluator.h | 2 +- include/validator.h | 2 ++ main.c | 8 ++---- specs/test/spec.json | 6 ++++ src/config.c | 8 +++--- src/evaluator.c | 68 ++++++++++++++++++++++++++++++++++++++------ src/string_buf.c | 7 ++--- src/validator.c | 7 ++--- 9 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 specs/test/spec.json diff --git a/README.md b/README.md index 90bc63e..5eb1926 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # bootstrap TODO: -- [ ] Make free-ing data consistent with null pointers. - [ ] Add documentation throughout (ownership, docstrings, etc.). -- [ ] Organize variables in each function to the top? CLI utility for initializing projects in reproducible ways. diff --git a/include/evaluator.h b/include/evaluator.h index 031d5e0..5560cc1 100644 --- a/include/evaluator.h +++ b/include/evaluator.h @@ -7,7 +7,7 @@ int evaluate_run_sh( const struct Config *const config, - const struct DynArray *const prompts, + const struct DynArray *const fields, struct Error **error ); diff --git a/include/validator.h b/include/validator.h index 05714a6..b976c17 100644 --- a/include/validator.h +++ b/include/validator.h @@ -11,7 +11,9 @@ enum FieldType { struct Field { enum FieldType type; + // OWNERSHIP: Does not take ownership. const char *key; + // OWNERSHIP: Does not take ownership. const char *prompt; }; diff --git a/main.c b/main.c index 5f2cd34..415222a 100644 --- a/main.c +++ b/main.c @@ -46,14 +46,10 @@ static int run(const char *root_dir, const char *target) { } cleanup_prompts: - if (prompts) { - dyn_array_free(prompts); - } + dyn_array_free(prompts); cleanup_parsed: - if (parsed) { - cJSON_Delete(parsed); - } + cJSON_Delete(parsed); cleanup_config: config_free(config); diff --git a/specs/test/spec.json b/specs/test/spec.json new file mode 100644 index 0000000..a0e72c2 --- /dev/null +++ b/specs/test/spec.json @@ -0,0 +1,6 @@ +{ + "display": { + "type": "STRING", + "prompt": "This is a prompt" + } +} diff --git a/src/config.c b/src/config.c index 1d4df64..50d05b1 100644 --- a/src/config.c +++ b/src/config.c @@ -14,6 +14,8 @@ struct Error *config_load( const char *target, struct Config **config ) { + assert(target); + if (cwd == 0) { return ERROR_NEW(ERROR_CONFIG_ENV_CWD_INVALID, "Could not retrieve $CWD."); } @@ -22,11 +24,9 @@ struct Error *config_load( ERROR_CONFIG_ENV_ROOT_DIR_INVALID, "No specified root directory." ); } - assert(target); - // Check if the specified directory exists. const char *segments[] = {root_dir, target}; - char *filepath = + const char *filepath = join_path_segments(sizeof(segments) / sizeof(char *), segments); struct stat sb; @@ -58,7 +58,7 @@ struct Error *config_load( (*config)->target = target; cleanup: - free(filepath); + free((void *)filepath); return error; } diff --git a/src/evaluator.c b/src/evaluator.c index fdcbee8..49ec859 100644 --- a/src/evaluator.c +++ b/src/evaluator.c @@ -1,13 +1,19 @@ #include "evaluator.h" +#include +#include #include #include +#include #include #include "path.h" #include "string_buf.h" +#include "validator.h" static struct Error *find_run_sh(const struct Config *const config) { + assert(config); + struct stat sb; const char *segments[] = {config->root_dir, config->target, "run.sh"}; char *filepath = @@ -35,9 +41,41 @@ static struct Error *find_run_sh(const struct Config *const config) { return 0; } +static const char *prompt_field(struct Field *field) { + assert(field); + + switch (field->type) { + case FT_STRING: + printf("%s> ", field->prompt); + char *input = calloc(1, 256); + if (fgets(input, 256, stdin)) { + size_t len = strlen(input); + if (len > 0 && input[len - 1] == '\n') { + input[len - 1] = '\0'; + } + return input; + } else { + free(input); + return 0; + } + } +} + +static void push_env( + struct StringBuf *env, const char *key, const char *value +) { + assert(env); + for (const char *c = key; *c; ++c) { + string_buf_cappend(env, toupper(*c)); + } + string_buf_sappend(env, "='"); + string_buf_sappend(env, value); + string_buf_sappend(env, "' "); +} + int evaluate_run_sh( const struct Config *const config, - const struct DynArray *const prompts, + const struct DynArray *const fields, struct Error **error ) { *error = find_run_sh(config); @@ -45,15 +83,28 @@ int evaluate_run_sh( return EXIT_FAILURE; } - if (prompts) { - for (int i = 0; i < prompts->size; ++i) { + struct StringBuf *env_buf = string_buf_new(512); + push_env(env_buf, "OUT", config->cwd); + + if (fields) { + for (int i = 0; i < fields->size; ++i) { + struct Field *field = fields->buf[i]; + const char *response = prompt_field(field); + if (!response) { + *error = ERROR_NEW( + ERROR_EVALUATOR_RESPONSE_INVALID, "Could not read in response." + ); + string_buf_free(env_buf); + return EXIT_FAILURE; + } + push_env(env_buf, field->key, response); } - // TODO: Display prompts and collect answers. } const char *segments[] = {config->root_dir, config->target, "run.sh"}; - char *filepath = + const char *filepath = join_path_segments(sizeof(segments) / sizeof(char *), segments); + const char *env = string_buf_convert(env_buf); struct StringBuf *command_buf = string_buf_new(1024); string_buf_sappend(command_buf, "cd "); @@ -61,13 +112,12 @@ int evaluate_run_sh( string_buf_cappend(command_buf, '/'); string_buf_sappend(command_buf, config->target); string_buf_sappend(command_buf, " && "); - string_buf_sappend(command_buf, "OUT="); - string_buf_sappend(command_buf, config->cwd); - string_buf_cappend(command_buf, ' '); + string_buf_sappend(command_buf, env); string_buf_sappend(command_buf, filepath); const char *command = string_buf_convert(command_buf); - free(filepath); + free((void *)env); + free((void *)filepath); int exit_code = system(command); free((void *)command); diff --git a/src/string_buf.c b/src/string_buf.c index cc2c7ce..064c8b0 100644 --- a/src/string_buf.c +++ b/src/string_buf.c @@ -15,7 +15,6 @@ struct StringBuf *string_buf_new(size_t capacity) { size_t string_buf_size(struct StringBuf *sb) { assert(sb); - return sb->size; } @@ -55,15 +54,15 @@ void string_buf_sappend(struct StringBuf *sb, const char s[static 1]) { const char *string_buf_convert(struct StringBuf *sb) { assert(sb); - const char *buf = sb->buf; free(sb); return buf; } void string_buf_free(struct StringBuf *sb) { - assert(sb); - + if (!sb) { + return; + } free((void *)sb->buf); free(sb); sb = 0; diff --git a/src/validator.c b/src/validator.c index 9093339..f899500 100644 --- a/src/validator.c +++ b/src/validator.c @@ -14,6 +14,7 @@ static struct Error *read_field(const cJSON *const field, struct Field **out) { struct Error *error = 0; *out = malloc(sizeof(struct Field)); + (*out)->key = field->string; const cJSON *type = cJSON_GetObjectItemCaseSensitive(field, "type"); if (!cJSON_IsString(type)) { @@ -94,9 +95,7 @@ struct Error *validate_spec_json( return 0; cleanup: - if (*fields) { - dyn_array_free(*fields); - *fields = 0; - } + dyn_array_free(*fields); + *fields = 0; return error; }