Allow reading in STRING prompts.

pull/9/head
Joshua Potter 2023-11-25 10:37:41 -07:00
parent 537226c651
commit 16e971af0b
9 changed files with 80 additions and 30 deletions

View File

@ -1,9 +1,7 @@
# bootstrap # bootstrap
TODO: TODO:
- [ ] Make free-ing data consistent with null pointers.
- [ ] Add documentation throughout (ownership, docstrings, etc.). - [ ] Add documentation throughout (ownership, docstrings, etc.).
- [ ] Organize variables in each function to the top?
CLI utility for initializing projects in reproducible ways. CLI utility for initializing projects in reproducible ways.

View File

@ -7,7 +7,7 @@
int evaluate_run_sh( int evaluate_run_sh(
const struct Config *const config, const struct Config *const config,
const struct DynArray *const prompts, const struct DynArray *const fields,
struct Error **error struct Error **error
); );

View File

@ -11,7 +11,9 @@ enum FieldType {
struct Field { struct Field {
enum FieldType type; enum FieldType type;
// OWNERSHIP: Does not take ownership.
const char *key; const char *key;
// OWNERSHIP: Does not take ownership.
const char *prompt; const char *prompt;
}; };

8
main.c
View File

@ -46,14 +46,10 @@ static int run(const char *root_dir, const char *target) {
} }
cleanup_prompts: cleanup_prompts:
if (prompts) { dyn_array_free(prompts);
dyn_array_free(prompts);
}
cleanup_parsed: cleanup_parsed:
if (parsed) { cJSON_Delete(parsed);
cJSON_Delete(parsed);
}
cleanup_config: cleanup_config:
config_free(config); config_free(config);

6
specs/test/spec.json Normal file
View File

@ -0,0 +1,6 @@
{
"display": {
"type": "STRING",
"prompt": "This is a prompt"
}
}

View File

@ -14,6 +14,8 @@ struct Error *config_load(
const char *target, const char *target,
struct Config **config struct Config **config
) { ) {
assert(target);
if (cwd == 0) { if (cwd == 0) {
return ERROR_NEW(ERROR_CONFIG_ENV_CWD_INVALID, "Could not retrieve $CWD."); 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." ERROR_CONFIG_ENV_ROOT_DIR_INVALID, "No specified root directory."
); );
} }
assert(target);
// Check if the specified directory exists.
const char *segments[] = {root_dir, target}; const char *segments[] = {root_dir, target};
char *filepath = const char *filepath =
join_path_segments(sizeof(segments) / sizeof(char *), segments); join_path_segments(sizeof(segments) / sizeof(char *), segments);
struct stat sb; struct stat sb;
@ -58,7 +58,7 @@ struct Error *config_load(
(*config)->target = target; (*config)->target = target;
cleanup: cleanup:
free(filepath); free((void *)filepath);
return error; return error;
} }

View File

@ -1,13 +1,19 @@
#include "evaluator.h" #include "evaluator.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "path.h" #include "path.h"
#include "string_buf.h" #include "string_buf.h"
#include "validator.h"
static struct Error *find_run_sh(const struct Config *const config) { static struct Error *find_run_sh(const struct Config *const config) {
assert(config);
struct stat sb; struct stat sb;
const char *segments[] = {config->root_dir, config->target, "run.sh"}; const char *segments[] = {config->root_dir, config->target, "run.sh"};
char *filepath = char *filepath =
@ -35,9 +41,41 @@ static struct Error *find_run_sh(const struct Config *const config) {
return 0; 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( int evaluate_run_sh(
const struct Config *const config, const struct Config *const config,
const struct DynArray *const prompts, const struct DynArray *const fields,
struct Error **error struct Error **error
) { ) {
*error = find_run_sh(config); *error = find_run_sh(config);
@ -45,15 +83,28 @@ int evaluate_run_sh(
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (prompts) { struct StringBuf *env_buf = string_buf_new(512);
for (int i = 0; i < prompts->size; ++i) { 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"}; const char *segments[] = {config->root_dir, config->target, "run.sh"};
char *filepath = const char *filepath =
join_path_segments(sizeof(segments) / sizeof(char *), segments); join_path_segments(sizeof(segments) / sizeof(char *), segments);
const char *env = string_buf_convert(env_buf);
struct StringBuf *command_buf = string_buf_new(1024); struct StringBuf *command_buf = string_buf_new(1024);
string_buf_sappend(command_buf, "cd "); string_buf_sappend(command_buf, "cd ");
@ -61,13 +112,12 @@ int evaluate_run_sh(
string_buf_cappend(command_buf, '/'); string_buf_cappend(command_buf, '/');
string_buf_sappend(command_buf, config->target); string_buf_sappend(command_buf, config->target);
string_buf_sappend(command_buf, " && "); string_buf_sappend(command_buf, " && ");
string_buf_sappend(command_buf, "OUT="); string_buf_sappend(command_buf, env);
string_buf_sappend(command_buf, config->cwd);
string_buf_cappend(command_buf, ' ');
string_buf_sappend(command_buf, filepath); string_buf_sappend(command_buf, filepath);
const char *command = string_buf_convert(command_buf); const char *command = string_buf_convert(command_buf);
free(filepath); free((void *)env);
free((void *)filepath);
int exit_code = system(command); int exit_code = system(command);
free((void *)command); free((void *)command);

View File

@ -15,7 +15,6 @@ struct StringBuf *string_buf_new(size_t capacity) {
size_t string_buf_size(struct StringBuf *sb) { size_t string_buf_size(struct StringBuf *sb) {
assert(sb); assert(sb);
return sb->size; 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) { const char *string_buf_convert(struct StringBuf *sb) {
assert(sb); assert(sb);
const char *buf = sb->buf; const char *buf = sb->buf;
free(sb); free(sb);
return buf; return buf;
} }
void string_buf_free(struct StringBuf *sb) { void string_buf_free(struct StringBuf *sb) {
assert(sb); if (!sb) {
return;
}
free((void *)sb->buf); free((void *)sb->buf);
free(sb); free(sb);
sb = 0; sb = 0;

View File

@ -14,6 +14,7 @@ static struct Error *read_field(const cJSON *const field, struct Field **out) {
struct Error *error = 0; struct Error *error = 0;
*out = malloc(sizeof(struct Field)); *out = malloc(sizeof(struct Field));
(*out)->key = field->string;
const cJSON *type = cJSON_GetObjectItemCaseSensitive(field, "type"); const cJSON *type = cJSON_GetObjectItemCaseSensitive(field, "type");
if (!cJSON_IsString(type)) { if (!cJSON_IsString(type)) {
@ -94,9 +95,7 @@ struct Error *validate_spec_json(
return 0; return 0;
cleanup: cleanup:
if (*fields) { dyn_array_free(*fields);
dyn_array_free(*fields); *fields = 0;
*fields = 0;
}
return error; return error;
} }