aboutsummaryrefslogtreecommitdiffstats
path: root/src/blogc-make/rules.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/blogc-make/rules.c')
-rw-r--r--src/blogc-make/rules.c752
1 files changed, 752 insertions, 0 deletions
diff --git a/src/blogc-make/rules.c b/src/blogc-make/rules.c
new file mode 100644
index 0000000..80de6bb
--- /dev/null
+++ b/src/blogc-make/rules.c
@@ -0,0 +1,752 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <math.h>
+#include "../common/utils.h"
+#include "ctx.h"
+#include "exec.h"
+#include "exec-native.h"
+#include "rules.h"
+
+
+// INDEX RULE
+
+static bc_slist_t*
+index_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ const char *html_ext = bc_trie_lookup(ctx->settings->settings,
+ "html_ext");
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *index_prefix = bc_trie_lookup(ctx->settings->settings,
+ "index_prefix");
+ bool is_index = (index_prefix == NULL) && (html_ext[0] == '/');
+ char *f = bc_strdup_printf("%s%s%s%s", output_dir,
+ is_index ? "" : "/", is_index ? "" : index_prefix,
+ html_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ return rv;
+}
+
+static int
+index_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return 0;
+
+ int rv = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "FILTER_PER_PAGE",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "posts_per_page")));
+ bc_trie_insert(variables, "FILTER_PAGE", bc_strdup("1"));
+ bc_trie_insert(variables, "DATE_FORMAT",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "date_format")));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("index"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("post"));
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next) {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+ if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx,
+ ctx->main_template_fctx, fctx, false))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, true,
+ ctx->main_template_fctx, fctx, ctx->posts_fctx, verbose,
+ false);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// ATOM RULE
+
+static bc_slist_t*
+atom_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *atom_prefix = bc_trie_lookup(ctx->settings->settings,
+ "atom_prefix");
+ const char *atom_ext = bc_trie_lookup(ctx->settings->settings, "atom_ext");
+ char *f = bc_strdup_printf("%s/%s%s", output_dir, atom_prefix, atom_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ return rv;
+}
+
+static int
+atom_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return 0;
+
+ int rv = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "FILTER_PER_PAGE",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings,
+ "atom_posts_per_page")));
+ bc_trie_insert(variables, "FILTER_PAGE", bc_strdup("1"));
+ bc_trie_insert(variables, "DATE_FORMAT", bc_strdup("%Y-%m-%dT%H:%M:%SZ"));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("atom"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("atom"));
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next) {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+ if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx, NULL,
+ fctx, false))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, true,
+ ctx->atom_template_fctx, fctx, ctx->posts_fctx, verbose,
+ false);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// ATOM TAGS RULE
+
+static bc_slist_t*
+atom_tags_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *atom_prefix = bc_trie_lookup(ctx->settings->settings,
+ "atom_prefix");
+ const char *atom_ext = bc_trie_lookup(ctx->settings->settings, "atom_ext");
+ for (size_t i = 0; ctx->settings->tags[i] != NULL; i++) {
+ char *f = bc_strdup_printf("%s/%s/%s%s", output_dir, atom_prefix,
+ ctx->settings->tags[i], atom_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+atom_tags_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
+ return 0;
+
+ int rv = 0;
+ size_t i = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "FILTER_PER_PAGE",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings,
+ "atom_posts_per_page")));
+ bc_trie_insert(variables, "FILTER_PAGE", bc_strdup("1"));
+ bc_trie_insert(variables, "DATE_FORMAT", bc_strdup("%Y-%m-%dT%H:%M:%SZ"));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("atom_tags"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("atom"));
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next, i++) {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+
+ bc_trie_insert(variables, "FILTER_TAG",
+ bc_strdup(ctx->settings->tags[i]));
+
+ if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx, NULL,
+ fctx, false))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, true,
+ ctx->atom_template_fctx, fctx, ctx->posts_fctx, verbose,
+ false);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// PAGINATION RULE
+
+static bc_slist_t*
+pagination_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return NULL;
+
+ long num_posts = bc_slist_length(ctx->posts_fctx);
+ long posts_per_page = strtol(
+ bc_trie_lookup(ctx->settings->settings, "posts_per_page"),
+ NULL, 10); // FIXME: improve
+ size_t pages = ceilf(((float) num_posts) / posts_per_page);
+
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *pagination_prefix = bc_trie_lookup(ctx->settings->settings,
+ "pagination_prefix");
+ const char *html_ext = bc_trie_lookup(ctx->settings->settings,
+ "html_ext");
+
+ bc_slist_t *rv = NULL;
+ for (size_t i = 0; i < pages; i++) {
+ char *f = bc_strdup_printf("%s/%s/%d%s", output_dir, pagination_prefix,
+ i + 1, html_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+pagination_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return 0;
+
+ int rv = 0;
+ size_t page = 1;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "FILTER_PER_PAGE",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "posts_per_page")));
+ bc_trie_insert(variables, "DATE_FORMAT",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "date_format")));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("pagination"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("post"));
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next, page++) {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+ bc_trie_insert(variables, "FILTER_PAGE", bc_strdup_printf("%zu", page));
+ if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx,
+ ctx->main_template_fctx, fctx, false))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, true,
+ ctx->main_template_fctx, fctx, ctx->posts_fctx, verbose, false);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// POSTS RULE
+
+static bc_slist_t*
+posts_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return NULL;
+
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *post_prefix = bc_trie_lookup(ctx->settings->settings,
+ "post_prefix");
+ const char *html_ext = bc_trie_lookup(ctx->settings->settings,
+ "html_ext");
+
+ bc_slist_t *rv = NULL;
+ for (size_t i = 0; ctx->settings->posts[i] != NULL; i++) {
+ char *f = bc_strdup_printf("%s/%s/%s%s", output_dir, post_prefix,
+ ctx->settings->posts[i], html_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+posts_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL)
+ return 0;
+
+ int rv = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "IS_POST", bc_strdup("1"));
+ bc_trie_insert(variables, "DATE_FORMAT",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "date_format")));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("posts"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("post"));
+
+ bc_slist_t *s, *o;
+
+ for (s = ctx->posts_fctx, o = outputs; s != NULL && o != NULL;
+ s = s->next, o = o->next)
+ {
+ bm_filectx_t *o_fctx = o->data;
+ if (o_fctx == NULL)
+ continue;
+ if (bm_rule_need_rebuild(s, ctx->settings_fctx,
+ ctx->main_template_fctx, o_fctx, true))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, false,
+ ctx->main_template_fctx, o_fctx, s, verbose, true);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// TAGS RULE
+
+static bc_slist_t*
+tags_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *tag_prefix = bc_trie_lookup(ctx->settings->settings,
+ "tag_prefix");
+ const char *html_ext = bc_trie_lookup(ctx->settings->settings, "html_ext");
+ for (size_t i = 0; ctx->settings->tags[i] != NULL; i++) {
+ char *f = bc_strdup_printf("%s/%s/%s%s", output_dir, tag_prefix,
+ ctx->settings->tags[i], html_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+tags_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
+ return 0;
+
+ int rv = 0;
+ size_t i = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "FILTER_PER_PAGE",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings,
+ "atom_posts_per_page")));
+ bc_trie_insert(variables, "FILTER_PAGE", bc_strdup("1"));
+ bc_trie_insert(variables, "DATE_FORMAT",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "date_format")));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("tags"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("post"));
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next, i++) {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+
+ bc_trie_insert(variables, "FILTER_TAG",
+ bc_strdup(ctx->settings->tags[i]));
+
+ if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx,
+ ctx->main_template_fctx, fctx, false))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, true,
+ ctx->main_template_fctx, fctx, ctx->posts_fctx, verbose,
+ false);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// PAGES RULE
+
+static bc_slist_t*
+pages_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->pages == NULL)
+ return NULL;
+
+ const char *output_dir = bc_trie_lookup(ctx->settings->settings,
+ "output_dir");
+ const char *html_ext = bc_trie_lookup(ctx->settings->settings, "html_ext");
+
+ bc_slist_t *rv = NULL;
+ for (size_t i = 0; ctx->settings->pages[i] != NULL; i++) {
+ bool is_index = (0 == strcmp(ctx->settings->pages[i], "index"))
+ && (html_ext[0] == '/');
+ char *f = bc_strdup_printf("%s%s%s%s", output_dir,
+ is_index ? "" : "/", is_index ? "" : ctx->settings->pages[i],
+ html_ext);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+pages_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->pages == NULL)
+ return 0;
+
+ int rv = 0;
+
+ bc_trie_t *variables = bc_trie_new(free);
+ bc_trie_insert(variables, "DATE_FORMAT",
+ bc_strdup(bc_trie_lookup(ctx->settings->settings, "date_format")));
+ bc_trie_insert(variables, "BM_RULE", bc_strdup("pages"));
+ bc_trie_insert(variables, "BM_TYPE", bc_strdup("page"));
+
+ bc_slist_t *s, *o;
+
+ for (s = ctx->pages_fctx, o = outputs; s != NULL && o != NULL;
+ s = s->next, o = o->next)
+ {
+ bm_filectx_t *o_fctx = o->data;
+ if (o_fctx == NULL)
+ continue;
+ if (bm_rule_need_rebuild(s, ctx->settings_fctx,
+ ctx->main_template_fctx, o_fctx, true))
+ {
+ rv = bm_exec_blogc(ctx->settings, variables, false,
+ ctx->main_template_fctx, o_fctx, s, verbose, true);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ bc_trie_free(variables);
+
+ return rv;
+}
+
+
+// COPY FILES RULE
+
+static bc_slist_t*
+copy_files_outputlist(bm_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->settings->copy_files == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ const char *dir = bc_trie_lookup(ctx->settings->settings, "output_dir");
+ for (size_t i = 0; ctx->settings->copy_files[i] != NULL; i++) {
+ char *f = bc_strdup_printf("%s/%s", dir, ctx->settings->copy_files[i]);
+ rv = bc_slist_append(rv, bm_filectx_new(ctx, f));
+ free(f);
+ }
+ return rv;
+}
+
+static int
+copy_files_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ if (ctx == NULL || ctx->settings->copy_files == NULL)
+ return 0;
+
+ int rv = 0;
+
+ bc_slist_t *s, *o;
+
+ for (s = ctx->copy_files_fctx, o = outputs; s != NULL && o != NULL;
+ s = s->next, o = o->next)
+ {
+ bm_filectx_t *o_fctx = o->data;
+ if (o_fctx == NULL)
+ continue;
+
+ if (bm_rule_need_rebuild(s, ctx->settings_fctx, NULL, o_fctx, true)) {
+ rv = bm_exec_native_cp(s->data, o_fctx, verbose);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ return rv;
+}
+
+
+// CLEAN RULE
+
+static bc_slist_t*
+clean_outputlist(bm_ctx_t *ctx)
+{
+ return bm_rule_list_built_files(ctx);
+}
+
+static int
+clean_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bool verbose)
+{
+ int rv = 0;
+
+ for (bc_slist_t *l = outputs; l != NULL; l = l->next)
+ {
+ bm_filectx_t *fctx = l->data;
+ if (fctx == NULL)
+ continue;
+
+ if (fctx->readable) {
+ rv = bm_exec_native_rm(fctx, verbose);
+ if (rv != 0)
+ break;
+ }
+ }
+
+ return rv;
+}
+
+
+const bm_rule_t const rules[] = {
+ {
+ .name = "index",
+ .help = "build website index from posts",
+ .outputlist_func = index_outputlist,
+ .exec_func = index_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "atom",
+ .help = "build main atom feed from posts",
+ .outputlist_func = atom_outputlist,
+ .exec_func = atom_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "atom_tags",
+ .help = "build atom feeds for each tag from posts",
+ .outputlist_func = atom_tags_outputlist,
+ .exec_func = atom_tags_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "pagination",
+ .help = "build pagination pages from posts",
+ .outputlist_func = pagination_outputlist,
+ .exec_func = pagination_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "posts",
+ .help = "build individual pages for each post",
+ .outputlist_func = posts_outputlist,
+ .exec_func = posts_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "tags",
+ .help = "build post listings for each tag from posts",
+ .outputlist_func = tags_outputlist,
+ .exec_func = tags_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "pages",
+ .help = "build individual pages for each page",
+ .outputlist_func = pages_outputlist,
+ .exec_func = pages_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "copy_files",
+ .help = "copy static files from source directory to output directory",
+ .outputlist_func = copy_files_outputlist,
+ .exec_func = copy_files_exec,
+ .generate_files = true,
+ },
+ {
+ .name = "clean",
+ .help = "clean built files and empty directories in output directory",
+ .outputlist_func = clean_outputlist,
+ .exec_func = clean_exec,
+ .generate_files = false,
+ },
+ {NULL, NULL, NULL, NULL, false},
+};
+
+
+int
+bm_rule_executor(bm_ctx_t *ctx, bc_slist_t *rule_list, bool verbose)
+{
+ const bm_rule_t *rule = NULL;
+ int rv = 0;
+
+ for (bc_slist_t *l = rule_list; l != NULL; l = l->next) {
+ if (0 == strcmp("all", (char*) l->data)) {
+ bc_slist_t *s = NULL;
+ for (size_t i = 0; rules[i].name != NULL; i++) {
+ if (!rules[i].generate_files) {
+ continue;
+ }
+ s = bc_slist_append(s, bc_strdup(rules[i].name));
+ }
+ rv = bm_rule_executor(ctx, s, verbose);
+ bc_slist_free_full(s, free);
+ continue;
+ }
+ rule = NULL;
+ for (size_t i = 0; rules[i].name != NULL; i++) {
+ if (0 == strcmp((char*) l->data, rules[i].name)) {
+ rule = &(rules[i]);
+ rv = bm_rule_execute(ctx, rule, verbose);
+ if (rv != 0)
+ return rv;
+ }
+ }
+ if (rule == NULL) {
+ fprintf(stderr, "blogc-make: error: rule not found: %s\n",
+ (char*) l->data);
+ rv = 3;
+ }
+ }
+
+ return rv;
+}
+
+
+int
+bm_rule_execute(bm_ctx_t *ctx, const bm_rule_t *rule, bool verbose)
+{
+ if (rule == NULL)
+ return 3;
+
+ bc_slist_t *outputs = rule->outputlist_func(ctx);
+ int rv = rule->exec_func(ctx, outputs, verbose);
+
+ bc_slist_free_full(outputs, (bc_free_func_t) bm_filectx_free);
+
+ return rv;
+}
+
+
+bool
+bm_rule_need_rebuild(bc_slist_t *sources, bm_filectx_t *settings,
+ bm_filectx_t *template, bm_filectx_t *output, bool only_first_source)
+{
+ if (output == NULL || !output->readable)
+ return true;
+
+ bool rv = false;
+
+ bc_slist_t *s = NULL;
+ if (settings != NULL)
+ s = bc_slist_append(s, settings);
+ if (template != NULL)
+ s = bc_slist_append(s, template);
+
+ for (bc_slist_t *l = sources; l != NULL; l = l->next) {
+ s = bc_slist_append(s, l->data);
+ if (only_first_source)
+ break;
+ }
+
+ for (bc_slist_t *l = s; l != NULL; l = l->next) {
+ bm_filectx_t *source = l->data;
+ if (source == NULL || !source->readable) {
+ // this is unlikely to happen, but lets just say that we need
+ // a rebuild and let blogc bail out.
+ rv = true;
+ break;
+ }
+ if (source->timestamp.tv_sec == output->timestamp.tv_sec) {
+ if (source->timestamp.tv_nsec > output->timestamp.tv_nsec) {
+ rv = true;
+ break;
+ }
+ }
+ else if (source->timestamp.tv_sec > output->timestamp.tv_sec) {
+ rv = true;
+ break;
+ }
+ }
+
+ bc_slist_free(s);
+
+ return rv;
+}
+
+
+bc_slist_t*
+bm_rule_list_built_files(bm_ctx_t *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+
+ bc_slist_t *rv = NULL;
+ for (size_t i = 0; rules[i].name != NULL; i++) {
+ if (!rules[i].generate_files) {
+ continue;
+ }
+
+ bc_slist_t *o = rules[i].outputlist_func(ctx);
+ for (bc_slist_t *l = o; l != NULL; l = l->next) {
+ rv = bc_slist_append(rv, l->data);
+ }
+ bc_slist_free(o);
+ }
+ return rv;
+}
+
+
+void
+bm_rule_print_help(void)
+{
+ printf(
+ "\n"
+ "build rules:\n"
+ " all run all rules that generate output files\n");
+
+ for (size_t i = 0; rules[i].name != NULL; i++) {
+ printf(" %-12s %s\n", rules[i].name, rules[i].help);
+ }
+}