Allow executing `run.sh`.

pull/9/head
Joshua Potter 2023-11-24 20:29:24 -07:00
parent a17d6eb3e3
commit 683bf863ab
11 changed files with 281 additions and 15 deletions

View File

@ -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 all: build bear
@ -12,6 +12,6 @@ test: test/runner
$^ $^
test/runner: include/*.h src/*.c test/*.h test/*.c 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 .PHONY: test

View File

@ -5,8 +5,10 @@
#include "validator.h" #include "validator.h"
enum SpecEvaluationError { 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. // 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( enum SpecEvaluationError evaluate_spec_json(

96
include/string_buf.h Normal file
View File

@ -0,0 +1,96 @@
#ifndef _BOOTSTRAP_STRING_BUF_H
#define _BOOTSTRAP_STRING_BUF_H
#include <stdlib.h>
/**
* @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 */

10
main.c
View File

@ -5,6 +5,7 @@
#include "cJSON.h" #include "cJSON.h"
#include "config.h" #include "config.h"
#include "evaluator.h"
#include "parser.h" #include "parser.h"
#include "validator.h" #include "validator.h"
@ -73,7 +74,14 @@ static int run(const char *root_dir, const char *target) {
goto cleanup_parsed; 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; retval = EXIT_SUCCESS;

2
specs/clang/run.sh Normal file → Executable file
View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cp -r "template/*" "$OUT" cp -r "template" "$OUT"

View File

@ -57,4 +57,5 @@ void config_free(struct Config *config) {
return; return;
} }
free(config); free(config);
config = 0;
} }

View File

@ -35,4 +35,5 @@ void dyn_array_free(struct DynArray *a) {
} }
free(a->buf); free(a->buf);
free(a); free(a);
a = 0;
} }

View File

@ -2,36 +2,61 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h>
#include "path.h" #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"}; const char *segments[] = {config->root_dir, config->target, "run.sh"};
char *filepath = char *filepath =
join_path_segments(sizeof(segments) / sizeof(char *), segments); join_path_segments(sizeof(segments) / sizeof(char *), segments);
int stat_res = stat(filepath, &sb);
free(filepath);
int retval = 0; if (stat_res == -1 && errno == ENOENT) {
// It is ok if the file does not exist. It is not ok if we couldn't open the return SEE_RUN_SH_NOT_FOUND;
// file for any other reason.
*handle = fopen(filepath, "r");
if (!*handle && errno != ENOENT) {
retval = errno;
} }
free(filepath); return 0;
return retval;
} }
enum SpecEvaluationError evaluate_spec_json( enum SpecEvaluationError evaluate_spec_json(
const struct Config *const config, const struct DynArray *const prompts const struct Config *const config, const struct DynArray *const prompts
) { ) {
enum SpecEvaluationError retval = find_run_sh(config);
if (retval != 0) {
return retval;
}
if (prompts) { if (prompts) {
for (int i = 0; i < prompts->size; ++i) { for (int i = 0; i < prompts->size; ++i) {
} }
// TODO: Display prompts and collect answers. // 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; return 0;
} }

84
src/string_buf.c Normal file
View File

@ -0,0 +1,84 @@
#include "string_buf.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
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;
}

View File

@ -3,6 +3,7 @@
#include "test_dyn_array.h" #include "test_dyn_array.h"
#include "test_parser.h" #include "test_parser.h"
#include "test_path.h" #include "test_path.h"
#include "test_string_buf.h"
#include "test_validator.h" #include "test_validator.h"
int main(int argc, char *argv[]) { 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_zero_capacity);
sput_run_test(test_dyn_array_nonzero_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_enter_suite("path");
sput_run_test(test_join_path_single_segments); sput_run_test(test_join_path_single_segments);
sput_run_test(test_join_path_multiple_segments); sput_run_test(test_join_path_multiple_segments);

44
test/test_string_buf.h Normal file
View File

@ -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 */