diff options
| author | Joseph Hunkeler <jhunkeler@gmail.com> | 2026-03-25 13:22:42 -0400 |
|---|---|---|
| committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2026-03-25 13:22:42 -0400 |
| commit | 44342619d43375313b56f7c530c4532b0f69be25 (patch) | |
| tree | cbb68df916001a3b6c3ea17eeea40e28cdc8f307 | |
| parent | d79588bdf85b8c1514019e679c7eefbc9014edd9 (diff) | |
| download | do-44342619d43375313b56f7c530c4532b0f69be25.tar.gz | |
Refactor program structure
| -rw-r--r-- | CMakeLists.txt | 7 | ||||
| -rw-r--r-- | common.h | 27 | ||||
| -rw-r--r-- | main.c | 488 | ||||
| -rw-r--r-- | target.c | 131 | ||||
| -rw-r--r-- | target.h | 34 | ||||
| -rw-r--r-- | targetlist.c | 301 | ||||
| -rw-r--r-- | targetlist.h | 24 |
7 files changed, 531 insertions, 481 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3791872..d2ce118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,4 +20,9 @@ endif() add_executable(do main.c str.c - str.h) + str.h + targetlist.c + targetlist.h + common.h + target.c + target.h) diff --git a/common.h b/common.h new file mode 100644 index 0000000..b8c38e0 --- /dev/null +++ b/common.h @@ -0,0 +1,27 @@ +#ifndef DO_COMMON_H +#define DO_COMMON_H + +#include <stdlib.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "str.h" +#include "target.h" +#include "targetlist.h" + +#define safe_free(PTR) { \ + do { \ + free((PTR)); \ + (PTR) = NULL; \ + } while (0); \ +} + +#define safe_free_array(PTR, LEN) { \ + do { \ + for (size_t p = 0; p < (LEN); p++) { free((PTR)[p]); } \ + free((PTR)); \ + } while (0); \ +} + +#endif //DO_COMMON_H
\ No newline at end of file @@ -1,476 +1,4 @@ -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include "str.h" - -#define MAX_TARGETS 255 - -#define FREE_SAFE(PTR) { \ - do { \ - free((PTR)); \ - (PTR) = NULL; \ - } while (0); \ -} -#define FREE_ARRAY(PTR, LEN) { \ - do { \ - for (size_t p = 0; p < (LEN); p++) { free((PTR)[p]); } \ - free((PTR)); \ - } while (0); \ -} - - -struct TargetList { - struct Target **targets; - size_t count_used; - size_t count_alloc; -}; - -struct TargetInfo { - size_t line_no; - char *line; - char *filename; -}; - -struct Target { - char *id; - char *_tmp_deps; - struct TargetList *dependencies; - char *script; - struct TargetInfo info; -}; - -void free_target(struct Target **target); -void free_target_list(struct TargetList **list); - - -char *create_target_script_file(struct Target *t) { - char template[] = "do_XXXXXX"; - if (mkstemps(template, 0) < 0) { - perror("mkstemps"); - return NULL; - } - return strdup(template); -} - -struct TargetList *new_target_list(const size_t len) { - struct TargetList *list = calloc(1, sizeof(struct TargetList)); - if (!list) { - fprintf(stderr, "unable to allocate memory for list\n"); - return NULL; - } - - list->count_alloc = len; - list->targets = calloc(list->count_alloc + 1, sizeof(struct Target *)); - if (!list->targets) { - fprintf(stderr, "unable to allocate memory for list->targets\n"); - FREE_SAFE(list); - return NULL; - } - - return list; -} - -int add_target(struct TargetList *list, struct Target *t); - - -struct Target *new_target() { - struct Target *t = calloc(1, sizeof(struct Target)); - return t; -} - -struct Target *clone_target(const struct Target *src) { - struct Target *t = new_target(); - if (!t) { - fprintf(stderr, "unable to allocate memory to clone target\n"); - return NULL; - } - t->id = src->id ? strdup(src->id) : NULL; - t->_tmp_deps = src->_tmp_deps ? strdup(src->_tmp_deps) : NULL; - t->script = src->script ? strdup(src->script) : NULL; - t->info.line = src->info.line ? strdup(src->info.line) : NULL; - t->info.filename = src->info.filename ? strdup(src->info.filename) : NULL; - if (src->dependencies && src->dependencies->count_used) { - t->dependencies = new_target_list(src->dependencies->count_alloc); - if (!t->dependencies) { - fprintf(stderr, "unable to allocate memory to clone target dependencies\n"); - return NULL; - } - for (size_t i = 0; i < src->dependencies->count_used; i++) { - struct Target *src_target = src->dependencies->targets[i]; - //struct Target *dep = clone_target(src->dependencies->targets[i]); - add_target(t->dependencies, src_target); - //free_target(&dep); - //add_target(t->dependencies, src->dependencies->targets[i]); - } - } - return t; -} - -int append_target_list(struct TargetList **dest, const struct TargetList *src) { - for (size_t i = 0; i < src->count_used; i++) { - struct Target *t = src->targets[i]; - if (!t) { - return -1; - } - add_target((*dest), t); - } - return 0; -} - -int add_target(struct TargetList *list, struct Target *t) { - if (list->count_used > list->count_alloc) { - fprintf(stderr, "too many items\n"); - return -1; - } - list->targets[list->count_used] = t; - list->count_used++; - return 0; -} - -void free_target(struct Target **target) { - FREE_SAFE((*target)->id); - FREE_SAFE((*target)->_tmp_deps); - FREE_SAFE((*target)->script); - FREE_SAFE((*target)->info.line); - FREE_SAFE((*target)->info.filename); - if ((*target)->dependencies) { - // the dependencies are pointers to other targets, not allocated on the heap. - FREE_SAFE((*target)->dependencies->targets); - FREE_SAFE((*target)->dependencies); - } - FREE_SAFE((*target)); -} - -void free_target_list(struct TargetList **list) { - if (!list || !*list) { - return; - } - for (size_t i = 0; i < (*list)->count_used; i++) { - free_target(&(*list)->targets[i]); - } - FREE_SAFE((*list)->targets); - FREE_SAFE(*list); -} - -struct Target *get_target(const struct TargetList *list, const char *id) { - for (size_t i = 0; i < list->count_used; i++) { - struct Target *t = list->targets[i]; - if (strcmp(t->id, id) == 0) { - return t; - } - } - return NULL; -} - -struct TargetList *get_targets_from_dofile(const char *filename) { - struct TargetList *list = new_target_list(MAX_TARGETS); - if (!list) { - return NULL; - } - - FILE *fp = fopen(filename, "r"); - if (!fp) { - free_target_list(&list); - return NULL; - } - - char *line = calloc(BUFSIZ + 1, sizeof(char)); - if (!line) { - free_target_list(&list); - return NULL; - } - - size_t buffer_size = BUFSIZ; - struct Target *t = NULL; - size_t line_no = 0; - while (getline(&line, &buffer_size, fp) > 0) { - // Skip comments - if (line[0] == '#') { - line_no++; - continue; - } - - char *deps_delim = strchr(line, ':'); - const char *deps = deps_delim ? deps_delim + 1 : NULL; - char *var_delim = strchr(line, '='); - const int is_var = isalpha(line[0]) && var_delim != NULL; - const int is_target = isalnum(line[0]) && deps_delim != NULL; - const int is_script = isspace(line[0]); - const int is_include = strncmp(line, "include", strlen("include")) == 0; - - if (t && is_script) { - if (!t->script) { - t->script = calloc(BUFSIZ + 1, sizeof(char)); - if (!t->script) { - fprintf(stderr, "unable to allocate memory for script data\n"); - FREE_SAFE(line); - free_target_list(&list); - return NULL; - } - t->info.filename = strdup(filename); - if (!t->info.filename) { - fprintf(stderr, "unable to allocate memory for info.filename\n"); - FREE_SAFE(line); - free_target_list(&list); - return NULL; - } - } - char *buf_start = line; - for (size_t i = 0; i < strlen(line); i++) { - if (line[i] == '\t' && i < 4) { - continue; - } - if (isspace(line[i]) && i < 4) { - continue; - } - buf_start = &line[i]; - break; - } - strcat(t->script, buf_start); - } else if (is_target) { - t = new_target(); - if (!t) { - fprintf(stderr, "unable to allocate memory for new target\n"); - FREE_SAFE(line); - free_target(&t); - free_target_list(&list); - return NULL; - } - t->info.line_no = line_no; - t->info.line = strdup(line); - - if (!deps_delim) { - fprintf(stderr, "%s: line %lu: invalid target definition: %s\n", filename, t->info.line_no, line); - fprintf(stderr, ">>> %s\n", t->info.line); - FREE_SAFE(line); - free_target(&t); - free_target_list(&list); - return NULL; - } - *deps_delim = '\0'; - t->id = strdup(line); - if (deps) { - t->_tmp_deps = strdup(deps); - if (!t->_tmp_deps) { - fprintf(stderr, "unable to allocate memory for temporary dependency data\n"); - FREE_SAFE(line); - free_target(&t); - free_target_list(&list); - return NULL; - } - if (t->_tmp_deps[strlen(t->_tmp_deps) ? strlen(t->_tmp_deps) - 1 : 0] == '\n') { - t->_tmp_deps[strlen(t->_tmp_deps) ? strlen(t->_tmp_deps) - 1 : 0] = '\0'; - } - } - add_target(list, t); - } else if (is_var) { - printf("variable detected\n"); - size_t num_split = 0; - char **pair = split(line, "=", 1, &num_split); - if (num_split < 2) { - fprintf(stderr, "%s: line: %lu: invalid variable definition: %s\n", filename, line_no, line); - fprintf(stderr, ">>> %s\n", line); - FREE_SAFE(line); - free_target(&t); - free_target_list(&list); - return NULL; - } - FREE_ARRAY(pair, num_split); - } else if (is_include) { - size_t num_split = 0; - char **pair = split(line, " ", 1, &num_split); - if (!pair) { - fprintf(stderr, "%s: line: %lu: invalid include definition: %s\n", filename, line_no, line); - fprintf(stderr, ">>> %s\n", line); - FREE_SAFE(line); - free_target(&t); - free_target_list(&list); - return NULL; - } - if (pair[1][strlen(pair[1]) ? strlen(pair[1]) - 1 : 0] == '\n') { - pair[1][strlen(pair[1]) ? strlen(pair[1]) - 1 : 0] = '\0'; - } - - printf("Including file: %s\n", pair[1]); - struct TargetList *included_targets = get_targets_from_dofile(pair[1]); - if (included_targets) { - if (append_target_list(&list, included_targets)) { - fprintf(stderr, "unable to append to target list %p\n", list); - FREE_SAFE(line); - free_target_list(&list); - return NULL; - } - //free_target_list(&included_targets); - for (size_t i = 0; i < list->count_used; i++) { - included_targets->targets[i] = NULL; - } - free(included_targets->targets); - free(included_targets); - } else { - fprintf(stderr, "%s: line %lu: failed to include file: %s\n", filename, line_no, pair[1]); - FREE_SAFE(line); - FREE_ARRAY(pair, num_split); - free_target(&t); - free_target_list(&list); - return NULL; - } - FREE_ARRAY(pair, num_split); - } - line_no++; - } - - // Add dependencies - for (size_t i = 0; i < list->count_used; i++) { - if (!list->targets[i]->_tmp_deps || !strlen(list->targets[i]->_tmp_deps)) { - continue; - } - char *_tmp_deps_orig = list->targets[i]->_tmp_deps; - list->targets[i]->dependencies = new_target_list(MAX_TARGETS); - const char *token = NULL; - while ((token = strsep(&list->targets[i]->_tmp_deps, " ")) != NULL) { - char *data = strdup(token); - if (!strlen(data)) { - FREE_SAFE(data); - continue; - } - - if (data[strlen(data) ? strlen(data) - 1 : 0] == '\n') { - data[strlen(data) ? strlen(data) - 1 : 0] = '\0'; - } - - const int target_exists = get_target(list, data) != NULL; - if (!target_exists) { - fprintf(stderr, "%s: line %lu: target not found: %s\n", filename, line_no, data); - fprintf(stderr, ">>> %s\n", list->targets[i]->info.line); - FREE_SAFE(data); - FREE_SAFE(line); - free_target_list(&list); - return NULL; - } - - for (size_t j = 0; j < list->count_used; j++) { - if (strcmp(list->targets[j]->id, data) == 0) { - if (list->targets[i]->id == list->targets[j]->id) { - fprintf(stderr, "%s: line %lu: target '%s' cannot target itself (circular dependency)\n", filename, list->targets[i]->info.line_no + 1, list->targets[i]->id); - fprintf(stderr, ">>> %s\n", list->targets[i]->info.line); - free_target(&t); - free_target_list(&list); - return NULL; - } - add_target(list->targets[i]->dependencies, list->targets[j]); - } - } - FREE_SAFE(data); - } - list->targets[i]->_tmp_deps = _tmp_deps_orig; - } - - FREE_SAFE(line); - fclose(fp); - return list; -} - -void show_targets(const struct TargetList *list) { - for (size_t i = 0; i < list->count_used; i++) { - const struct Target *t = list->targets[i]; - printf("TARGET: %s\n", t->id); - printf("DEPENDENCIES: "); - if (t->dependencies && t->dependencies->count_used) { - printf("\n"); - for (size_t j = 0; j < t->dependencies->count_used; j++) { - printf(" - %s\n", t->dependencies->targets[j]->id); - } - } else { - printf("N/A\n"); - } - printf("SCRIPT: "); - if (t->script && strlen(t->script)) { - printf("\n%s\n", t->script); - } else { - printf("N/A\n"); - } - printf("\n"); - } -} - -int run_target(struct Target *t, const size_t *depth) { - if (!t || *depth > 100) { - return -1; - } - char *filename = create_target_script_file(t); - if (!filename) { - fprintf(stderr, "failed to create target script\n"); - return -1; - } - - if (chmod(filename, 0750) < 0) { - perror("chmod"); - FREE_SAFE(filename); - return -1; - } - - FILE *fp = fopen(filename, "w+"); - if (!fp) { - perror("unable to open target script for writing"); - remove(filename); - FREE_SAFE(filename); - return -1; - } - fprintf(fp, "%s", t->script ? t->script : ""); - fclose(fp); - - char *cmd = NULL; - if (asprintf(&cmd, "bash %s", filename) < 0) { - perror("asprintf"); - remove(filename); - FREE_SAFE(filename); - return -1; - } - - printf("==> Running target %s\n", t->id); - const int status = system(cmd); - remove(filename); - - FREE_SAFE(cmd); - FREE_SAFE(filename); - - return status; -} - -#define MAX_DEPTH 100 -int run_targets(struct Target *t, size_t *depth) { - int fail = 0; - - *depth += 1; - if (*depth > MAX_DEPTH) { - fprintf(stderr, "maximum recursion depth reached: %d\n", MAX_DEPTH); - if (t) { - fprintf(stderr, "%s: line %lu: %s\n", t->info.filename ? t->info.filename : "???", t->info.line_no, t->info.line); - } - *depth -= 1; - return -1; - } - - if (t && t->dependencies) { - for (size_t i = 0; i < t->dependencies->count_used; i++) { - struct Target *d = t->dependencies->targets[i]; - - if (run_targets(d, depth)) { - fail = 1; - *depth -= 1; - break; - } - } - } - if (!fail) { - *depth -= 1; - return run_target(t, depth); - } - *depth -= 1; - return -1; -} +#include "common.h" int main(int argc, char *argv[]) { const char *dofile_name = argv[1] ? argv[1] : "dofile"; @@ -484,28 +12,28 @@ int main(int argc, char *argv[]) { return 1; } - struct TargetList *targets = get_targets_from_dofile(dofile_name); + struct TargetList *targets = target_list_from_file(dofile_name); if (!targets) { fprintf(stderr, "%s: failed to resolve all target(s)\n", dofile_name); return 1; } - show_targets(targets); + target_list_show(targets); puts(""); - struct Target *target = get_target(targets, target_name); + struct Target *target = target_list_get(targets, target_name); if (!target) { fprintf(stderr, "%s: target not found: '%s'\n", dofile_name, target_name); - free_target_list(&targets); + target_list_free(&targets); return 1; } size_t depth = 0; - if (run_targets(target, &depth)) { + if (target_run_all(target, &depth)) { fprintf(stderr, "an error occurred\n"); - free_target_list(&targets); + target_list_free(&targets); return 1; } - free_target_list(&targets); + target_list_free(&targets); return 0; }
\ No newline at end of file diff --git a/target.c b/target.c new file mode 100644 index 0000000..bbda4e6 --- /dev/null +++ b/target.c @@ -0,0 +1,131 @@ +#include "common.h" + +struct Target *target_new() { + struct Target *t = calloc(1, sizeof(struct Target)); + return t; +} + +struct Target *target_clone(const struct Target *src) { + struct Target *t = target_new(); + if (!t) { + fprintf(stderr, "unable to allocate memory to clone target\n"); + return NULL; + } + t->id = src->id ? strdup(src->id) : NULL; + t->_tmp_deps = src->_tmp_deps ? strdup(src->_tmp_deps) : NULL; + t->script = src->script ? strdup(src->script) : NULL; + t->info.line = src->info.line ? strdup(src->info.line) : NULL; + t->info.filename = src->info.filename ? strdup(src->info.filename) : NULL; + if (src->dependencies && src->dependencies->count_used) { + t->dependencies = target_list_new(src->dependencies->count_alloc); + if (!t->dependencies) { + fprintf(stderr, "unable to allocate memory to clone target dependencies\n"); + return NULL; + } + for (size_t i = 0; i < src->dependencies->count_used; i++) { + struct Target *src_target = src->dependencies->targets[i]; + target_list_add(t->dependencies, src_target); + } + } + return t; +} + +char *target_create_script_file(struct Target *t) { + char template[] = TARGET_SCRIPT_FILENAME_TEMPLATE; + if (mkstemps(template, 0) < 0) { + perror("mkstemps"); + return NULL; + } + return strdup(template); +} + +int target_run_all(struct Target *t, size_t *depth) { + int fail = 0; + + *depth += 1; + if (*depth > TARGET_RUN_MAX_DEPTH) { + fprintf(stderr, "maximum recursion depth reached: %d\n", TARGET_RUN_MAX_DEPTH); + if (t) { + fprintf(stderr, "%s: line %lu: %s\n", t->info.filename ? t->info.filename : "???", t->info.line_no, t->info.line); + } + *depth -= 1; + return -1; + } + + if (t && t->dependencies) { + for (size_t i = 0; i < t->dependencies->count_used; i++) { + struct Target *d = t->dependencies->targets[i]; + + if (target_run_all(d, depth)) { + fail = 1; + *depth -= 1; + break; + } + } + } + if (!fail) { + *depth -= 1; + return target_run(t, depth); + } + *depth -= 1; + return -1; +} + +int target_run(struct Target *t, const size_t *depth) { + if (!t || *depth > 100) { + return -1; + } + char *filename = target_create_script_file(t); + if (!filename) { + fprintf(stderr, "failed to create target script\n"); + return -1; + } + + if (chmod(filename, 0750) < 0) { + perror("chmod"); + safe_free(filename); + return -1; + } + + FILE *fp = fopen(filename, "w+"); + if (!fp) { + perror("unable to open target script for writing"); + remove(filename); + safe_free(filename); + return -1; + } + fprintf(fp, "%s", t->script ? t->script : ""); + fclose(fp); + + char *cmd = NULL; + if (asprintf(&cmd, "bash %s", filename) < 0) { + perror("asprintf"); + remove(filename); + safe_free(filename); + return -1; + } + + printf("==> Running target %s\n", t->id); + const int status = system(cmd); + remove(filename); + + safe_free(cmd); + safe_free(filename); + + return status; +} + +void target_free(struct Target **target) { + safe_free((*target)->id); + safe_free((*target)->_tmp_deps); + safe_free((*target)->script); + safe_free((*target)->info.line); + safe_free((*target)->info.filename); + if ((*target)->dependencies) { + // Dependencies are shared between lists. Do not wipe out the targets _here_. + safe_free((*target)->dependencies->targets); + safe_free((*target)->dependencies); + } + safe_free((*target)); +} + diff --git a/target.h b/target.h new file mode 100644 index 0000000..a035e42 --- /dev/null +++ b/target.h @@ -0,0 +1,34 @@ +#ifndef DO_TARGET_H +#define DO_TARGET_H + +#include <stdlib.h> +#include <sys/stat.h> + +#define TARGET_RUN_MAX_DEPTH 100 +#define TARGET_SCRIPT_FILENAME_TEMPLATE "do_XXXXXX" + +// Data structures +struct TargetInfo { + size_t line_no; + char *line; + char *filename; +}; + +struct Target { + char *id; + char *_tmp_deps; + char *script; + struct TargetList *dependencies; + struct TargetInfo info; +}; + + +// Prototypes +struct Target *target_new(); +struct Target *target_clone(const struct Target *src); +char *target_create_script_file(struct Target *t); +int target_run(struct Target *t, const size_t *depth); // single +int target_run_all(struct Target *t, size_t *depth); // recursive +void target_free(struct Target **target); + +#endif //DO_TARGET_H
\ No newline at end of file diff --git a/targetlist.c b/targetlist.c new file mode 100644 index 0000000..574376d --- /dev/null +++ b/targetlist.c @@ -0,0 +1,301 @@ +#include "common.h" + +struct TargetList *target_list_new(const size_t len) { + struct TargetList *list = calloc(1, sizeof(struct TargetList)); + if (!list) { + fprintf(stderr, "unable to allocate memory for list\n"); + return NULL; + } + + list->count_alloc = len; + list->targets = calloc(list->count_alloc + 1, sizeof(struct Target *)); + if (!list->targets) { + fprintf(stderr, "unable to allocate memory for list->targets\n"); + safe_free(list); + return NULL; + } + + return list; +} + +// Append a target list to an existing target list +int target_list_append(struct TargetList **dest, const struct TargetList *src) { + for (size_t i = 0; i < src->count_used; i++) { + struct Target *t = src->targets[i]; + if (!t) { + return -1; + } + target_list_add((*dest), t); + } + return 0; +} + +// Add target to list +int target_list_add(struct TargetList *list, struct Target *t) { + if (list->count_used >= list->count_alloc) { + fprintf(stderr, "%p: growing list from %zu records ", list, list->count_alloc); + list->count_alloc *= 2; + fprintf(stderr, "to %zu records\n", list->count_alloc); + struct Target **tmp = realloc(list->targets, list->count_alloc * sizeof(*list->targets)); + if (!tmp) { + fprintf(stderr, "unable to reallocate memory for target list\n"); + return -1; + } + list->targets = tmp; + } + list->targets[list->count_used] = t; + list->count_used++; + return 0; +} + +void target_list_free(struct TargetList **list) { + if (!list || !*list) { + return; + } + for (size_t i = 0; i < (*list)->count_used; i++) { + target_free(&(*list)->targets[i]); + } + safe_free((*list)->targets); + safe_free(*list); +} + +struct Target *target_list_get(const struct TargetList *list, const char *id) { + for (size_t i = 0; i < list->count_used; i++) { + struct Target *t = list->targets[i]; + if (strcmp(t->id, id) == 0) { + return t; + } + } + return NULL; +} + +struct TargetList *target_list_from_file(const char *filename) { + struct TargetList *list = target_list_new(TARGETLIST_INITIAL_COUNT); + if (!list) { + return NULL; + } + + FILE *fp = fopen(filename, "r"); + if (!fp) { + target_list_free(&list); + return NULL; + } + + char *line = calloc(BUFSIZ + 1, sizeof(char)); + if (!line) { + target_list_free(&list); + return NULL; + } + + size_t buffer_size = BUFSIZ; + struct Target *t = NULL; + size_t line_no = 0; + while (getline(&line, &buffer_size, fp) > 0) { + // Skip comments + if (line[0] == '#') { + line_no++; + continue; + } + + char *deps_delim = strchr(line, ':'); + const char *deps = deps_delim ? deps_delim + 1 : NULL; + char *var_delim = strchr(line, '='); + const int is_var = isalpha(line[0]) && var_delim != NULL; + const int is_target = isalnum(line[0]) && deps_delim != NULL; + const int is_script = isspace(line[0]); + const int is_include = strncmp(line, "include", strlen("include")) == 0; + + if (t && is_script) { + if (!t->script) { + t->script = calloc(BUFSIZ + 1, sizeof(char)); + if (!t->script) { + fprintf(stderr, "unable to allocate memory for script data\n"); + safe_free(line); + target_list_free(&list); + return NULL; + } + t->info.filename = strdup(filename); + if (!t->info.filename) { + fprintf(stderr, "unable to allocate memory for info.filename\n"); + safe_free(line); + target_list_free(&list); + return NULL; + } + } + char *buf_start = line; + for (size_t i = 0; i < strlen(line); i++) { + if (line[i] == '\t' && i < 4) { + continue; + } + if (isspace(line[i]) && i < 4) { + continue; + } + buf_start = &line[i]; + break; + } + strcat(t->script, buf_start); + } else if (is_target) { + t = target_new(); + if (!t) { + fprintf(stderr, "unable to allocate memory for new target\n"); + safe_free(line); + target_free(&t); + target_list_free(&list); + return NULL; + } + t->info.line_no = line_no; + t->info.line = strdup(line); + + if (!deps_delim) { + fprintf(stderr, "%s: line %lu: invalid target definition: %s\n", filename, t->info.line_no, line); + fprintf(stderr, ">>> %s\n", t->info.line); + safe_free(line); + target_free(&t); + target_list_free(&list); + return NULL; + } + *deps_delim = '\0'; + t->id = strdup(line); + if (deps) { + t->_tmp_deps = strdup(deps); + if (!t->_tmp_deps) { + fprintf(stderr, "unable to allocate memory for temporary dependency data\n"); + safe_free(line); + target_free(&t); + target_list_free(&list); + return NULL; + } + if (t->_tmp_deps[strlen(t->_tmp_deps) ? strlen(t->_tmp_deps) - 1 : 0] == '\n') { + t->_tmp_deps[strlen(t->_tmp_deps) ? strlen(t->_tmp_deps) - 1 : 0] = '\0'; + } + } + target_list_add(list, t); + } else if (is_var) { + printf("variable detected\n"); + size_t num_split = 0; + char **pair = split(line, "=", 1, &num_split); + if (num_split < 2) { + fprintf(stderr, "%s: line: %lu: invalid variable definition: %s\n", filename, line_no, line); + fprintf(stderr, ">>> %s\n", line); + safe_free(line); + target_free(&t); + target_list_free(&list); + return NULL; + } + safe_free_array(pair, num_split); + } else if (is_include) { + size_t num_split = 0; + char **pair = split(line, " ", 1, &num_split); + if (!pair) { + fprintf(stderr, "%s: line: %lu: invalid include definition: %s\n", filename, line_no, line); + fprintf(stderr, ">>> %s\n", line); + safe_free(line); + target_free(&t); + target_list_free(&list); + return NULL; + } + if (pair[1][strlen(pair[1]) ? strlen(pair[1]) - 1 : 0] == '\n') { + pair[1][strlen(pair[1]) ? strlen(pair[1]) - 1 : 0] = '\0'; + } + + printf("Including file: %s\n", pair[1]); + struct TargetList *included_targets = target_list_from_file(pair[1]); + if (included_targets) { + if (target_list_append(&list, included_targets)) { + fprintf(stderr, "unable to append to target list %p\n", list); + safe_free(line); + target_list_free(&list); + return NULL; + } + for (size_t i = 0; i < list->count_used; i++) { + included_targets->targets[i] = NULL; + } + free(included_targets->targets); + free(included_targets); + } else { + fprintf(stderr, "%s: line %lu: failed to include file: %s\n", filename, line_no, pair[1]); + safe_free(line); + safe_free_array(pair, num_split); + target_free(&t); + target_list_free(&list); + return NULL; + } + safe_free_array(pair, num_split); + } + line_no++; + } + + // Add dependencies + for (size_t i = 0; i < list->count_used; i++) { + if (!list->targets[i]->_tmp_deps || !strlen(list->targets[i]->_tmp_deps)) { + continue; + } + char *_tmp_deps_orig = list->targets[i]->_tmp_deps; + list->targets[i]->dependencies = target_list_new(TARGETLIST_INITIAL_COUNT); + const char *token = NULL; + while ((token = strsep(&list->targets[i]->_tmp_deps, " ")) != NULL) { + char *data = strdup(token); + if (!strlen(data)) { + safe_free(data); + continue; + } + + if (data[strlen(data) ? strlen(data) - 1 : 0] == '\n') { + data[strlen(data) ? strlen(data) - 1 : 0] = '\0'; + } + + const int target_exists = target_list_get(list, data) != NULL; + if (!target_exists) { + fprintf(stderr, "%s: line %lu: target not found: %s\n", filename, line_no, data); + fprintf(stderr, ">>> %s\n", list->targets[i]->info.line); + safe_free(data); + safe_free(line); + target_list_free(&list); + return NULL; + } + + for (size_t j = 0; j < list->count_used; j++) { + if (strcmp(list->targets[j]->id, data) == 0) { + if (list->targets[i]->id == list->targets[j]->id) { + fprintf(stderr, "%s: line %lu: target '%s' cannot target itself (circular dependency)\n", filename, list->targets[i]->info.line_no + 1, list->targets[i]->id); + fprintf(stderr, ">>> %s\n", list->targets[i]->info.line); + target_free(&t); + target_list_free(&list); + return NULL; + } + target_list_add(list->targets[i]->dependencies, list->targets[j]); + } + } + safe_free(data); + } + list->targets[i]->_tmp_deps = _tmp_deps_orig; + } + + safe_free(line); + fclose(fp); + return list; +} + +void target_list_show(const struct TargetList *list) { + for (size_t i = 0; i < list->count_used; i++) { + const struct Target *t = list->targets[i]; + printf("TARGET: %s\n", t->id); + printf("DEPENDENCIES: "); + if (t->dependencies && t->dependencies->count_used) { + printf("\n"); + for (size_t j = 0; j < t->dependencies->count_used; j++) { + printf(" - %s\n", t->dependencies->targets[j]->id); + } + } else { + printf("N/A\n"); + } + printf("SCRIPT: "); + if (t->script && strlen(t->script)) { + printf("\n%s\n", t->script); + } else { + printf("N/A\n"); + } + printf("\n"); + } +} + diff --git a/targetlist.h b/targetlist.h new file mode 100644 index 0000000..fdda2be --- /dev/null +++ b/targetlist.h @@ -0,0 +1,24 @@ +#ifndef DO_TARGETLIST_H +#define DO_TARGETLIST_H + +#include "target.h" + +#define TARGETLIST_INITIAL_COUNT 100 + +// Data structures +struct TargetList { + size_t count_used; + size_t count_alloc; + struct Target **targets; +}; + +// Prototypes +struct TargetList *target_list_new(size_t len); +struct TargetList *target_list_from_file(const char *filename); +int target_list_add(struct TargetList *list, struct Target *t); +int target_list_append(struct TargetList **dest, const struct TargetList *src); +struct Target *target_list_get(const struct TargetList *list, const char *id); +void target_list_show(const struct TargetList *list); +void target_list_free(struct TargetList **list); + +#endif //DO_TARGETLIST_H
\ No newline at end of file |
