From 683bf863ab40be9eda37f581b4abbb28e689273f Mon Sep 17 00:00:00 2001 From: Joshua Potter Date: Fri, 24 Nov 2023 20:29:24 -0700 Subject: [PATCH] Allow executing `run.sh`. --- Makefile | 4 +- include/evaluator.h | 4 +- include/string_buf.h | 96 ++++++++++++++++++++++++++++++++++++++++++ main.c | 10 ++++- specs/clang/run.sh | 2 +- src/config.c | 1 + src/dyn_array.c | 1 + src/evaluator.c | 45 +++++++++++++++----- src/string_buf.c | 84 ++++++++++++++++++++++++++++++++++++ test/runner.c | 5 +++ test/test_string_buf.h | 44 +++++++++++++++++++ 11 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 include/string_buf.h mode change 100644 => 100755 specs/clang/run.sh create mode 100644 src/string_buf.c create mode 100644 test/test_string_buf.h diff --git a/Makefile b/Makefile index 9230f7b..9a9f4cb 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -BUILD=clang -Og -g -I include src/*.c main.c -o bootstrap +BUILD=clang -Og -g -I include src/*.c main.c -o bootstrap -lm all: build bear @@ -12,6 +12,6 @@ test: test/runner $^ test/runner: include/*.h src/*.c test/*.h test/*.c - clang -I include src/*.c test/*.c -o test/runner + clang -I include src/*.c test/*.c -o test/runner -lm .PHONY: test diff --git a/include/evaluator.h b/include/evaluator.h index a8811e8..e14c899 100644 --- a/include/evaluator.h +++ b/include/evaluator.h @@ -5,8 +5,10 @@ #include "validator.h" enum SpecEvaluationError { + // Then `run.sh` file could not be found. + SEE_RUN_SH_NOT_FOUND = 1, // The provided input does not match the expected prompt response type. - SEE_INVALID_PROMPT_RESPONSE = 1 + SEE_INVALID_PROMPT_RESPONSE, }; enum SpecEvaluationError evaluate_spec_json( diff --git a/include/string_buf.h b/include/string_buf.h new file mode 100644 index 0000000..224270b --- /dev/null +++ b/include/string_buf.h @@ -0,0 +1,96 @@ +#ifndef _BOOTSTRAP_STRING_BUF_H +#define _BOOTSTRAP_STRING_BUF_H + +#include + +/** + * @brief A dynamic character array. + * + * A `char*` wrapper. Appending `char`s or NUL-terminated strings allocates + * additional space as needed. + */ +struct StringBuf; + +/** + * Create a new `StringBuf` instance. + * + * @param capacity + * The initial size of the internal array (including the trailing `NUL` + * character). To avoid too many reallocations, aim to make this value large + * enough to accommodate the size the string is expected to eventually take. + * @return + * A new `StringBuf` instance. The caller takes ownership of this value. + * + * @see string_buf_free + */ +struct StringBuf *string_buf_new(size_t capacity); + +/** + * Return the number of characters contained in the internal buffer. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * @return + * The number of characters contained in the internal buffer. + */ +size_t string_buf_size(struct StringBuf *sb); + +/** + * Return the internal `NUL`-terminated string buffer. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * @return + * The internally managed string. + */ +const char *string_buf_value(struct StringBuf *sb); + +/** + * Append a character to the end of a `StringBuf`. + * + * If appending would cause the internal buffer to overflow, reallocates the + * internal array to accommodate. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * @param c + * The `char` to append to the end of `sb`. + */ +void string_buf_cappend(struct StringBuf *sb, char c); + +/** + * Append a `NUL`-terminated string to the end of a `StringBuf`. + * + * If appending would cause the internal buffer to overflow, `realloc`s are + * performed internally to accommodate. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * @param s + * The `char*` to append to the end of `sb`. + */ +void string_buf_sappend(struct StringBuf *sb, const char s[static 1]); + +/** + * Convert a `StringBuf` instance into a `char*`. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * @return + * A null pointer if `sb` is null. Otherwise a `NUL`-terminated string + * corresponding to the value of `sb`. The caller takes ownership of this + * value. + */ +const char *string_buf_convert(struct StringBuf *sb); + +/** + * Deallocate a previously allocated `StringBuf` instance. + * + * @param sb + * A valid pointer to a `StringBuf` instance. + * + * @see string_buf_new + */ +void string_buf_free(struct StringBuf *sb); + +#endif /* _BOOTSTRAP_STRING_BUF_H */ diff --git a/main.c b/main.c index 16c946f..eb50289 100644 --- a/main.c +++ b/main.c @@ -5,6 +5,7 @@ #include "cJSON.h" #include "config.h" +#include "evaluator.h" #include "parser.h" #include "validator.h" @@ -73,7 +74,14 @@ static int run(const char *root_dir, const char *target) { goto cleanup_parsed; } - // TODO: Run `run.sh`. + switch (evaluate_spec_json(config, prompts)) { + case SEE_RUN_SH_NOT_FOUND: + fprintf(stderr, "Could not find `%s/run.sh`.\n", target); + goto cleanup_parsed; + case SEE_INVALID_PROMPT_RESPONSE: + fprintf(stderr, "Could not interpret response.\n"); + goto cleanup_parsed; + } retval = EXIT_SUCCESS; diff --git a/specs/clang/run.sh b/specs/clang/run.sh old mode 100644 new mode 100755 index a8741c7..2fbbbcb --- a/specs/clang/run.sh +++ b/specs/clang/run.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -cp -r "template/*" "$OUT" +cp -r "template" "$OUT" diff --git a/src/config.c b/src/config.c index c5fcb15..34b177b 100644 --- a/src/config.c +++ b/src/config.c @@ -57,4 +57,5 @@ void config_free(struct Config *config) { return; } free(config); + config = 0; } diff --git a/src/dyn_array.c b/src/dyn_array.c index 0bdfb1d..0f6fad4 100644 --- a/src/dyn_array.c +++ b/src/dyn_array.c @@ -35,4 +35,5 @@ void dyn_array_free(struct DynArray *a) { } free(a->buf); free(a); + a = 0; } diff --git a/src/evaluator.c b/src/evaluator.c index 341d2d2..328e7db 100644 --- a/src/evaluator.c +++ b/src/evaluator.c @@ -2,36 +2,61 @@ #include #include +#include #include "path.h" +#include "string_buf.h" -static int find_run_sh(const struct Config *const config, FILE **handle) { +static enum SpecEvaluationError find_run_sh(const struct Config *const config) { + struct stat sb; const char *segments[] = {config->root_dir, config->target, "run.sh"}; char *filepath = join_path_segments(sizeof(segments) / sizeof(char *), segments); + int stat_res = stat(filepath, &sb); + free(filepath); - int retval = 0; - // It is ok if the file does not exist. It is not ok if we couldn't open the - // file for any other reason. - *handle = fopen(filepath, "r"); - if (!*handle && errno != ENOENT) { - retval = errno; + if (stat_res == -1 && errno == ENOENT) { + return SEE_RUN_SH_NOT_FOUND; } - free(filepath); - return retval; + return 0; } enum SpecEvaluationError evaluate_spec_json( const struct Config *const config, const struct DynArray *const prompts ) { + enum SpecEvaluationError retval = find_run_sh(config); + if (retval != 0) { + return retval; + } + if (prompts) { for (int i = 0; i < prompts->size; ++i) { } // TODO: Display prompts and collect answers. } - // TODO: Run `run.sh`. + const char *segments[] = {config->root_dir, config->target, "run.sh"}; + char *filepath = + join_path_segments(sizeof(segments) / sizeof(char *), segments); + + struct StringBuf *command_buf = string_buf_new(1024); + string_buf_sappend(command_buf, "cd "); + string_buf_sappend(command_buf, config->root_dir); + 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, filepath); + const char *command = string_buf_convert(command_buf); + + free(filepath); + + // TODO: Want to return this status out. + int status = system(command); + ; return 0; } diff --git a/src/string_buf.c b/src/string_buf.c new file mode 100644 index 0000000..fdb98e7 --- /dev/null +++ b/src/string_buf.c @@ -0,0 +1,84 @@ +#include "string_buf.h" + +#include +#include +#include +#include + +struct StringBuf { + char *buf; + // The length of @buf excluding `NUL`. + size_t size; + // The allocated size of @buf including `NUL`. + size_t _capacity; +}; + +struct StringBuf *string_buf_new(size_t capacity) { + struct StringBuf *sb = malloc(sizeof(struct StringBuf)); + sb->buf = calloc(capacity, sizeof(char)); + sb->size = 0; + sb->_capacity = capacity; + return sb; +} + +size_t string_buf_size(struct StringBuf *sb) { + assert(sb); + + return sb->size; +} + +const char *string_buf_value(struct StringBuf *sb) { + assert(sb); + + return sb->buf; +} + +void string_buf_cappend(struct StringBuf *sb, char c) { + assert(sb); + + if (sb->_capacity) { + sb->_capacity *= 2; + } else { + sb->_capacity = 2; + } + + sb->buf = realloc((void *)sb->buf, sb->_capacity); + sb->buf[sb->size++] = c; + sb->buf[sb->size] = 0; +} + +void string_buf_sappend(struct StringBuf *sb, const char s[static 1]) { + assert(sb); + + double goal = sb->size + strlen(s) + 1; + double denom = sb->_capacity ? sb->_capacity : 1; + double scale = pow(2, ceil(log2(goal / denom))); + + if (sb->_capacity) { + sb->_capacity *= scale; + } else { + sb->_capacity = scale; + } + + sb->buf = realloc((void *)sb->buf, sb->_capacity); + for (const char *i = s; *i; ++i) { + sb->buf[sb->size++] = *i; + } + sb->buf[sb->size] = 0; +} + +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); + + free((void *)sb->buf); + free(sb); + sb = 0; +} diff --git a/test/runner.c b/test/runner.c index 6933d42..89ea801 100644 --- a/test/runner.c +++ b/test/runner.c @@ -3,6 +3,7 @@ #include "test_dyn_array.h" #include "test_parser.h" #include "test_path.h" +#include "test_string_buf.h" #include "test_validator.h" int main(int argc, char *argv[]) { @@ -18,6 +19,10 @@ int main(int argc, char *argv[]) { sput_run_test(test_dyn_array_zero_capacity); sput_run_test(test_dyn_array_nonzero_capacity); + sput_enter_suite("string_buf"); + sput_run_test(test_string_buf_sappend); + sput_run_test(test_string_buf_cappend); + sput_enter_suite("path"); sput_run_test(test_join_path_single_segments); sput_run_test(test_join_path_multiple_segments); diff --git a/test/test_string_buf.h b/test/test_string_buf.h new file mode 100644 index 0000000..d56d20b --- /dev/null +++ b/test/test_string_buf.h @@ -0,0 +1,44 @@ +#ifndef _BOOTSTRAP_TEST_STRING_BUF +#define _BOOTSTRAP_TEST_STRING_BUF + +#include "sput.h" +#include "string_buf.h" + +static void test_string_buf_sappend() { + struct StringBuf *sb = string_buf_new(0); + string_buf_sappend(sb, "hello world"); + string_buf_sappend(sb, "!!"); + + sput_fail_unless( + string_buf_size(sb) == strlen("hello world!!"), "sappend size" + ); + const char *converted = string_buf_convert(sb); + sput_fail_unless( + strcmp(converted, "hello world!!") == 0, "sappend converted" + ); +} + +static void test_string_buf_cappend() { + struct StringBuf *sb = string_buf_new(0); + string_buf_sappend(sb, "hello world"); + string_buf_cappend(sb, '!'); + string_buf_cappend(sb, '!'); + + sput_fail_unless( + string_buf_size(sb) == strlen("hello world!!"), "cappend size" + ); + const char *converted = string_buf_convert(sb); + sput_fail_unless( + strcmp(converted, "hello world!!") == 0, "cappend converted" + ); +} + +static void test_string_buf_nonzero_capacity() { + struct StringBuf *sb = string_buf_new(100); + string_buf_sappend(sb, "hello world"); + string_buf_cappend(sb, '!'); + string_buf_free(sb); + sput_fail_unless(sb == 0, "free"); +} + +#endif /* _BOOTSTRAP_TEST_STRING_BUF */