Allow reading in STRING prompts.
parent
537226c651
commit
16e971af0b
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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
8
main.c
|
@ -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);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"display": {
|
||||||
|
"type": "STRING",
|
||||||
|
"prompt": "This is a prompt"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue