summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blogc-git-receiver.c501
-rw-r--r--src/blogc-git-receiver/main.c44
-rw-r--r--src/blogc-git-receiver/post-receive.c91
-rw-r--r--src/blogc-git-receiver/post-receive.h14
-rw-r--r--src/blogc-git-receiver/pre-receive-parser.c84
-rw-r--r--src/blogc-git-receiver/pre-receive-parser.h17
-rw-r--r--src/blogc-git-receiver/pre-receive.c318
-rw-r--r--src/blogc-git-receiver/pre-receive.h14
-rw-r--r--src/blogc-git-receiver/settings.c110
-rw-r--r--src/blogc-git-receiver/settings.h19
-rw-r--r--src/blogc-git-receiver/shell-command-parser.c101
-rw-r--r--src/blogc-git-receiver/shell-command-parser.h14
-rw-r--r--src/blogc-git-receiver/shell.c190
-rw-r--r--src/blogc-git-receiver/shell.h15
-rw-r--r--src/blogc-make/atom.c137
-rw-r--r--src/blogc-make/atom.h19
-rw-r--r--src/blogc-make/ctx.c409
-rw-r--r--src/blogc-make/ctx.h84
-rw-r--r--src/blogc-make/exec-native.c192
-rw-r--r--src/blogc-make/exec-native.h20
-rw-r--r--src/blogc-make/exec.c498
-rw-r--r--src/blogc-make/exec.h35
-rw-r--r--src/blogc-make/httpd.c121
-rw-r--r--src/blogc-make/httpd.h19
-rw-r--r--src/blogc-make/main.c134
-rw-r--r--src/blogc-make/reloader.c116
-rw-r--r--src/blogc-make/reloader.h20
-rw-r--r--src/blogc-make/rules.c1078
-rw-r--r--src/blogc-make/rules.h36
-rw-r--r--src/blogc-make/settings.c203
-rw-r--r--src/blogc-make/settings.h29
-rw-r--r--src/blogc-make/utils.c128
-rw-r--r--src/blogc-make/utils.h20
-rw-r--r--src/blogc-runserver.c386
-rw-r--r--src/blogc-runserver/httpd-utils.c105
-rw-r--r--src/blogc-runserver/httpd-utils.h19
-rw-r--r--src/blogc-runserver/httpd.c413
-rw-r--r--src/blogc-runserver/httpd.h15
-rw-r--r--src/blogc-runserver/main.c153
-rw-r--r--src/blogc-runserver/mime.c164
-rw-r--r--src/blogc-runserver/mime.h15
-rw-r--r--src/blogc.c297
-rw-r--r--src/blogc/content-parser.h25
-rw-r--r--src/blogc/debug.c80
-rw-r--r--src/blogc/filelist-parser.c67
-rw-r--r--src/blogc/filelist-parser.h16
-rw-r--r--src/blogc/funcvars.c62
-rw-r--r--src/blogc/funcvars.h19
-rw-r--r--src/blogc/loader.c318
-rw-r--r--src/blogc/loader.h22
-rw-r--r--src/blogc/main.c393
-rw-r--r--src/blogc/renderer.h24
-rw-r--r--src/blogc/rusage.c86
-rw-r--r--src/blogc/rusage.h36
-rw-r--r--src/blogc/sysinfo.c153
-rw-r--r--src/blogc/sysinfo.h38
-rw-r--r--src/blogc/template-parser.h58
-rw-r--r--src/blogc/toctree.c103
-rw-r--r--src/blogc/toctree.h26
-rw-r--r--src/common/compat.c39
-rw-r--r--src/common/compat.h14
-rw-r--r--src/common/config-parser.c442
-rw-r--r--src/common/config-parser.h31
-rw-r--r--src/common/error.h47
-rw-r--r--src/common/file.c66
-rw-r--r--src/common/file.h20
-rw-r--r--src/common/sort.c44
-rw-r--r--src/common/sort.h18
-rw-r--r--src/common/stdin.c28
-rw-r--r--src/common/stdin.h16
-rw-r--r--src/common/utils.h110
-rw-r--r--src/content-parser.c (renamed from src/blogc/content-parser.c)749
-rw-r--r--src/content-parser.h174
-rw-r--r--src/datetime-parser.c (renamed from src/blogc/datetime-parser.c)68
-rw-r--r--src/datetime-parser.h (renamed from src/blogc/datetime-parser.h)6
-rw-r--r--src/debug.c80
-rw-r--r--src/debug.h (renamed from src/blogc/debug.h)6
-rw-r--r--src/error.c (renamed from src/common/error.c)75
-rw-r--r--src/error.h34
-rw-r--r--src/file.c81
-rw-r--r--src/file.h21
-rw-r--r--src/loader.c212
-rw-r--r--src/loader.h21
-rw-r--r--src/renderer.c (renamed from src/blogc/renderer.c)309
-rw-r--r--src/renderer.h24
-rw-r--r--src/source-parser.c (renamed from src/blogc/source-parser.c)106
-rw-r--r--src/source-parser.h (renamed from src/blogc/source-parser.h)10
-rw-r--r--src/template-parser.c (renamed from src/blogc/template-parser.c)249
-rw-r--r--src/template-parser.h53
-rw-r--r--src/utf8.c (renamed from src/common/utf8.c)26
-rw-r--r--src/utf8.h (renamed from src/common/utf8.h)8
-rw-r--r--src/utils.c (renamed from src/common/utils.c)300
-rw-r--r--src/utils.h102
93 files changed, 2926 insertions, 8586 deletions
diff --git a/src/blogc-git-receiver.c b/src/blogc-git-receiver.c
new file mode 100644
index 0000000..820fce5
--- /dev/null
+++ b/src/blogc-git-receiver.c
@@ -0,0 +1,501 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-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 <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+
+#include "utils.h"
+
+#ifndef BUFFER_SIZE
+#define BUFFER_SIZE 4096
+#endif
+
+
+static unsigned int
+cpu_count(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ long num = sysconf(_SC_NPROCESSORS_ONLN);
+ if (num >= 1)
+ return (unsigned int) num;
+#endif
+ return 1;
+}
+
+
+static void
+rmdir_recursive(const char *dir)
+{
+ struct stat buf;
+ if (0 != stat(dir, &buf)) {
+ fprintf(stderr, "warning: failed to remove directory (%s): %s\n", dir,
+ strerror(errno));
+ return;
+ }
+ if (!S_ISDIR(buf.st_mode)) {
+ fprintf(stderr, "error: trying to remove invalid directory: %s\n", dir);
+ exit(2);
+ }
+ DIR *d = opendir(dir);
+ if (d == NULL) {
+ fprintf(stderr, "error: failed to open directory: %s\n",
+ strerror(errno));
+ exit(2);
+ }
+ struct dirent *e;
+ while (NULL != (e = readdir(d))) {
+ if ((0 == strcmp(e->d_name, ".")) || (0 == strcmp(e->d_name, "..")))
+ continue;
+ char *f = sb_strdup_printf("%s/%s", dir, e->d_name);
+ if (0 != stat(f, &buf)) {
+ fprintf(stderr, "error: failed to stat directory entry (%s): %s\n",
+ e->d_name, strerror(errno));
+ free(f);
+ exit(2);
+ }
+ if (S_ISDIR(buf.st_mode)) {
+ rmdir_recursive(f);
+ }
+ else if (0 != unlink(f)) {
+ fprintf(stderr, "error: failed to remove file (%s): %s\n", f,
+ strerror(errno));
+ free(f);
+ exit(2);
+ }
+ free(f);
+ }
+ if (0 != closedir(d)) {
+ fprintf(stderr, "error: failed to close directory: %s\n",
+ strerror(errno));
+ exit(2);
+ }
+ if (0 != rmdir(dir)) {
+ fprintf(stderr, "error: failed to remove directory: %s\n",
+ strerror(errno));
+ exit(2);
+ }
+}
+
+
+static int
+git_shell(int argc, char *argv[])
+{
+ int rv = 0;
+
+ char *repo = NULL;
+ char *command_orig = NULL;
+ char *command_name = NULL;
+ char command_new[BUFFER_SIZE];
+
+ bool exec_git = false;
+
+ // validate git command
+ size_t len = strlen(argv[2]);
+ if (!((len > 17 && (0 == strncmp(argv[2], "git-receive-pack ", 17))) ||
+ (len > 16 && (0 == strncmp(argv[2], "git-upload-pack ", 16))) ||
+ (len > 19 && (0 == strncmp(argv[2], "git-upload-archive ", 19)))))
+ {
+ fprintf(stderr, "error: unsupported git command: %s\n", argv[2]);
+ rv = 1;
+ goto cleanup;
+ }
+
+ // get shell path
+ char *self = getenv("SHELL");
+ if (self == NULL) {
+ fprintf(stderr, "error: failed to find blogc-git-receiver path\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ // get home path
+ char *home = getenv("HOME");
+ if (home == NULL) {
+ fprintf(stderr, "error: failed to find user home path\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ // get git repository
+ command_orig = sb_strdup(argv[2]);
+ char *p, *r;
+ for (p = command_orig; *p != ' ' && *p != '\0'; p++);
+ if (*p == ' ')
+ p++;
+ if (*p == '\'' || *p == '"')
+ p++;
+ if (*p == '/')
+ p++;
+ for (r = p; *p != '\'' && *p != '"' && *p != '\0'; p++);
+ if (*p == '\'' || *p == '"')
+ *p = '\0';
+ if (*--p == '/')
+ *p = '\0';
+
+ repo = sb_strdup_printf("repos/%s", r);
+
+ // check if repository is sane
+ if (0 == strlen(repo)) {
+ fprintf(stderr, "error: invalid repository\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 == strncmp(argv[2], "git-upload-", 11)) // no need to check len here
+ goto git_exec;
+
+ if (0 != chdir(home)) {
+ fprintf(stderr, "error: failed to chdir (%s): %s\n", home,
+ strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 != access(repo, F_OK)) {
+ char *git_init_cmd = sb_strdup_printf(
+ "git init --bare \"%s\" > /dev/null", repo);
+ if (0 != system(git_init_cmd)) {
+ fprintf(stderr, "error: failed to create git repository: %s\n",
+ repo);
+ rv = 1;
+ free(git_init_cmd);
+ goto cleanup;
+ }
+ free(git_init_cmd);
+ }
+
+ if (0 != chdir(repo)) {
+ fprintf(stderr, "error: failed to chdir (%s/%s): %s\n", home, repo,
+ strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 != access("hooks", F_OK)) {
+ // openwrt git package won't install git templates, then the git
+ // repositories created with it won't have the hooks/ directory.
+ if (0 != mkdir("hooks", 0777)) { // mkdir honors umask for us.
+ fprintf(stderr, "error: failed to create directory (%s/%s/hooks): "
+ "%s\n", home, repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+ }
+
+ if (0 != chdir("hooks")) {
+ fprintf(stderr, "error: failed to chdir (%s/%s/hooks): %s\n", home,
+ repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 == access("pre-receive", F_OK)) {
+ if (0 != unlink("pre-receive")) {
+ fprintf(stderr, "error: failed to remove old symlink "
+ "(%s/%s/hooks/pre-receive): %s\n", home, repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+ }
+
+ if (0 != symlink(self, "pre-receive")) {
+ fprintf(stderr, "error: failed to create symlink "
+ "(%s/%s/hooks/pre-receive): %s\n", home, repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 == access("post-receive", F_OK)) {
+ if (0 != unlink("post-receive")) {
+ fprintf(stderr, "error: failed to remove old symlink "
+ "(%s/%s/hooks/post-receive): %s\n", home, repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+ }
+
+ if (0 != symlink(self, "post-receive")) {
+ fprintf(stderr, "error: failed to create symlink "
+ "(%s/%s/hooks/post-receive): %s\n", home, repo, strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (0 != chdir(home)) {
+ fprintf(stderr, "error: failed to chdir (%s): %s\n", home,
+ strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+git_exec:
+ command_name = sb_strdup(argv[2]);
+ for (p = command_name; *p != ' ' && *p != '\0'; p++);
+ if (*p == ' ')
+ *p = '\0';
+
+ if (BUFFER_SIZE < (strlen(command_name) + strlen(repo) + 4)) {
+ fprintf(stderr, "error: git-shell command is too big\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (snprintf(command_new, BUFFER_SIZE, "%s '%s'", command_name, repo))
+ exec_git = true;
+
+cleanup:
+ free(repo);
+ free(command_orig);
+ free(command_name);
+
+ if (exec_git) {
+ execlp("git-shell", "git-shell", "-c", command_new, NULL);
+
+ // execlp only returns on error, then something bad happened
+ fprintf(stderr, "error: failed to execute git-shell\n");
+ rv = 1;
+ }
+
+ return rv;
+}
+
+
+static int
+git_post_receive_hook(int argc, char *argv[])
+{
+ if (0 != system("git config --local remote.mirror.pushurl &> /dev/null")) {
+ if (0 != system("git config --local remote.mirror.url &> /dev/null")) {
+ fprintf(stderr, "warning: repository mirroring disabled\n");
+ return 0;
+ }
+ }
+
+ // at this point we know that we have a remote called mirror, we can just
+ // push to it.
+ if (0 != system("git push --mirror mirror"))
+ fprintf(stderr, "warning: failed push to git mirror\n");
+
+ return 0;
+}
+
+
+typedef enum {
+ START_OLD = 1,
+ OLD,
+ START_NEW,
+ NEW,
+ START_REF,
+ REF
+} input_state_t;
+
+
+static int
+git_pre_receive_hook(int argc, char *argv[])
+{
+ int c;
+ char buffer[BUFFER_SIZE];
+
+ input_state_t state = START_OLD;
+ size_t i = 0;
+ size_t start = 0;
+
+ int rv = 0;
+ char *new = NULL;
+ char *master = NULL;
+
+ while (EOF != (c = getc(stdin))) {
+
+ buffer[i] = (char) c;
+
+ switch (state) {
+ case START_OLD:
+ start = i;
+ state = OLD;
+ break;
+ case OLD:
+ if (c != ' ')
+ break;
+ // no need to store old
+ state = START_NEW;
+ break;
+ case START_NEW:
+ start = i;
+ state = NEW;
+ break;
+ case NEW:
+ if (c != ' ')
+ break;
+ state = START_REF;
+ new = strndup(buffer + start, i - start);
+ break;
+ case START_REF:
+ start = i;
+ state = REF;
+ break;
+ case REF:
+ if (c != '\n')
+ break;
+ state = START_OLD;
+ // we just care about a ref (refs/heads/master), everything
+ // else is disposable :)
+ if (!((i - start == 17) &&
+ (0 == strncmp("refs/heads/master", buffer + start, 17))))
+ {
+ free(new);
+ new = NULL;
+ break;
+ }
+ master = new;
+ break;
+ }
+
+ if (++i >= BUFFER_SIZE) {
+ fprintf(stderr, "error: pre-receive hook payload is too big.\n");
+ rv = 1;
+ goto cleanup2;
+ }
+ }
+
+ if (master == NULL) {
+ fprintf(stderr, "warning: no reference to master branch found. "
+ "nothing to deploy.\n");
+ goto cleanup2;
+ }
+
+ char *repo_dir = NULL;
+ char *output_dir = NULL;
+
+ if (NULL == getcwd(buffer, BUFFER_SIZE)) {
+ fprintf(stderr, "error: failed to get repository remote path: %s\n",
+ strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ repo_dir = sb_strdup(buffer);
+
+ char dir[] = "/tmp/blogc_XXXXXX";
+ if (NULL == mkdtemp(dir)) {
+ rv = 1;
+ goto cleanup;
+ }
+
+ char *git_archive_cmd = sb_strdup_printf(
+ "git archive \"%s\" | tar -x -C \"%s\" -f -", master, dir);
+ if (0 != system(git_archive_cmd)) {
+ fprintf(stderr, "error: failed to extract git content to temporary "
+ "directory: %s\n", dir);
+ rv = 1;
+ free(git_archive_cmd);
+ goto cleanup;
+ }
+ free(git_archive_cmd);
+
+ if (0 != chdir(dir)) {
+ fprintf(stderr, "error: failed to chdir (%s): %s\n", dir,
+ strerror(errno));
+ rv = 1;
+ goto cleanup;
+ }
+
+ if ((0 != access("Makefile", F_OK)) && (0 != access("GNUMakefile", F_OK))) {
+ fprintf(stderr, "warning: no makefile found. skipping ...\n");
+ goto cleanup;
+ }
+
+ char *home = getenv("HOME");
+ if (home == NULL) {
+ fprintf(stderr, "error: failed to find user home path\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ unsigned long epoch = time(NULL);
+ output_dir = sb_strdup_printf("%s/builds/%s-%lu", home, master, epoch);
+ char *gmake_cmd = sb_strdup_printf(
+ "gmake -j%d OUTPUT_DIR=\"%s\" BLOGC_GIT_RECEIVER=1",
+ cpu_count(), output_dir);
+ fprintf(stdout, "running command: %s\n\n", gmake_cmd);
+ fflush(stdout);
+ if (0 != system(gmake_cmd)) {
+ fprintf(stderr, "error: failed to build website ...\n");
+ rmdir_recursive(output_dir);
+ free(gmake_cmd);
+ rv = 1;
+ goto cleanup;
+ }
+ free(gmake_cmd);
+
+ if (0 != chdir(repo_dir)) {
+ fprintf(stderr, "error: failed to chdir (%s): %s\n", repo_dir,
+ strerror(errno));
+ rmdir_recursive(output_dir);
+ rv = 1;
+ goto cleanup;
+ }
+
+ char *htdocs_sym = NULL;
+ ssize_t htdocs_sym_len = readlink("htdocs", buffer, BUFFER_SIZE);
+ if (0 < htdocs_sym_len) {
+ if (0 != unlink("htdocs")) {
+ fprintf(stderr, "error: failed to remove symlink (%s/htdocs): %s\n",
+ repo_dir, strerror(errno));
+ rmdir_recursive(output_dir);
+ rv = 1;
+ goto cleanup;
+ }
+ buffer[htdocs_sym_len] = '\0';
+ htdocs_sym = buffer;
+ }
+
+ if (0 != symlink(output_dir, "htdocs")) {
+ fprintf(stderr, "error: failed to create symlink (%s/htdocs): %s\n",
+ repo_dir, strerror(errno));
+ rmdir_recursive(output_dir);
+ rv = 1;
+ goto cleanup;
+ }
+
+ if (htdocs_sym != NULL)
+ rmdir_recursive(htdocs_sym);
+
+cleanup:
+ free(output_dir);
+ rmdir_recursive(dir);
+ free(repo_dir);
+cleanup2:
+ free(new);
+ return rv;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ if (argc > 0) {
+ if (0 == strcmp(basename(argv[0]), "pre-receive"))
+ return git_pre_receive_hook(argc, argv);
+ if (0 == strcmp(basename(argv[0]), "post-receive"))
+ return git_post_receive_hook(argc, argv);
+ }
+
+ if (argc == 3 && (0 == strcmp(argv[1], "-c")))
+ return git_shell(argc, argv);
+
+ fprintf(stderr, "error: this is a special shell, go away!\n");
+ return 1;
+}
diff --git a/src/blogc-git-receiver/main.c b/src/blogc-git-receiver/main.c
deleted file mode 100644
index 13218dd..0000000
--- a/src/blogc-git-receiver/main.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libgen.h>
-#include "shell.h"
-#include "pre-receive.h"
-#include "post-receive.h"
-
-
-int
-main(int argc, char *argv[])
-{
- if (argc > 0) {
- if (0 == strcmp(basename(argv[0]), "pre-receive"))
- return bgr_pre_receive_hook(argc, argv);
- if (0 == strcmp(basename(argv[0]), "post-receive"))
- return bgr_post_receive_hook(argc, argv);
- }
-
- if (argc == 3 && (0 == strcmp(argv[1], "-c"))) {
- return bgr_shell(argc, argv);
- }
-
- // this is a hack to make blogc-git-receiver work out-of-the-box as a
- // `command=` in authorized_keys file. it will only work if the command
- // path is absolute.
- char *ssh_orig = getenv("SSH_ORIGINAL_COMMAND");
- if (argc == 1 && ssh_orig != NULL && argv[0][0] == '/') {
- setenv("SHELL", argv[0], 1);
- char* _argv[] = {argv[0], "-c", ssh_orig};
- return bgr_shell(3, _argv);
- }
-
- fprintf(stderr, "error: this is a special shell, go away!\n");
- return 1;
-}
diff --git a/src/blogc-git-receiver/post-receive.c b/src/blogc-git-receiver/post-receive.c
deleted file mode 100644
index 17a8aa7..0000000
--- a/src/blogc-git-receiver/post-receive.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include "../common/utils.h"
-#include "../common/config-parser.h"
-#include "settings.h"
-#include "post-receive.h"
-
-
-int
-bgr_post_receive_hook(int argc, char *argv[])
-{
- int rv = 0;
- char *mirror = NULL;
-
- char *hooks_dir = dirname(argv[0]); // this was validated by main()
- char *real_hooks_dir = realpath(hooks_dir, NULL);
- if (real_hooks_dir == NULL) {
- fprintf(stderr, "error: failed to guess repository root: %s\n",
- strerror(errno));
- return 1;
- }
-
- char *repo_path = bc_strdup(dirname(real_hooks_dir));
- free(real_hooks_dir);
- if (0 != chdir(repo_path)) {
- fprintf(stderr, "error: failed to change to repository root\n");
- rv = 1;
- goto cleanup;
- }
-
- // local repository settings should take precedence, so if the repo have
- // the 'mirror' remote, just push to it.
- // this will be removed at some point, but will be kept for compatibility
- // with old setups.
- if ((0 == system("git config --local remote.mirror.pushurl > /dev/null")) ||
- (0 == system("git config --local remote.mirror.url > /dev/null")))
- {
- mirror = bc_strdup("mirror");
- goto push;
- }
-
- bc_config_t *config = bgr_settings_parse();
- if (config == NULL) {
- fprintf(stderr, "warning: repository mirroring disabled\n");
- goto cleanup;
- }
-
- char *section = bgr_settings_get_section(config, repo_path);
- if (section == NULL) {
- fprintf(stderr, "warning: repository mirroring disabled\n");
- bc_config_free(config);
- goto cleanup;
- }
-
- mirror = bc_strdup(bc_config_get(config, section, "mirror"));
- free(section);
- bc_config_free(config);
-
- if (mirror == NULL) {
- fprintf(stderr, "warning: repository mirroring disabled\n");
- goto cleanup;
- }
-
-push:
-
- {
- char *git_cmd = bc_strdup_printf("git push --mirror %s", mirror);
- if (0 != system(git_cmd))
- fprintf(stderr, "warning: failed push to git mirror\n");
- free(git_cmd);
- }
-
- free(mirror);
-
-cleanup:
- free(repo_path);
-
- return rv;
-}
diff --git a/src/blogc-git-receiver/post-receive.h b/src/blogc-git-receiver/post-receive.h
deleted file mode 100644
index fc56dc3..0000000
--- a/src/blogc-git-receiver/post-receive.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _POST_RECEIVE_H
-#define _POST_RECEIVE_H
-
-int bgr_post_receive_hook(int argc, char *argv[]);
-
-#endif /* _POST_RECEIVE_H */
diff --git a/src/blogc-git-receiver/pre-receive-parser.c b/src/blogc-git-receiver/pre-receive-parser.c
deleted file mode 100644
index 61a533c..0000000
--- a/src/blogc-git-receiver/pre-receive-parser.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include "../common/utils.h"
-#include "pre-receive-parser.h"
-
-typedef enum {
- START_OLD = 1,
- OLD,
- START_NEW,
- NEW,
- START_REF,
- REF
-} input_state_t;
-
-
-bc_trie_t*
-bgr_pre_receive_parse(const char *input, size_t input_len)
-{
- input_state_t state = START_OLD;
- size_t start = 0;
- size_t start_new = 0;
-
- bc_trie_t* rv = bc_trie_new(free);
-
- for (size_t current = 0; current < input_len; current++) {
-
- char c = input[current];
-
- switch (state) {
- case START_OLD:
- start = current;
- state = OLD;
- break;
- case OLD:
- if (c != ' ')
- break;
- // no need to store old
- state = START_NEW;
- break;
- case START_NEW:
- start = current;
- state = NEW;
- break;
- case NEW:
- if (c != ' ')
- break;
- state = START_REF;
- start_new = start;
- break;
- case START_REF:
- start = current;
- state = REF;
- break;
- case REF:
- if (c != '\n')
- break;
- state = START_OLD;
- if ((current - start > 11) &&
- (0 == strncmp("refs/heads/", input + start, 11)))
- {
- char *key = bc_strndup(input + start + 11, current - start - 11);
- bc_trie_insert(rv, key, bc_strndup(input + start_new, start - 1 - start_new));
- free(key);
- }
- break;
- }
- }
-
- if (bc_trie_size(rv) == 0) {
- bc_trie_free(rv);
- return NULL;
- }
-
- return rv;
-}
diff --git a/src/blogc-git-receiver/pre-receive-parser.h b/src/blogc-git-receiver/pre-receive-parser.h
deleted file mode 100644
index 45a9da8..0000000
--- a/src/blogc-git-receiver/pre-receive-parser.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _PRE_RECEIVE_PARSER_H
-#define _PRE_RECEIVE_PARSER_H
-
-#include <stddef.h>
-#include "../common/utils.h"
-
-bc_trie_t* bgr_pre_receive_parse(const char *input, size_t input_len);
-
-#endif /* _PRE_RECEIVE_PARSER_H */
diff --git a/src/blogc-git-receiver/pre-receive.c b/src/blogc-git-receiver/pre-receive.c
deleted file mode 100644
index 12d29cf..0000000
--- a/src/blogc-git-receiver/pre-receive.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dirent.h>
-#include <time.h>
-#include <libgen.h>
-#include "../common/compat.h"
-#include "../common/utils.h"
-#include "../common/stdin.h"
-#include "settings.h"
-#include "pre-receive-parser.h"
-#include "pre-receive.h"
-
-
-static size_t
-cpu_count(void)
-{
-#ifdef _SC_NPROCESSORS_ONLN
- long num = sysconf(_SC_NPROCESSORS_ONLN);
- if (num >= 1)
- return (size_t) num;
-#endif
- return 1;
-}
-
-
-static void
-rmdir_recursive(const char *dir)
-{
- if (dir == NULL)
- return;
- struct stat buf;
- if (0 != stat(dir, &buf)) {
- return;
- }
- if (!S_ISDIR(buf.st_mode)) {
- fprintf(stderr, "error: trying to remove invalid directory: %s\n", dir);
- exit(2);
- }
- DIR *d = opendir(dir);
- if (d == NULL) {
- fprintf(stderr, "error: failed to open directory: %s\n",
- strerror(errno));
- exit(2);
- }
- struct dirent *e;
- while (NULL != (e = readdir(d))) {
- if ((0 == strcmp(e->d_name, ".")) || (0 == strcmp(e->d_name, "..")))
- continue;
- char *f = bc_strdup_printf("%s/%s", dir, e->d_name);
- if (0 != stat(f, &buf)) {
- fprintf(stderr, "error: failed to stat directory entry (%s): %s\n",
- e->d_name, strerror(errno));
- free(f);
- exit(2);
- }
- if (S_ISDIR(buf.st_mode)) {
- rmdir_recursive(f);
- }
- else if (0 != unlink(f)) {
- fprintf(stderr, "error: failed to remove file (%s): %s\n", f,
- strerror(errno));
- free(f);
- exit(2);
- }
- free(f);
- }
- if (0 != closedir(d)) {
- fprintf(stderr, "error: failed to close directory: %s\n",
- strerror(errno));
- exit(2);
- }
- if (0 != rmdir(dir)) {
- fprintf(stderr, "error: failed to remove directory: %s\n",
- strerror(errno));
- exit(2);
- }
-}
-
-
-int
-bgr_pre_receive_hook(int argc, char *argv[])
-{
- int rv = 0;
- char *ref = NULL;
- char *output_dir = NULL;
- char *tmpdir = NULL;
- char *sym = NULL;
-
- char *hooks_dir = dirname(argv[0]); // this was validated by main()
- char *real_hooks_dir = realpath(hooks_dir, NULL);
- if (real_hooks_dir == NULL) {
- fprintf(stderr, "error: failed to guess repository root: %s\n",
- strerror(errno));
- return 1;
- }
-
- char *repo_dir = bc_strdup(dirname(real_hooks_dir));
- free(real_hooks_dir);
- if (0 != chdir(repo_dir)) {
- fprintf(stderr, "error: failed to change to repository root\n");
- rv = 1;
- goto cleanup;
- }
-
- bc_config_t *config = bgr_settings_parse();
- if (config == NULL) {
- goto default_sym;
- }
-
- char *section = bgr_settings_get_section(config, repo_dir);
- if (section == NULL) {
- bc_config_free(config);
- goto default_sym;
- }
-
- const char *sym_tmp = bc_config_get(config, section, "symlink");
- if (sym_tmp == NULL) {
- free(section);
- bc_config_free(config);
- goto default_sym;
- }
-
- sym = bc_str_starts_with(sym_tmp, "/") ? bc_strdup(sym_tmp) :
- bc_strdup_printf("%s/%s", repo_dir, sym_tmp);
- free(section);
- bc_config_free(config);
-
-default_sym:
-
- if (sym == NULL) {
- sym = bc_strdup_printf("%s/htdocs", repo_dir);
- }
-
- if (NULL == getenv("GIT_DIR")) {
- if (0 != access(sym, R_OK)) {
- fprintf(stderr, "error: no previous build found. nothing to "
- "rebuild.\n");
- rv = 1;
- goto cleanup;
- }
- char *build_dir = realpath(sym, NULL);
- if (build_dir == NULL) {
- fprintf(stderr, "error: failed to get the hash of last built "
- "commit: %s\n", strerror(errno));
- rv = 1;
- goto cleanup;
- }
- char **pieces = bc_str_split(basename(build_dir), '-', 2);
- free(build_dir);
- if (bc_strv_length(pieces) != 2) {
- fprintf(stderr, "error: failed to parse the hash of last built "
- "commit.\n");
- bc_strv_free(pieces);
- rv = 1;
- goto cleanup;
- }
- ref = bc_strdup(pieces[0]);
- bc_strv_free(pieces);
- }
- else {
- size_t input_len;
- char *input = bc_stdin_read(&input_len);
- bc_trie_t *branches = bgr_pre_receive_parse(input, input_len);
-
- // try 'master' by default to avoid breaking existing setups
- ref = bc_strdup(bc_trie_lookup(branches, "master"));
- if (ref == NULL) {
- // try 'main'
- ref = bc_strdup(bc_trie_lookup(branches, "main"));
- }
-
- bc_trie_free(branches);
- free(input);
- }
-
- if (ref == NULL) {
- fprintf(stderr, "warning: no suitable branch found. "
- "nothing to deploy.\n");
- goto cleanup;
- }
-
- char dir[] = "/tmp/blogc_XXXXXX";
- if (NULL == mkdtemp(dir)) {
- rv = 1;
- goto cleanup;
- }
- tmpdir = dir;
-
- char *git_archive_cmd = bc_strdup_printf(
- "git archive \"%s\" | tar -x -C \"%s\" -f -", ref, tmpdir);
- if (0 != system(git_archive_cmd)) {
- fprintf(stderr, "error: failed to extract git content to temporary "
- "directory: %s\n", tmpdir);
- rv = 1;
- free(git_archive_cmd);
- goto cleanup;
- }
- free(git_archive_cmd);
-
- if (0 != chdir(tmpdir)) {
- fprintf(stderr, "error: failed to chdir (%s): %s\n", tmpdir,
- strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- char *buildsd = bgr_settings_get_builds_dir();
- if (buildsd == NULL) {
- fprintf(stderr, "error: failed to find builds directory path\n");
- rv = 1;
- goto cleanup;
- }
-
- unsigned long epoch = time(NULL);
- output_dir = bc_strdup_printf("%s/%s-%lu", buildsd, ref, epoch);
- free(buildsd);
-
- if (0 == access(output_dir, F_OK)) {
- char *tmp = output_dir;
- output_dir = bc_strdup_printf("%s-", tmp);
- free(tmp);
- }
-
- // detect if we will run blogc-make, make or nothing, and generate the
- // command.
- char *build_cmd = NULL;
- if (0 == access("blogcfile", F_OK)) {
- int status_bmake = system("blogc-make -v 2> /dev/null > /dev/null");
- if (127 == bc_compat_status_code(status_bmake)) {
- fprintf(stderr, "error: failed to find blogc-make binary\n");
- rv = 1;
- goto cleanup;
- }
- build_cmd = bc_strdup_printf("OUTPUT_DIR=\"%s\" blogc-make -V all",
- output_dir);
- }
- else if ((0 == access("Makefile", F_OK)) || (0 == access("GNUMakefile", F_OK))) {
- const char *make_impl = NULL;
-
- int status_gmake = system("gmake -f /dev/null 2> /dev/null > /dev/null");
- if (127 != bc_compat_status_code(status_gmake)) {
- make_impl = "gmake";
- }
- else {
- int status_make = system("make -f /dev/null 2> /dev/null > /dev/null");
- if (127 != bc_compat_status_code(status_make)) {
- make_impl = "make";
- }
- }
-
- if (make_impl == NULL) {
- fprintf(stderr, "error: no 'make' implementation found\n");
- rv = 1;
- goto cleanup;
- }
- build_cmd = bc_strdup_printf(
- "%s -j%d OUTPUT_DIR=\"%s\" BLOGC_GIT_RECEIVER=1", make_impl,
- cpu_count(), output_dir);
- }
- else {
- fprintf(stderr, "warning: no blogcfile or Makefile found. skipping ...\n");
- goto cleanup;
- }
-
- fprintf(stdout, "running command: %s\n\n", build_cmd);
- fflush(stdout);
- if (0 != system(build_cmd)) {
- fprintf(stderr, "error: failed to build website ...\n");
- rmdir_recursive(output_dir);
- free(build_cmd);
- rv = 1;
- goto cleanup;
- }
- free(build_cmd);
-
- char *htdocs_sym = realpath(sym, NULL);
- if ((htdocs_sym != NULL) && (0 != unlink(sym))) {
- fprintf(stderr, "error: failed to remove symlink (%s): %s\n", sym,
- strerror(errno));
- rmdir_recursive(output_dir);
- rv = 1;
- goto cleanup2;
- }
-
- if (0 != symlink(output_dir, sym)) {
- fprintf(stderr, "error: failed to create symlink (%s): %s\n", sym,
- strerror(errno));
- rmdir_recursive(output_dir);
- rv = 1;
- goto cleanup2;
- }
-
- if (htdocs_sym != NULL)
- rmdir_recursive(htdocs_sym);
-
-cleanup2:
- free(htdocs_sym);
-
-cleanup:
- free(sym);
- free(ref);
- free(output_dir);
- rmdir_recursive(tmpdir);
- free(repo_dir);
- return rv;
-}
diff --git a/src/blogc-git-receiver/pre-receive.h b/src/blogc-git-receiver/pre-receive.h
deleted file mode 100644
index ab19c47..0000000
--- a/src/blogc-git-receiver/pre-receive.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _PRE_RECEIVE_H
-#define _PRE_RECEIVE_H
-
-int bgr_pre_receive_hook(int argc, char *argv[]);
-
-#endif /* _PRE_RECEIVE_H */
diff --git a/src/blogc-git-receiver/settings.c b/src/blogc-git-receiver/settings.c
deleted file mode 100644
index db29b18..0000000
--- a/src/blogc-git-receiver/settings.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdio.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include "../common/utils.h"
-#include "../common/config-parser.h"
-#include "../common/error.h"
-#include "../common/file.h"
-#include "settings.h"
-
-
-const char*
-bgr_settings_get_base_dir(void)
-{
- char *rv = getenv("BLOGC_GIT_RECEIVER_BASE_DIR");
- if (rv != NULL) {
- return rv;
- }
- return getenv("HOME");
-}
-
-
-char*
-bgr_settings_get_builds_dir(void)
-{
- char *rv = getenv("BLOGC_GIT_RECEIVER_BUILDS_DIR");
- if (rv != NULL) {
- return bc_strdup(rv);
- }
- return bc_strdup_printf("%s/builds", bgr_settings_get_base_dir());
-}
-
-
-char*
-bgr_settings_get_section(bc_config_t *config, const char *repo_path)
-{
- const char *bd = bgr_settings_get_base_dir();
- if (bd == NULL) {
- return NULL;
- }
- char *rv = NULL;
- char** sections = bc_config_list_sections(config);
- for (size_t i = 0; sections[i] != NULL; i++) {
- if (bc_str_starts_with(sections[i], "repo:")) {
- char *tmp_repo = bc_strdup_printf("%s/repos/%s", bd, sections[i] + 5);
- char *real_tmp_repo = realpath(tmp_repo, NULL); // maybe not needed
- free(tmp_repo);
- if (real_tmp_repo == NULL)
- continue;
- if (0 == strcmp(real_tmp_repo, repo_path)) {
- rv = bc_strdup(sections[i]);
- free(real_tmp_repo);
- break;
- }
- free(real_tmp_repo);
- }
- }
- bc_strv_free(sections);
- return rv;
-}
-
-
-bc_config_t*
-bgr_settings_parse(void)
-{
- const char *bd = bgr_settings_get_base_dir();
- if (bd == NULL) {
- return NULL;
- }
- char *config_file = bc_strdup_printf("%s/blogc-git-receiver.ini", bd);
- if ((0 != access(config_file, F_OK))) {
- free(config_file);
- return NULL;
- }
-
- size_t len;
- bc_error_t *err = NULL;
- char* config_content = bc_file_get_contents(config_file, true, &len, &err);
- if (err != NULL) {
- fprintf(stderr, "warning: failed to read configuration file (%s): %s\n",
- config_file, err->msg);
- bc_error_free(err);
- free(config_file);
- free(config_content);
- return NULL;
- }
-
- bc_config_t *config = bc_config_parse(config_content, len, NULL, &err);
- free(config_content);
- if (err != NULL) {
- fprintf(stderr, "warning: failed to parse configuration file (%s): %s\n",
- config_file, err->msg);
- bc_error_free(err);
- free(config_file);
- return NULL;
- }
- free(config_file);
-
- return config;
-}
diff --git a/src/blogc-git-receiver/settings.h b/src/blogc-git-receiver/settings.h
deleted file mode 100644
index 04c1a2b..0000000
--- a/src/blogc-git-receiver/settings.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _SETTINGS_H
-#define _SETTINGS_H
-
-#include "../common/config-parser.h"
-
-const char* bgr_settings_get_base_dir(void);
-char* bgr_settings_get_builds_dir(void);
-char* bgr_settings_get_section(bc_config_t *config, const char *repo_path);
-bc_config_t* bgr_settings_parse(void);
-
-#endif /* _SETTINGS_H */
diff --git a/src/blogc-git-receiver/shell-command-parser.c b/src/blogc-git-receiver/shell-command-parser.c
deleted file mode 100644
index 0091e0b..0000000
--- a/src/blogc-git-receiver/shell-command-parser.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 <stdlib.h>
-#include <string.h>
-#include "../common/utils.h"
-#include "shell-command-parser.h"
-
-typedef enum {
- START_COMMAND = 1,
- START_REPO,
- START_REPO2,
- REPO,
- START_ESCAPED,
-} command_state_t;
-
-
-char*
-bgr_shell_command_parse(const char *command)
-{
- command_state_t state = START_COMMAND;
- size_t start = 0;
- size_t command_len = strlen(command);
-
- bc_string_t *rv = bc_string_new();
-
- for (size_t current = 0; current < command_len; current++) {
-
- char c = command[current];
-
- switch (state) {
- case START_COMMAND:
- if (c == ' ') {
- if (((current == 16) &&
- (0 == strncmp("git-receive-pack", command, 16))) ||
- ((current == 15) &&
- (0 == strncmp("git-upload-pack", command, 15))) ||
- ((current == 18) &&
- (0 == strncmp("git-upload-archive", command, 18))))
- {
- state = START_REPO;
- break;
- }
- goto error;
- }
- break;
-
- case START_REPO:
- if (c == '\'') { // never saw git using double-quotes
- state = START_REPO2;
- break;
- }
- if (c == '\\') { // escaped ! or '
- state = START_ESCAPED;
- break;
- }
- goto error;
-
- case START_REPO2:
- if (c == '\'') {
- state = START_REPO;
- break;
- }
- start = current;
- if (rv->len == 0 && c == '/') { // no absolute urls
- start = current + 1;
- }
- state = REPO;
- break;
-
- case START_ESCAPED:
- if (c == '!' || c == '\'') {
- bc_string_append_c(rv, c);
- state = START_REPO;
- break;
- }
- goto error;
-
- case REPO:
- if (c == '\'') {
- bc_string_append_len(rv, command + start, current - start);
- state = START_REPO;
- break;
- }
- break;
- }
- }
-
- if (rv->len > 0)
- return bc_string_free(rv, false);
-
-error:
- bc_string_free(rv, true);
- return NULL;
-}
diff --git a/src/blogc-git-receiver/shell-command-parser.h b/src/blogc-git-receiver/shell-command-parser.h
deleted file mode 100644
index 818d098..0000000
--- a/src/blogc-git-receiver/shell-command-parser.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _SHELL_COMMAND_PARSER_H
-#define _SHELL_COMMAND_PARSER_H
-
-char* bgr_shell_command_parse(const char *command);
-
-#endif /* _SHELL_COMMAND_PARSER_H */
diff --git a/src/blogc-git-receiver/shell.c b/src/blogc-git-receiver/shell.c
deleted file mode 100644
index a4c8a2d..0000000
--- a/src/blogc-git-receiver/shell.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include "../common/utils.h"
-#include "settings.h"
-#include "shell-command-parser.h"
-#include "shell.h"
-
-
-static bool
-lexists(const char *pathname)
-{
- struct stat b;
- int tmp_errno = errno;
- bool rv = lstat(pathname, &b) == 0;
- errno = tmp_errno;
- return rv;
-}
-
-
-int
-bgr_shell(int argc, char *argv[])
-{
- int rv = 0;
-
- char *repo = NULL;
- char *quoted_repo = NULL;
-
- // get shell path
- char *self = getenv("SHELL");
- if (self == NULL) {
- fprintf(stderr, "error: failed to find blogc-git-receiver path\n");
- rv = 1;
- goto cleanup;
- }
-
- // get base dir path
- const char *bd = bgr_settings_get_base_dir();
- if (bd == NULL) {
- fprintf(stderr, "error: failed to find base directory path\n");
- rv = 1;
- goto cleanup;
- }
-
- // validate command and extract git repository
- char *tmp_repo = bgr_shell_command_parse(argv[2]);
- if (tmp_repo == NULL) {
- fprintf(stderr, "error: invalid git-shell command: %s\n", argv[2]);
- rv = 1;
- goto cleanup;
- }
-
- repo = bc_strdup_printf("%s/repos/%s", bd, tmp_repo);
- quoted_repo = bc_shell_quote(repo);
- free(tmp_repo);
-
- if (0 == strncmp(argv[2], "git-upload-", 11)) // no need to check len here
- goto git_exec;
-
- if (0 != access(repo, F_OK)) {
- char *git_init_cmd = bc_strdup_printf(
- "git init --bare %s > /dev/null", quoted_repo);
- if (0 != system(git_init_cmd)) {
- fprintf(stderr, "error: failed to create git repository: %s\n",
- repo);
- rv = 1;
- free(git_init_cmd);
- goto cleanup;
- }
- free(git_init_cmd);
- }
-
- if (0 != chdir(repo)) {
- fprintf(stderr, "error: failed to chdir (%s): %s\n", repo,
- strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- if (0 != access("hooks", F_OK)) {
- // openwrt git package won't install git templates, then the git
- // repositories created with it won't have the hooks/ directory.
- if (0 != mkdir("hooks", 0777)) { // mkdir honors umask for us.
- fprintf(stderr, "error: failed to create directory (%s/hooks): "
- "%s\n", repo, strerror(errno));
- rv = 1;
- goto cleanup;
- }
- }
-
- if (0 != chdir("hooks")) {
- fprintf(stderr, "error: failed to chdir (%s/hooks): %s\n", repo,
- strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- if (lexists("pre-receive")) {
- if (0 != unlink("pre-receive")) {
- fprintf(stderr, "error: failed to remove old symlink "
- "(%s/hooks/pre-receive): %s\n", repo, strerror(errno));
- rv = 1;
- goto cleanup;
- }
- }
-
- if (0 != symlink(self, "pre-receive")) {
- fprintf(stderr, "error: failed to create symlink "
- "(%s/hooks/pre-receive): %s\n", repo, strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- if (lexists("post-receive")) {
- if (0 != unlink("post-receive")) {
- fprintf(stderr, "error: failed to remove old symlink "
- "(%s/hooks/post-receive): %s\n", repo, strerror(errno));
- rv = 1;
- goto cleanup;
- }
- }
-
- if (0 != symlink(self, "post-receive")) {
- fprintf(stderr, "error: failed to create symlink "
- "(%s/hooks/post-receive): %s\n", repo, strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
-git_exec:
-
- if (0 != chdir(bd)) {
- fprintf(stderr, "error: failed to chdir (%s): %s\n", bd, strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- // static allocation instead of bc_strdup_printf to avoid leaks
- char buffer[4096];
- char *command = bc_strdup(argv[2]);
- char *p;
- for (p = command; *p != ' ' && *p != '\0'; p++);
- if (*p == ' ')
- *p = '\0';
-
- if (sizeof(buffer) < (strlen(command) + strlen(quoted_repo) + 2)) {
- fprintf(stderr, "error: git-shell command is too big\n");
- rv = 1;
- goto cleanup;
- }
-
- if (0 > snprintf(buffer, sizeof(buffer), "%s %s", command, quoted_repo)) {
- fprintf(stderr, "error: failed to generate git-shell command\n");
- rv = 1;
- goto cleanup;
- }
-
- free(command);
- free(repo);
- free(quoted_repo);
-
- // this is a hack. no memory handling should be done inside this block
- if (NULL == getenv("__VALGRIND_ENABLED")) {
- execlp("git-shell", "git-shell", "-c", buffer, NULL);
-
- // execlp only returns on error, then something bad happened
- fprintf(stderr, "error: failed to execute git-shell\n");
- return 1;
- }
-
- printf("git-shell -c \"%s\"\n", buffer); // used by tests, ignore
- return 0;
-
-cleanup:
- free(repo);
- free(quoted_repo);
- return rv;
-}
diff --git a/src/blogc-git-receiver/shell.h b/src/blogc-git-receiver/shell.h
deleted file mode 100644
index b59ff40..0000000
--- a/src/blogc-git-receiver/shell.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _SHELL_H
-#define _SHELL_H
-
-char* bgr_shell_get_repo(const char *command);
-int bgr_shell(int argc, char *argv[]);
-
-#endif /* _SHELL_H */
diff --git a/src/blogc-make/atom.c b/src/blogc-make/atom.c
deleted file mode 100644
index 0b8cfd1..0000000
--- a/src/blogc-make/atom.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-#include "settings.h"
-#include "utils.h"
-#include "atom.h"
-
-static const char atom_template[] =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n"
- " <title type=\"text\">{{ SITE_TITLE }}{%% ifdef FILTER_TAG %%} - "
- "{{ FILTER_TAG }}{%% endif %%}</title>\n"
- " <id>{{ BASE_DOMAIN }}{{ BASE_URL }}%s</id>\n"
- " <updated>{{ DATE_FIRST_FORMATTED }}</updated>\n"
- " <link href=\"{{ BASE_DOMAIN }}{{ BASE_URL }}/\" />\n"
- " <link href=\"{{ BASE_DOMAIN }}{{ BASE_URL }}%s\" rel=\"self\" />\n"
- " <author>\n"
- " <name>{{ AUTHOR_NAME }}</name>\n"
- " <email>{{ AUTHOR_EMAIL }}</email>\n"
- " </author>\n"
- " <subtitle type=\"text\">{{ SITE_TAGLINE }}</subtitle>\n"
- " {%%- block listing %%}\n"
- " <entry>\n"
- " <title type=\"text\">{{ TITLE }}</title>\n"
- " <id>{{ BASE_DOMAIN }}{{ BASE_URL }}%s</id>\n"
- " <updated>{{ DATE_FORMATTED }}</updated>\n"
- " <published>{{ DATE_FORMATTED }}</published>\n"
- " <link href=\"{{ BASE_DOMAIN }}{{ BASE_URL }}%s\" />\n"
- " <author>\n"
- " <name>{{ AUTHOR_NAME }}</name>\n"
- " <email>{{ AUTHOR_EMAIL }}</email>\n"
- " </author>\n"
- " <content type=\"html\"><![CDATA[{{ CONTENT }}]]></content>\n"
- " </entry>\n"
- " {%%- endblock %%}\n"
- "</feed>\n";
-
-
-char*
-bm_atom_generate(bm_settings_t *settings)
-{
- if (settings == NULL)
- return NULL;
-
- const char *atom_prefix = bc_trie_lookup(settings->settings, "atom_prefix");
- const char *atom_ext = bc_trie_lookup(settings->settings, "atom_ext");
- const char *post_prefix = bc_trie_lookup(settings->settings, "post_prefix");
- const char *post_ext = bc_trie_lookup(settings->settings, "html_ext");
-
- bc_string_t *atom_url = bc_string_new();
-
- if (atom_prefix[0] != '\0')
- bc_string_append_c(atom_url, '/');
-
- bc_string_append(atom_url, atom_prefix);
- bc_string_append(atom_url, "{% ifdef FILTER_TAG %}/{{ FILTER_TAG }}");
-
- if (atom_prefix[0] == '\0' && atom_ext[0] != '/')
- bc_string_append(atom_url, "{% else %}/index");
-
- bc_string_append(atom_url, "{% endif %}");
- bc_string_append(atom_url, atom_ext);
-
- char *post_url = bm_generate_filename(NULL, post_prefix, "{{ FILENAME }}",
- post_ext);
-
- char *rv = bc_strdup_printf(atom_template, atom_url->str, atom_url->str,
- post_url, post_url);
-
- bc_string_free(atom_url, true);
- free(post_url);
-
- return rv;
-}
-
-
-char*
-bm_atom_deploy(bm_settings_t *settings, bc_error_t **err)
-{
- if (settings == NULL || err == NULL || *err != NULL)
- return NULL;
-
- if (NULL != bc_trie_lookup(settings->settings, "atom_legacy_entry_id")) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_ATOM,
- "'atom_legacy_entry_id' setting is not supported anymore. see "
- "https://blogc.rgm.io/news/blogc-0.16.1/ for details");
- return NULL;
- }
-
- // this is not really portable
- char fname[] = "/tmp/blogc-make_XXXXXX";
- int fd;
- if (-1 == (fd = mkstemp(fname))) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_ATOM,
- "Failed to create temporary atom template: %s", strerror(errno));
- return NULL;
- }
-
- char *content = bm_atom_generate(settings);
- if (content == NULL) {
- close(fd);
- unlink(fname);
- return NULL;
- }
-
- if (-1 == write(fd, content, strlen(content))) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_ATOM,
- "Failed to write to temporary atom template: %s", strerror(errno));
- free(content);
- close(fd);
- unlink(fname);
- return NULL;
- }
-
- free(content);
- close(fd);
-
- return bc_strdup(fname);
-}
-
-
-void
-bm_atom_destroy(const char *fname)
-{
- unlink(fname);
-}
diff --git a/src/blogc-make/atom.h b/src/blogc-make/atom.h
deleted file mode 100644
index 29a6dcb..0000000
--- a/src/blogc-make/atom.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_ATOM_H
-#define _MAKE_ATOM_H
-
-#include "../common/error.h"
-#include "settings.h"
-
-char* bm_atom_generate(bm_settings_t *settings);
-char* bm_atom_deploy(bm_settings_t *settings, bc_error_t **err);
-void bm_atom_destroy(const char *fname);
-
-#endif /* _MAKE_ATOM_H */
diff --git a/src/blogc-make/ctx.c b/src/blogc-make/ctx.c
deleted file mode 100644
index b8d23dd..0000000
--- a/src/blogc-make/ctx.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-#include <libgen.h>
-#include <limits.h>
-#include <time.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "atom.h"
-#include "settings.h"
-#include "exec.h"
-#include "utils.h"
-#include "ctx.h"
-
-
-bm_filectx_t*
-bm_filectx_new(bm_ctx_t *ctx, const char *filename, const char *slug,
- struct stat *st)
-{
- if (ctx == NULL || filename == NULL)
- return NULL;
-
- char *f = filename[0] == '/' ? bc_strdup(filename) :
- bc_strdup_printf("%s/%s", ctx->root_dir, filename);
-
- bm_filectx_t *rv = bc_malloc(sizeof(bm_filectx_t));
- rv->path = f;
- rv->short_path = bc_strdup(filename);
- rv->slug = bc_strdup(slug);
-
- if (st == NULL) {
- struct stat buf;
-
- if (0 != stat(f, &buf)) {
- rv->tv_sec = 0;
- rv->tv_nsec = 0;
- rv->readable = false;
- return rv;
- }
-
- st = &buf;
- }
-
- // if it isn't NULL the file exists for sure
- rv->tv_sec = st->st_mtim_tv_sec;
- rv->tv_nsec = st->st_mtim_tv_nsec;
- rv->readable = true;
- return rv;
-}
-
-
-bc_slist_t*
-bm_filectx_new_r(bc_slist_t *l, bm_ctx_t *ctx, const char *filename)
-{
- if (ctx == NULL || filename == NULL)
- return NULL;
-
- char *f = filename[0] == '/' ? bc_strdup(filename) :
- bc_strdup_printf("%s/%s", ctx->root_dir, filename);
-
- struct stat buf;
- if (0 != stat(f, &buf)) {
- free(f);
- return l;
- }
-
- if (S_ISDIR(buf.st_mode)) {
- DIR *dir = opendir(f);
- if (dir == NULL) {
- free(f);
- return l;
- }
-
- struct dirent *e;
- while (NULL != (e = readdir(dir))) {
- if ((0 == strcmp(e->d_name, ".")) || (0 == strcmp(e->d_name, "..")))
- continue;
- char *tmp = bc_strdup_printf("%s/%s", filename, e->d_name);
- l = bm_filectx_new_r(l, ctx, tmp);
- free(tmp);
- }
-
- closedir(dir);
- free(f);
- return l;
- }
-
- l = bc_slist_append(l, bm_filectx_new(ctx, filename, NULL, &buf));
- free(f);
- return l;
-}
-
-
-bool
-bm_filectx_changed(bm_filectx_t *ctx, time_t *tv_sec, long *tv_nsec)
-{
- if (ctx == NULL)
- return false;
-
- struct stat buf;
-
- if (0 == stat(ctx->path, &buf)) {
- if (buf.st_mtim_tv_sec == ctx->tv_sec) {
- if (buf.st_mtim_tv_nsec > ctx->tv_nsec) {
- if (tv_sec != NULL)
- *tv_sec = buf.st_mtim_tv_sec;
- if (tv_nsec != NULL)
- *tv_nsec = buf.st_mtim_tv_nsec;
- return true;
- }
- }
- else if (buf.st_mtim_tv_sec > ctx->tv_sec) {
- if (tv_sec != NULL)
- *tv_sec = buf.st_mtim_tv_sec;
- if (tv_nsec != NULL)
- *tv_nsec = buf.st_mtim_tv_nsec;
- return true;
- }
- }
-
- return false;
-}
-
-
-void
-bm_filectx_reload(bm_filectx_t *ctx)
-{
- if (ctx == NULL)
- return;
-
- time_t tv_sec;
- long tv_nsec;
-
- if (!bm_filectx_changed(ctx, &tv_sec, &tv_nsec))
- return;
-
- ctx->tv_sec = tv_sec;
- ctx->tv_nsec = tv_nsec;
- ctx->readable = true;
-}
-
-
-void
-bm_filectx_free(bm_filectx_t *fctx)
-{
- if (fctx == NULL)
- return;
- free(fctx->path);
- free(fctx->short_path);
- free(fctx->slug);
- free(fctx);
-}
-
-
-bm_ctx_t*
-bm_ctx_new(bm_ctx_t *base, const char *settings_file, const char *argv0,
- bc_error_t **err)
-{
- if (settings_file == NULL || err == NULL || *err != NULL)
- return NULL;
-
- char *abs_filename = bm_abspath(settings_file, err);
- if (*err != NULL)
- return NULL;
-
- size_t content_len;
- char *content = bc_file_get_contents(abs_filename, true, &content_len,
- err);
- if (*err != NULL) {
- free(abs_filename);
- return NULL;
- }
-
- bm_settings_t *settings = bm_settings_parse(content, content_len, err);
- if (settings == NULL || *err != NULL) {
- free(abs_filename);
- free(content);
- return NULL;
- }
- free(content);
-
- const char *template_dir = bc_trie_lookup(settings->settings, "template_dir");
- if (template_dir == NULL)
- template_dir = "";
-
- char *atom_template = NULL;
- bool atom_template_tmp = false;
- const char *atom_template_conf = bc_trie_lookup(settings->settings,
- "atom_template");
- if (atom_template_conf != NULL) {
- atom_template = bc_strdup_printf("%s/%s", template_dir, atom_template_conf);
- }
- else {
- atom_template = bm_atom_deploy(settings, err);
- atom_template_tmp = true;
- if (*err != NULL) {
- free(abs_filename);
- bm_settings_free(settings);
- return NULL;
- }
- }
-
- bm_ctx_t *rv = NULL;
- if (base == NULL) {
- rv = bc_malloc(sizeof(bm_ctx_t));
- rv->blogc = bm_exec_find_binary(argv0, "blogc", "BLOGC");
- rv->blogc_runserver = bm_exec_find_binary(argv0, "blogc-runserver",
- "BLOGC_RUNSERVER");
- rv->dev = false;
- rv->verbose = false;
- }
- else {
- bm_ctx_free_internal(base);
- rv = base;
- }
- rv->settings = settings;
-
- rv->settings_fctx = bm_filectx_new(rv, abs_filename, NULL, NULL);
- rv->root_dir = bc_strdup(dirname(abs_filename));
- free(abs_filename);
-
- const char *output_dir = getenv("OUTPUT_DIR");
- rv->short_output_dir = bc_strdup(output_dir != NULL ? output_dir : "_build");
-
- if (rv->short_output_dir[0] == '/') {
- rv->output_dir = bc_strdup(rv->short_output_dir);
- }
- else {
- rv->output_dir = bc_strdup_printf("%s/%s", rv->root_dir,
- rv->short_output_dir);
- }
-
- // can't return null and set error after this!
-
- char *main_template = bc_strdup_printf("%s/%s", template_dir,
- bm_ctx_settings_lookup(rv, "main_template"));
- rv->main_template_fctx = bm_filectx_new(rv, main_template, NULL, NULL);
- free(main_template);
-
- rv->atom_template_tmp = atom_template_tmp;
- rv->atom_template_fctx = bm_filectx_new(rv, atom_template, NULL, NULL);
- free(atom_template);
-
- const char *content_dir = bm_ctx_settings_lookup(rv, "content_dir");
- const char *post_prefix = bm_ctx_settings_lookup(rv, "post_prefix");
- const char *source_ext = bm_ctx_settings_lookup(rv, "source_ext");
- const char *listing_entry = bm_ctx_settings_lookup(rv, "listing_entry");
-
- rv->listing_entry_fctx = NULL;
- if (listing_entry != NULL) {
- char *f = bm_generate_filename(content_dir, NULL, listing_entry, source_ext);
- rv->listing_entry_fctx = bm_filectx_new(rv, f, listing_entry, NULL);
- free(f);
- }
-
- rv->posts_fctx = NULL;
- if (settings->posts != NULL) {
- for (size_t i = 0; settings->posts[i] != NULL; i++) {
- char *f = bm_generate_filename(content_dir, post_prefix,
- settings->posts[i], source_ext);
- rv->posts_fctx = bc_slist_append(rv->posts_fctx,
- bm_filectx_new(rv, f, settings->posts[i], NULL));
- free(f);
- }
- }
-
- rv->pages_fctx = NULL;
- if (settings->pages != NULL) {
- for (size_t i = 0; settings->pages[i] != NULL; i++) {
- char *f = bm_generate_filename(content_dir, NULL, settings->pages[i],
- source_ext);
- rv->pages_fctx = bc_slist_append(rv->pages_fctx,
- bm_filectx_new(rv, f, settings->pages[i], NULL));
- free(f);
- }
- }
-
- rv->copy_fctx = NULL;
- if (settings->copy != NULL) {
- for (size_t i = 0; settings->copy[i] != NULL; i++) {
- rv->copy_fctx = bm_filectx_new_r(rv->copy_fctx, rv,
- settings->copy[i]);
- }
- }
-
- return rv;
-}
-
-
-bool
-bm_ctx_reload(bm_ctx_t **ctx)
-{
- if (*ctx == NULL || (*ctx)->settings_fctx == NULL)
- return false;
-
- if (bm_filectx_changed((*ctx)->settings_fctx, NULL, NULL)) {
- // reload everything! we could just reload settings_fctx, as this
- // would force rebuilding everything, but we need to know new/deleted
- // files
-
- // needs to dup path, because it may be freed when reloading.
- char *tmp = bc_strdup((*ctx)->settings_fctx->path);
- bc_error_t *err = NULL;
- *ctx = bm_ctx_new(*ctx, tmp, NULL, &err);
- free(tmp);
- if (err != NULL) {
- bc_error_print(err, "blogc-make");
- bc_error_free(err);
- return false;
- }
- return true;
- }
-
- bm_filectx_reload((*ctx)->main_template_fctx);
- bm_filectx_reload((*ctx)->atom_template_fctx);
- bm_filectx_reload((*ctx)->listing_entry_fctx);
-
- for (bc_slist_t *tmp = (*ctx)->posts_fctx; tmp != NULL; tmp = tmp->next)
- bm_filectx_reload((bm_filectx_t*) tmp->data);
-
- for (bc_slist_t *tmp = (*ctx)->pages_fctx; tmp != NULL; tmp = tmp->next)
- bm_filectx_reload((bm_filectx_t*) tmp->data);
-
- for (bc_slist_t *tmp = (*ctx)->copy_fctx; tmp != NULL; tmp = tmp->next)
- bm_filectx_reload((bm_filectx_t*) tmp->data);
-
- return true;
-}
-
-
-void
-bm_ctx_free_internal(bm_ctx_t *ctx)
-{
- if (ctx == NULL)
- return;
-
- bm_settings_free(ctx->settings);
- ctx->settings = NULL;
-
- free(ctx->root_dir);
- ctx->root_dir = NULL;
- free(ctx->short_output_dir);
- ctx->short_output_dir = NULL;
- free(ctx->output_dir);
- ctx->output_dir = NULL;
-
- if (ctx->atom_template_tmp)
- bm_atom_destroy(ctx->atom_template_fctx->path);
- ctx->atom_template_tmp = false;
-
- bm_filectx_free(ctx->main_template_fctx);
- ctx->main_template_fctx = NULL;
- bm_filectx_free(ctx->atom_template_fctx);
- ctx->atom_template_fctx = NULL;
- bm_filectx_free(ctx->settings_fctx);
- ctx->settings_fctx = NULL;
- bm_filectx_free(ctx->listing_entry_fctx);
- ctx->listing_entry_fctx = NULL;
-
- bc_slist_free_full(ctx->posts_fctx, (bc_free_func_t) bm_filectx_free);
- ctx->posts_fctx = NULL;
- bc_slist_free_full(ctx->pages_fctx, (bc_free_func_t) bm_filectx_free);
- ctx->pages_fctx = NULL;
- bc_slist_free_full(ctx->copy_fctx, (bc_free_func_t) bm_filectx_free);
- ctx->copy_fctx = NULL;
-}
-
-
-void
-bm_ctx_free(bm_ctx_t *ctx)
-{
- if (ctx == NULL)
- return;
- bm_ctx_free_internal(ctx);
- free(ctx->blogc);
- free(ctx->blogc_runserver);
- free(ctx);
-}
-
-
-const char*
-bm_ctx_settings_lookup(bm_ctx_t *ctx, const char *key)
-{
- if (ctx == NULL || ctx->settings == NULL || ctx->settings->settings == NULL)
- return NULL;
- return bc_trie_lookup(ctx->settings->settings, key);
-}
-
-
-const char*
-bm_ctx_settings_lookup_str(bm_ctx_t *ctx, const char *key)
-{
- const char *rv = bm_ctx_settings_lookup(ctx, key);
- return rv == NULL ? "" : rv;
-}
diff --git a/src/blogc-make/ctx.h b/src/blogc-make/ctx.h
deleted file mode 100644
index a66d51c..0000000
--- a/src/blogc-make/ctx.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_CTX_H
-#define _MAKE_CTX_H
-
-#include <sys/stat.h>
-#include <stdbool.h>
-#include <time.h>
-#include "settings.h"
-#include "../common/error.h"
-#include "../common/utils.h"
-
-#ifdef __APPLE__
-#define st_mtim_tv_sec st_mtimespec.tv_sec
-#define st_mtim_tv_nsec st_mtimespec.tv_nsec
-#endif
-
-#ifdef __ANDROID__
-#define st_mtim_tv_sec st_mtime
-#define st_mtim_tv_nsec st_mtime_nsec
-#endif
-
-#ifndef st_mtim_tv_sec
-#define st_mtim_tv_sec st_mtim.tv_sec
-#endif
-#ifndef st_mtim_tv_nsec
-#define st_mtim_tv_nsec st_mtim.tv_nsec
-#endif
-
-
-typedef struct {
- char *path;
- char *short_path;
- char *slug;
- time_t tv_sec;
- long tv_nsec;
- bool readable;
-} bm_filectx_t;
-
-typedef struct {
- char *blogc;
- char *blogc_runserver;
-
- bool dev;
- bool verbose;
- bool atom_template_tmp;
-
- bm_settings_t *settings;
-
- char *root_dir;
- char *output_dir;
- char *short_output_dir;
-
- bm_filectx_t *main_template_fctx;
- bm_filectx_t *atom_template_fctx;
- bm_filectx_t *settings_fctx;
- bm_filectx_t *listing_entry_fctx;
-
- bc_slist_t *posts_fctx;
- bc_slist_t *pages_fctx;
- bc_slist_t *copy_fctx;
-} bm_ctx_t;
-
-bm_filectx_t* bm_filectx_new(bm_ctx_t *ctx, const char *filename, const char *slug,
- struct stat *st);
-bc_slist_t* bm_filectx_new_r(bc_slist_t *l, bm_ctx_t *ctx, const char *filename);
-bool bm_filectx_changed(bm_filectx_t *ctx, time_t *tv_sec, long *tv_nsec);
-void bm_filectx_reload(bm_filectx_t *ctx);
-void bm_filectx_free(bm_filectx_t *fctx);
-bm_ctx_t* bm_ctx_new(bm_ctx_t *base, const char *settings_file,
- const char *argv0, bc_error_t **err);
-bool bm_ctx_reload(bm_ctx_t **ctx);
-void bm_ctx_free_internal(bm_ctx_t *ctx);
-void bm_ctx_free(bm_ctx_t *ctx);
-const char* bm_ctx_settings_lookup(bm_ctx_t *ctx, const char *key);
-const char* bm_ctx_settings_lookup_str(bm_ctx_t *ctx, const char *key);
-
-#endif /* _MAKE_CTX_H */
diff --git a/src/blogc-make/exec-native.c b/src/blogc-make/exec-native.c
deleted file mode 100644
index 6d7f58f..0000000
--- a/src/blogc-make/exec-native.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <errno.h>
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "exec-native.h"
-#include "ctx.h"
-
-
-int
-bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose)
-{
- if (verbose)
- printf("Copying '%s' to '%s'\n", source->path, dest->path);
- else
- printf(" COPY %s\n", dest->short_path);
- fflush(stdout);
-
- char *fname = bc_strdup(dest->path);
- for (char *tmp = fname; *tmp != '\0'; tmp++) {
- if (*tmp != '/' && *tmp != '\\')
- continue;
- char bkp = *tmp;
- *tmp = '\0';
- if ((strlen(fname) > 0) &&
- (-1 == mkdir(fname, 0777)) &&
- (errno != EEXIST))
- {
- fprintf(stderr, "blogc-make: error: failed to create output "
- "directory (%s): %s\n", fname, strerror(errno));
- free(fname);
- return 1;
- }
- *tmp = bkp;
- }
- free(fname);
-
- int fd_from = open(source->path, O_RDONLY);
- if (fd_from < 0) {
- fprintf(stderr, "blogc-make: error: failed to open source file to copy "
- " (%s): %s\n", source->path, strerror(errno));
- return 1;
- }
-
- int fd_to = open(dest->path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd_to < 0) {
- fprintf(stderr, "blogc-make: error: failed to open destination file to "
- "copy (%s): %s\n", dest->path, strerror(errno));
- close(fd_from);
- return 1;
- }
-
- ssize_t nread;
- char buffer[BC_FILE_CHUNK_SIZE];
- while (0 < (nread = read(fd_from, buffer, BC_FILE_CHUNK_SIZE))) {
- char *out_ptr = buffer;
- do {
- ssize_t nwritten = write(fd_to, out_ptr, nread);
- if (nwritten == -1) {
- fprintf(stderr, "blogc-make: error: failed to write to "
- "destination file (%s): %s\n", dest->path, strerror(errno));
- close(fd_from);
- close(fd_to);
- return 1;
- }
- nread -= nwritten;
- out_ptr += nwritten;
- } while (nread > 0);
- }
-
- close(fd_from);
- close(fd_to);
-
- return 0;
-}
-
-
-bool
-bm_exec_native_is_empty_dir(const char *dir, bc_error_t **err)
-{
- DIR *d = opendir(dir);
- if (d == NULL) {
- if (errno == ENOENT) {
- return true;
- }
- if (err != NULL) {
- *err = bc_error_new_printf(0, "failed to open directory (%s): %s\n",
- dir, strerror(errno));
- }
- return true;
- }
-
- struct dirent *e;
- size_t count = 0;
- while (NULL != (e = readdir(d))) {
- if ((0 == strcmp(e->d_name, ".")) || (0 == strcmp(e->d_name, "..")))
- continue;
- count++;
- break;
- }
-
- if (0 != closedir(d)) {
- if (err != NULL) {
- *err = bc_error_new_printf(0, "failed to close directory (%s): %s\n",
- dir, strerror(errno));
- }
- return true;
- }
-
- return count == 0;
-}
-
-
-int
-bm_exec_native_rm(const char *output_dir, bm_filectx_t *dest, bool verbose)
-{
- if (verbose)
- printf("Removing file '%s'\n", dest->path);
- else
- printf(" CLEAN %s\n", dest->short_path);
- fflush(stdout);
-
- if (0 != unlink(dest->path)) {
- fprintf(stderr, "blogc-make: error: failed to remove file (%s): %s\n",
- dest->path, strerror(errno));
- return 1;
- }
-
- int rv = 0;
-
- // blame freebsd's libc for all of those memory allocations around dirname
- // calls!
- char *short_dir = bc_strdup(dirname(dest->short_path));
- char *dir = bc_strdup(dirname(dest->path));
-
- bc_error_t *err = NULL;
-
- while ((0 != strcmp(short_dir, ".")) && (0 != strcmp(short_dir, "/"))) {
- bool empty = bm_exec_native_is_empty_dir(dir, &err);
- if (err != NULL) {
- fprintf(stderr, "blogc-make: error: %s\n", err->msg);
- bc_error_free(err);
- rv = 1;
- break;
- }
- if (!empty) {
- break;
- }
- if (verbose) {
- printf("Removing directory '%s'\n", dir);
- fflush(stdout);
- }
- if (0 != rmdir(dir)) {
- fprintf(stderr,
- "blogc-make: error: failed to remove directory(%s): %s\n",
- dir, strerror(errno));
- rv = 1;
- break;
- }
- if (0 == strcmp(dir, output_dir)) {
- break;
- }
-
- char *tmp = short_dir;
- short_dir = bc_strdup(dirname(short_dir));
- free(tmp);
- tmp = dir;
- dir = bc_strdup(dirname(dir));
- free(tmp);
- }
-
- free(short_dir);
- free(dir);
-
- return rv;
-}
diff --git a/src/blogc-make/exec-native.h b/src/blogc-make/exec-native.h
deleted file mode 100644
index 56a1da1..0000000
--- a/src/blogc-make/exec-native.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_EXEC_NATIVE_H
-#define _MAKE_EXEC_NATIVE_H
-
-#include <stdbool.h>
-#include "../common/error.h"
-#include "ctx.h"
-
-int bm_exec_native_cp(bm_filectx_t *source, bm_filectx_t *dest, bool verbose);
-bool bm_exec_native_is_empty_dir(const char *dir, bc_error_t **err);
-int bm_exec_native_rm(const char *output_dir, bm_filectx_t *dest, bool verbose);
-
-#endif /* _MAKE_EXEC_NATIVE_H */
diff --git a/src/blogc-make/exec.c b/src/blogc-make/exec.c
deleted file mode 100644
index 4b1f6df..0000000
--- a/src/blogc-make/exec.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_SYSEXITS_H
-#include <sysexits.h>
-#else
-#define EX_CONFIG 78
-#endif /* HAVE_SYSEXITS_H */
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <libgen.h>
-#include "../common/compat.h"
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "ctx.h"
-#include "exec.h"
-#include "settings.h"
-
-
-char*
-bm_exec_find_binary(const char *argv0, const char *bin, const char *env)
-{
-#ifdef MAKE_EMBEDDED
- // for embedded blogc-make, if we are looking for blogc, we just return
- // argv0, because the static binary may not be named `blogc`, and we
- // prefer to use our own `blogc` instead of some other version around.
- if (argv0 != NULL && bin != NULL && (0 == strcmp(bin, "blogc"))) {
- return bc_shell_quote(argv0);
- }
-#endif
-
- // first try: env var
- const char *env_bin = getenv(env);
- if (env_bin != NULL) {
- return bc_shell_quote(env_bin);
- }
-
- // second try: same dir as current exec
- // we rely on some assumptions here:
- //
- // - if binary is called without a directory, that means location will
- // be resolved from $PATH, we don't care about doing a dir lookup.
- // - we *never* call chdir anywhere in the code, so we can assume
- // that relative paths will work as expected.
- // - windows path sep is not supported
- if (argv0 != NULL && (NULL != strchr(argv0, '/'))) {
- char *path = bc_strdup(argv0);
- char *dir = bc_strdup(dirname(path));
- free(path);
- char *tmp = bc_strdup_printf("%s/%s", dir, bin);
- free(dir);
- if (0 == access(tmp, X_OK)) {
- char *rv = bc_shell_quote(tmp);
- free(tmp);
- return rv;
- }
- free(tmp);
- }
-
- // last try: $PATH
- return bc_strdup(bin);
-}
-
-
-int
-bm_exec_command(const char *cmd, const char *input, char **output,
- char **error, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return 1;
-
- int fd_in[2];
- if (-1 == pipe(fd_in)) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to create stdin pipe: %s", strerror(errno));
- return 1;
- }
-
- int fd_out[2];
- if (-1 == pipe(fd_out)) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to create stdout pipe: %s", strerror(errno));
- close(fd_in[0]);
- close(fd_in[1]);
- return 1;
- }
-
- int fd_err[2];
- if (-1 == pipe(fd_err)) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to create stderr pipe: %s", strerror(errno));
- close(fd_in[0]);
- close(fd_in[1]);
- close(fd_out[0]);
- close(fd_out[1]);
- return 1;
- }
-
- pid_t pid = fork();
- if (pid == -1) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to fork: %s", strerror(errno));
- close(fd_in[0]);
- close(fd_in[1]);
- close(fd_out[0]);
- close(fd_out[1]);
- close(fd_err[0]);
- close(fd_err[1]);
- return 1;
- }
-
- // child
- if (pid == 0) {
- close(fd_in[1]);
- close(fd_out[0]);
- close(fd_err[0]);
-
- dup2(fd_in[0], STDIN_FILENO);
- dup2(fd_out[1], STDOUT_FILENO);
- dup2(fd_err[1], STDERR_FILENO);
-
- char *const argv[] = {
- "/bin/sh",
- "-c",
- (char*) cmd,
- NULL,
- };
-
- execv(argv[0], argv);
-
- exit(1);
- }
-
- // parent
- close(fd_in[0]);
- close(fd_out[1]);
- close(fd_err[1]);
-
- if (input != NULL) {
- if (-1 == write(fd_in[1], input, strlen(input))) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to write to stdin pipe: %s", strerror(errno));
- close(fd_in[1]);
- close(fd_out[0]);
- close(fd_err[0]);
- return 1;
- }
- }
-
- close(fd_in[1]);
-
- char buffer[BC_FILE_CHUNK_SIZE];
- ssize_t s;
-
- bc_string_t *out = NULL;
- while(0 != (s = read(fd_out[0], buffer, BC_FILE_CHUNK_SIZE))) {
- if (s == -1) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to read from stdout pipe: %s", strerror(errno));
- close(fd_out[0]);
- close(fd_err[0]);
- bc_string_free(out, true);
- return 1;
- }
- if (out == NULL) {
- out = bc_string_new();
- }
- bc_string_append_len(out, buffer, s);
- }
- if (out != NULL) {
- *output = bc_string_free(out, false);
- }
- close(fd_out[0]);
-
- out = NULL;
- while(0 != (s = read(fd_err[0], buffer, BC_FILE_CHUNK_SIZE))) {
- if (s == -1) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC,
- "Failed to read from stderr pipe: %s", strerror(errno));
- close(fd_err[0]);
- bc_string_free(out, true);
- return 1;
- }
- if (out == NULL)
- out = bc_string_new();
- bc_string_append_len(out, buffer, s);
- }
- if (out != NULL) {
- *error = bc_string_free(out, false);
- }
- close(fd_err[0]);
-
- int status;
- waitpid(pid, &status, 0);
-
- return bc_compat_status_code(status);
-}
-
-
-static void
-list_variables(const char *key, const char *value, bc_string_t *str)
-{
- char *tmp = bc_shell_quote(value);
- bc_string_append_printf(str, " -D %s=%s", key, tmp);
- free(tmp);
-}
-
-
-char*
-bm_exec_build_blogc_cmd(const char *blogc_bin, bm_settings_t *settings,
- bc_trie_t *global_variables, bc_trie_t *local_variables, const char *print,
- bool listing, const char *listing_entry, const char *template,
- const char *output, bool dev, bool sources_stdin)
-{
- bc_string_t *rv = bc_string_new();
-
- const char *locale = NULL;
- if (settings != NULL) {
- locale = bc_trie_lookup(settings->settings, "locale");
- }
- if (locale != NULL) {
- char *tmp = bc_shell_quote(locale);
- bc_string_append_printf(rv, "LC_ALL=%s ", tmp);
- free(tmp);
- }
-
- bc_string_append(rv, blogc_bin);
-
- if (settings != NULL) {
- if (settings->tags != NULL) {
- char *tags = bc_strv_join(settings->tags, " ");
- bc_string_append_printf(rv, " -D MAKE_TAGS='%s'", tags);
- free(tags);
- }
-
- bc_trie_foreach(settings->global,
- (bc_trie_foreach_func_t) list_variables, rv);
- }
-
- bc_trie_foreach(global_variables, (bc_trie_foreach_func_t) list_variables, rv);
- bc_trie_foreach(local_variables, (bc_trie_foreach_func_t) list_variables, rv);
-
- if (dev) {
- bc_string_append(rv, " -D MAKE_ENV_DEV=1 -D MAKE_ENV='dev'");
- }
-
- if (print != NULL) {
- bc_string_append_printf(rv, " -p %s", print);
- }
-
- if (listing) {
- bc_string_append(rv, " -l");
- if (listing_entry != NULL) {
- char *tmp = bc_shell_quote(listing_entry);
- bc_string_append_printf(rv, " -e %s", tmp);
- free(tmp);
- }
- }
-
- if (template != NULL) {
- char *tmp = bc_shell_quote(template);
- bc_string_append_printf(rv, " -t %s", tmp);
- free(tmp);
- }
-
- if (output != NULL) {
- char *tmp = bc_shell_quote(output);
- bc_string_append_printf(rv, " -o %s", tmp);
- free(tmp);
- }
-
- if (sources_stdin) {
- bc_string_append(rv, " -i");
- }
-
- return bc_string_free(rv, false);
-}
-
-
-int
-bm_exec_blogc(bm_ctx_t *ctx, bc_trie_t *global_variables, bc_trie_t *local_variables,
- bool listing, bm_filectx_t *listing_entry, bm_filectx_t *template,
- bm_filectx_t *output, bc_slist_t *sources, bool only_first_source)
-{
- if (ctx == NULL)
- return 1;
-
- bc_string_t *input = bc_string_new();
- for (bc_slist_t *l = sources; l != NULL; l = l->next) {
- bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path);
- if (only_first_source)
- break;
- }
-
- char *cmd = bm_exec_build_blogc_cmd(ctx->blogc, ctx->settings, global_variables,
- local_variables, NULL, listing, listing_entry == NULL ? NULL : listing_entry->path,
- template->path, output->path, ctx->dev, input->len > 0);
-
- if (ctx->verbose)
- printf("%s\n", cmd);
- else
- printf(" BLOGC %s\n", output->short_path);
- fflush(stdout);
-
- char *out = NULL;
- char *err = NULL;
- bc_error_t *error = NULL;
-
- int rv = bm_exec_command(cmd, input->str, &out, &err, &error);
-
- if (error != NULL) {
- bc_error_print(error, "blogc-make");
- free(cmd);
- free(out);
- free(err);
- bc_string_free(input, true);
- bc_error_free(error);
- return 1;
- }
-
- if (rv != 0 && ctx->verbose) {
- fprintf(stderr,
- "blogc-make: error: Failed to execute command.\n"
- "\n"
- "STATUS CODE: %d\n", rv);
- if (input->len > 0) {
- fprintf(stderr, "\nSTDIN:\n"
- "----------------------------->8-----------------------------\n"
- "%s\n"
- "----------------------------->8-----------------------------\n",
- bc_str_strip(input->str));
- }
- if (out != NULL) {
- fprintf(stderr, "\nSTDOUT:\n"
- "----------------------------->8-----------------------------\n"
- "%s\n"
- "----------------------------->8-----------------------------\n",
- bc_str_strip(out));
- }
- if (err != NULL) {
- fprintf(stderr, "\nSTDERR:\n"
- "----------------------------->8-----------------------------\n"
- "%s\n"
- "----------------------------->8-----------------------------\n",
- bc_str_strip(err));
- }
- fprintf(stderr, "\n");
- }
- else if (err != NULL) {
- fprintf(stderr, "%s\n", err);
- }
-
- bc_string_free(input, true);
- free(cmd);
- free(out);
- free(err);
-
- return rv == 127 ? 1 : rv;
-}
-
-
-char*
-bm_exec_blogc_get_variable(bm_ctx_t *ctx, bc_trie_t *global_variables,
- bc_trie_t *local_variables, const char *variable, bool listing,
- bc_slist_t *sources, bool only_first_source)
-{
- if (ctx == NULL)
- return NULL;
-
- bc_string_t *input = bc_string_new();
- for (bc_slist_t *l = sources; l != NULL; l = l->next) {
- bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path);
- if (only_first_source)
- break;
- }
-
- char *cmd = bm_exec_build_blogc_cmd(ctx->blogc, ctx->settings, global_variables,
- local_variables, variable, listing, NULL, NULL, NULL, ctx->dev, input->len > 0);
-
- if (ctx->verbose)
- printf("%s\n", cmd);
- fflush(stdout);
-
- char *out = NULL;
- char *err = NULL;
- bc_error_t *error = NULL;
-
- int rv = bm_exec_command(cmd, input->str, &out, &err, &error);
-
- if (error != NULL) {
- bc_error_print(error, "blogc-make");
- bc_error_free(error);
- bc_string_free(input, true);
- free(cmd);
- free(out);
- free(err);
- return NULL;
- }
-
- if (rv != 0) {
- if (rv != EX_CONFIG)
- fprintf(stderr, "blogc-make: error: %s\n", bc_str_strip(err));
- bc_string_free(input, true);
- free(cmd);
- free(out);
- free(err);
- return NULL;
- }
-
- char *val = NULL;
- if (out != NULL)
- val = bc_strndup(out, strlen(out) - 1);
-
- bc_string_free(input, true);
- free(cmd);
- free(out);
- free(err);
-
- return val;
-}
-
-
-int
-bm_exec_blogc_runserver(bm_ctx_t *ctx, const char *host, const char *port,
- const char *threads)
-{
- if (ctx == NULL)
- return 1;
-
- bc_string_t *cmd = bc_string_new();
-
- bc_string_append(cmd, ctx->blogc_runserver);
-
- if (host != NULL) {
- char *tmp = bc_shell_quote(host);
- bc_string_append_printf(cmd, " -t %s", tmp);
- free(tmp);
- }
-
- if (port != NULL) {
- char *tmp = bc_shell_quote(port);
- bc_string_append_printf(cmd, " -p %s", tmp);
- free(tmp);
- }
-
- if (threads != NULL) {
- char *tmp = bc_shell_quote(threads);
- bc_string_append_printf(cmd, " -m %s", tmp);
- free(tmp);
- }
-
- char *tmp = bc_shell_quote(ctx->output_dir);
- bc_string_append_printf(cmd, " %s", tmp);
- free(tmp);
-
- if (ctx->verbose)
- printf("%s\n\n", cmd->str);
- else
- printf("\n");
- fflush(stdout);
-
- // we don't need pipes to run blogc-runserver, because it is "interactive"
- int status = system(cmd->str);
- int rv = bc_compat_status_code(status);
- bc_string_free(cmd, true);
-
- if (rv != 0 && rv != 130) {
- if (rv == 127) {
- fprintf(stderr,
- "blogc-make: error: blogc-runserver command not found. Maybe "
- "it is not installed?\n");
- rv = 1; // blogc-make exists, so we should not return 127
- }
- else {
- fprintf(stderr,
- "blogc-make: error: Failed to execute command, returned "
- "status code: %d\n", rv);
- }
- }
-
- return rv;
-}
diff --git a/src/blogc-make/exec.h b/src/blogc-make/exec.h
deleted file mode 100644
index 6bc206f..0000000
--- a/src/blogc-make/exec.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_EXEC_H
-#define _MAKE_EXEC_H
-
-#include <stdbool.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-#include "ctx.h"
-#include "settings.h"
-
-char* bm_exec_find_binary(const char *argv0, const char *bin, const char *env);
-int bm_exec_command(const char *cmd, const char *input, char **output,
- char **error, bc_error_t **err);
-char* bm_exec_build_blogc_cmd(const char *blogc_bin, bm_settings_t *settings,
- bc_trie_t *global_variables, bc_trie_t *local_variables, const char *print,
- bool listing, const char *listing_entry, const char *template,
- const char *output, bool dev, bool sources_stdin);
-int bm_exec_blogc(bm_ctx_t *ctx, bc_trie_t *global_variables,
- bc_trie_t *local_variables, bool listing, bm_filectx_t *listing_entry,
- bm_filectx_t *template, bm_filectx_t *output, bc_slist_t *sources,
- bool only_first_source);
-char* bm_exec_blogc_get_variable(bm_ctx_t *ctx, bc_trie_t *global_variables,
- bc_trie_t *local_variables, const char *variable, bool listing,
- bc_slist_t *sources, bool only_first_source);
-int bm_exec_blogc_runserver(bm_ctx_t *ctx, const char *host, const char *port,
- const char *threads);
-
-#endif /* _MAKE_EXEC_H */
diff --git a/src/blogc-make/httpd.c b/src/blogc-make/httpd.c
deleted file mode 100644
index c352da0..0000000
--- a/src/blogc-make/httpd.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <unistd.h>
-#include "../common/utils.h"
-#include "ctx.h"
-#include "exec.h"
-#include "reloader.h"
-#include "httpd.h"
-
-// we are not going to unit-test these functions, then printing errors
-// directly is not a big issue
-
-
-typedef struct {
- bm_ctx_t *ctx;
- bc_trie_t *args;
-} bm_httpd_t;
-
-static pthread_mutex_t mutex_httpd_starting = PTHREAD_MUTEX_INITIALIZER;
-static bool httpd_starting = false;
-
-
-static void*
-httpd_thread(void *arg)
-{
- bm_httpd_t *httpd = arg;
-
- pthread_mutex_lock(&mutex_httpd_starting);
- httpd_starting = true;
- pthread_mutex_unlock(&mutex_httpd_starting);
-
- int rv = bm_exec_blogc_runserver(httpd->ctx, bc_trie_lookup(httpd->args, "host"),
- bc_trie_lookup(httpd->args, "port"), bc_trie_lookup(httpd->args, "threads"));
-
- pthread_mutex_lock(&mutex_httpd_starting);
- httpd_starting = false;
- pthread_mutex_unlock(&mutex_httpd_starting);
-
- free(httpd);
-
- // stop the reloader
- bm_reloader_stop(rv);
-
- return NULL;
-}
-
-
-int
-bm_httpd_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec, bc_slist_t *outputs,
- bc_trie_t *args)
-{
- pthread_mutex_lock(&mutex_httpd_starting);
- bool starting = httpd_starting;
- pthread_mutex_unlock(&mutex_httpd_starting);
-
- if (starting) {
- fprintf(stderr, "blogc-make: error: httpd already running\n");
- return 1;
- }
-
- int err;
-
- pthread_attr_t attr;
- if (0 != (err = pthread_attr_init(&attr))) {
- fprintf(stderr, "blogc-make: error: failed to initialize httpd "
- "thread attributes: %s\n", strerror(err));
- return 1;
- }
-
- // we run the thread detached, because we don't want to wait it to join
- // before exiting. the OS can clean it properly
- if (0 != (err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))) {
- fprintf(stderr, "blogc-make: error: failed to mark httpd thread as "
- "detached: %s\n", strerror(err));
- return 1;
- }
-
- bm_httpd_t *rv = bc_malloc(sizeof(bm_httpd_t));
- rv->ctx = *ctx;
- rv->args = args;
-
- pthread_t thread;
- if (0 != (err = pthread_create(&thread, &attr, httpd_thread, rv))) {
- fprintf(stderr, "blogc-make: error: failed to create httpd "
- "thread: %s\n", strerror(err));
- free(rv);
- return 1;
- }
-
- // we could use some pthread_*_timedwait() apis here, but I decided to
- // just use simple mutexes for the sake of portability.
- size_t count = 0;
- while (true) {
- pthread_mutex_lock(&mutex_httpd_starting);
- starting = httpd_starting;
- pthread_mutex_unlock(&mutex_httpd_starting);
-
- if (starting)
- break;
-
- if (++count > 100) {
- fprintf(stderr, "blogc-make: error: failed to start httpd thread: "
- "too many retries\n");
- // rv will leak, but it is not safe to free here
- return 1;
- }
- usleep(100000);
- }
-
- return bm_reloader_run(ctx, rule_exec, outputs, args);
-}
diff --git a/src/blogc-make/httpd.h b/src/blogc-make/httpd.h
deleted file mode 100644
index b0fa87d..0000000
--- a/src/blogc-make/httpd.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_HTTPD_H
-#define _MAKE_HTTPD_H
-
-#include "../common/utils.h"
-#include "ctx.h"
-#include "rules.h"
-
-int bm_httpd_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec, bc_slist_t *outputs,
- bc_trie_t *args);
-
-#endif /* _MAKE_HTTPD_H */
diff --git a/src/blogc-make/main.c b/src/blogc-make/main.c
deleted file mode 100644
index 5b4a030..0000000
--- a/src/blogc-make/main.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-#include "ctx.h"
-#include "rules.h"
-
-
-static void
-print_help(void)
-{
- printf(
- "usage:\n"
- " blogc-make [-h] [-v] [-D] [-V] [-f FILE] [RULE ...]\n"
- " - A simple build tool for blogc.\n"
- "\n"
- "positional arguments:\n"
- " RULE build rule(s) to run. can include comma-separated\n"
- " key-value pairs of rule arguments in the format\n"
- " RULE:arg1=value1,arg2=value2,... (default: all)\n"
- "\n"
- "optional arguments:\n"
- " -h show this help message and exit\n"
- " -v show version and exit\n"
- " -D build for development environment\n"
- " -V be verbose when executing commands\n"
- " -f FILE read FILE as blogcfile\n");
- bm_rule_print_help();
-}
-
-
-static void
-print_usage(void)
-{
- printf("usage: blogc-make [-h] [-v] [-D] [-V] [-f FILE] [RULE ...]\n");
-}
-
-
-int
-#ifdef MAKE_EMBEDDED
-bm_main(int argc, char **argv)
-#else
-main(int argc, char **argv)
-#endif
-{
- setlocale(LC_ALL, "");
-
- int rv = 0;
- bc_error_t *err = NULL;
-
- bc_slist_t *rules = NULL;
- bool verbose = false;
- bool dev = false;
- char *blogcfile = NULL;
- bm_ctx_t *ctx = NULL;
-
- for (size_t i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- switch (argv[i][1]) {
- case 'h':
- print_help();
- goto cleanup;
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- goto cleanup;
- case 'D':
- dev = true;
- break;
- case 'V':
- verbose = true;
- break;
- case 'f':
- if (argv[i][2] != '\0')
- blogcfile = bc_strdup(argv[i] + 2);
- else if (i + 1 < argc)
- blogcfile = bc_strdup(argv[++i]);
- break;
-#ifdef MAKE_EMBEDDED
- case 'm':
- // no-op, for embedding into blogc binary.
- break;
-#endif
- default:
- print_usage();
- fprintf(stderr, "blogc-make: error: invalid argument: "
- "-%c\n", argv[i][1]);
- rv = 1;
- goto cleanup;
- }
- }
- else {
- rules = bc_slist_append(rules, bc_strdup(argv[i]));
- }
- }
-
- if (rules == NULL) {
- rules = bc_slist_append(rules, bc_strdup("all"));
- }
-
- ctx = bm_ctx_new(NULL, blogcfile ? blogcfile : "blogcfile",
- argc > 0 ? argv[0] : NULL, &err);
- if (err != NULL) {
- bc_error_print(err, "blogc-make");
- rv = 1;
- goto cleanup;
- }
- ctx->dev = dev;
- ctx->verbose = verbose;
-
- rv = bm_rule_executor(ctx, rules);
-
-cleanup:
-
- bc_slist_free_full(rules, free);
- free(blogcfile);
- bm_ctx_free(ctx);
- bc_error_free(err);
-
- return rv;
-}
diff --git a/src/blogc-make/reloader.c b/src/blogc-make/reloader.c
deleted file mode 100644
index fea8bd4..0000000
--- a/src/blogc-make/reloader.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <errno.h>
-#include "../common/utils.h"
-#include "ctx.h"
-#include "rules.h"
-#include "reloader.h"
-
-// we are not going to unit-test these functions, then printing errors
-// directly is not a big issue
-
-static pthread_mutex_t mutex_running = PTHREAD_MUTEX_INITIALIZER;
-static bool running = false;
-static int reloader_status_code = 0;
-static void (*handler_func)(int) = NULL;
-
-static void
-sig_handler(int signum)
-{
- bm_reloader_stop(signum + 128);
-}
-
-
-int
-bm_reloader_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec,
- bc_slist_t *outputs, bc_trie_t *args)
-{
- // install ^C handler
- struct sigaction current_action;
- if (sigaction(SIGINT, NULL, &current_action) < 0) {
- fprintf(stderr, "blogc-make: failed to run reloader: %s\n", strerror(errno));
- return 1;
- }
- if (current_action.sa_handler != sig_handler) { // not installed yet
- // backup current handler
- pthread_mutex_lock(&mutex_running);
- handler_func = current_action.sa_handler;
- pthread_mutex_unlock(&mutex_running);
-
- // set new handler
- struct sigaction new_action;
- new_action.sa_handler = sig_handler;
- sigemptyset(&new_action.sa_mask);
- new_action.sa_flags = 0;
- if (sigaction(SIGINT, &new_action, NULL) < 0) {
- fprintf(stderr, "blogc-make: failed to run reloader: %s\n",
- strerror(errno));
- return 1;
- }
- }
-
- pthread_mutex_lock(&mutex_running);
- running = true;
- pthread_mutex_unlock(&mutex_running);
-
- while (running) {
- if (!bm_ctx_reload(ctx)) {
- fprintf(stderr, "blogc-make: warning: failed to reload context. "
- "retrying in 5 seconds ...\n\n");
- sleep(5);
- continue;
- }
- if (0 != rule_exec(*ctx, outputs, args)) {
- fprintf(stderr, "blogc-make: warning: failed to rebuild website. "
- "retrying in 5 seconds ...\n\n");
- sleep(5);
- continue;
- }
- sleep(1);
- }
-
- return reloader_status_code;
-}
-
-
-void
-bm_reloader_stop(int status_code)
-{
- pthread_mutex_lock(&mutex_running);
-
- running = false;
- reloader_status_code = status_code;
-
- // reraise if SIGINT
- if (status_code == SIGINT + 128) {
-
- // reinstall old ^C handler
- struct sigaction new_action;
- new_action.sa_handler = handler_func;
- sigemptyset(&new_action.sa_mask);
- new_action.sa_flags = 0;
- sigaction(SIGINT, &new_action, NULL);
-
- // run it
- raise(SIGINT);
-
- // SIGINT will usually kill the process, but in the case that the
- // `handler_func` prevents it, our custom handler will be reinstalled
- // by `bm_reloader_run`.
- }
-
- pthread_mutex_unlock(&mutex_running);
-}
diff --git a/src/blogc-make/reloader.h b/src/blogc-make/reloader.h
deleted file mode 100644
index f3fddf4..0000000
--- a/src/blogc-make/reloader.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_RELOADER_H
-#define _MAKE_RELOADER_H
-
-#include "../common/utils.h"
-#include "ctx.h"
-#include "rules.h"
-
-int bm_reloader_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec,
- bc_slist_t *outputs, bc_trie_t *args);
-void bm_reloader_stop(int status_code);
-
-#endif /* _MAKE_RELOADER_H */
diff --git a/src/blogc-make/rules.c b/src/blogc-make/rules.c
deleted file mode 100644
index 7d92b95..0000000
--- a/src/blogc-make/rules.c
+++ /dev/null
@@ -1,1078 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 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 "../common/utils.h"
-#include "atom.h"
-#include "ctx.h"
-#include "exec.h"
-#include "exec-native.h"
-#include "httpd.h"
-#include "reloader.h"
-#include "settings.h"
-#include "utils.h"
-#include "rules.h"
-
-
-static void
-posts_ordering(bm_ctx_t *ctx, bc_trie_t *variables, const char *variable)
-{
- if (ctx == NULL)
- return; // something is wrong, let's not add any variable
-
- const char *value = bm_ctx_settings_lookup_str(ctx, variable);
- bool asc = 0 == strcasecmp(value, "asc");
- bool sort = bc_str_to_bool(bm_ctx_settings_lookup(ctx, "posts_sort"));
-
- if (sort) {
- bc_trie_insert(variables, "FILTER_SORT", bc_strdup("1"));
- }
-
- if ((sort && asc) || (!sort && !asc)) {
- bc_trie_insert(variables, "FILTER_REVERSE", bc_strdup("1"));
- }
-}
-
-
-static void
-posts_pagination(bm_ctx_t *ctx, bc_trie_t *variables, const char *variable)
-{
- if (ctx == NULL)
- return; // something is wrong, let's not add any variable
-
- long posts_per_page = strtol(bm_ctx_settings_lookup_str(ctx, variable), NULL, 10);
- if (posts_per_page >= 0) {
- bc_trie_insert(variables, "FILTER_PAGE", bc_strdup("1"));
- bc_trie_insert(variables, "FILTER_PER_PAGE",
- bc_strdup(bm_ctx_settings_lookup(ctx, variable)));
- }
-}
-
-
-static bool
-posts_pagination_enabled(bm_ctx_t *ctx, const char *variable)
-{
- if (ctx == NULL)
- return false;
-
- long posts_per_page = strtol(bm_ctx_settings_lookup_str(ctx, variable), NULL, 10);
- return posts_per_page != 0;
-}
-
-
-// INDEX RULE
-
-static bc_slist_t*
-index_outputlist(bm_ctx_t *ctx)
-{
- if (ctx == NULL || ctx->settings->posts == NULL)
- return NULL;
-
- if (!posts_pagination_enabled(ctx, "posts_per_page"))
- return NULL;
-
- bc_slist_t *rv = NULL;
-
- const char *index_prefix = bm_ctx_settings_lookup(ctx, "index_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- char *f = bm_generate_filename(ctx->short_output_dir, index_prefix, NULL,
- html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
-
- return rv;
-}
-
-static int
-index_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- if (ctx == NULL || ctx->settings->posts == NULL)
- return 0;
-
- int rv = 0;
-
- bc_trie_t *variables = bc_trie_new(free);
- posts_pagination(ctx, variables, "posts_per_page");
- posts_ordering(ctx, variables, "html_order");
- bc_trie_insert(variables, "DATE_FORMAT",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "date_format")));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("index"));
- bc_trie_insert(variables, "MAKE_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->listing_entry_fctx, ctx->main_template_fctx, fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, ctx->listing_entry_fctx,
- ctx->main_template_fctx, fctx, ctx->posts_fctx, 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;
-
- if (!posts_pagination_enabled(ctx, "atom_posts_per_page"))
- return NULL;
-
- bc_slist_t *rv = NULL;
-
- const char *atom_prefix = bm_ctx_settings_lookup(ctx, "atom_prefix");
- const char *atom_ext = bm_ctx_settings_lookup(ctx, "atom_ext");
-
- char *f = bm_generate_filename(ctx->short_output_dir, atom_prefix, NULL,
- atom_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
-
- return rv;
-}
-
-static int
-atom_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- if (ctx == NULL || ctx->settings->posts == NULL)
- return 0;
-
- int rv = 0;
-
- bc_trie_t *variables = bc_trie_new(free);
- posts_pagination(ctx, variables, "atom_posts_per_page");
- posts_ordering(ctx, variables, "atom_order");
- bc_trie_insert(variables, "DATE_FORMAT", bc_strdup("%Y-%m-%dT%H:%M:%SZ"));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("atom"));
- bc_trie_insert(variables, "MAKE_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,
- ctx->atom_template_tmp ? NULL : ctx->atom_template_fctx,
- fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, NULL, ctx->atom_template_fctx,
- fctx, ctx->posts_fctx, 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;
-
- if (!posts_pagination_enabled(ctx, "atom_posts_per_page"))
- return NULL;
-
- bc_slist_t *rv = NULL;
-
- const char *atom_prefix = bm_ctx_settings_lookup(ctx, "atom_prefix");
- const char *atom_ext = bm_ctx_settings_lookup(ctx, "atom_ext");
-
- for (size_t i = 0; ctx->settings->tags[i] != NULL; i++) {
- char *f = bm_generate_filename(ctx->short_output_dir, atom_prefix,
- ctx->settings->tags[i], atom_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
- }
-
- return rv;
-}
-
-static int
-atom_tags_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- 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);
- posts_pagination(ctx, variables, "atom_posts_per_page");
- posts_ordering(ctx, variables, "atom_order");
- bc_trie_insert(variables, "DATE_FORMAT", bc_strdup("%Y-%m-%dT%H:%M:%SZ"));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("atom_tags"));
- bc_trie_insert(variables, "MAKE_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,
- ctx->atom_template_tmp ? NULL : ctx->atom_template_fctx,
- fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, NULL, ctx->atom_template_fctx,
- fctx, ctx->posts_fctx, 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;
-
- if (!posts_pagination_enabled(ctx, "posts_per_page"))
- return NULL;
-
- bc_trie_t *variables = bc_trie_new(free);
- posts_pagination(ctx, variables, "posts_per_page");
-
- char *last_page = bm_exec_blogc_get_variable(ctx, variables, NULL, "LAST_PAGE",
- true, ctx->posts_fctx, false);
-
- bc_trie_free(variables);
-
- if (last_page == NULL)
- return NULL;
-
- long pages = strtol(last_page, NULL, 10);
- free(last_page);
-
- bc_slist_t *rv = NULL;
-
- const char *pagination_prefix = bm_ctx_settings_lookup(ctx, "pagination_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- for (size_t i = 0; i < pages; i++) {
- char *j = bc_strdup_printf("%d", i + 1);
- char *f = bm_generate_filename(ctx->short_output_dir, pagination_prefix,
- j, html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(j);
- free(f);
- }
-
- return rv;
-}
-
-static int
-pagination_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- if (ctx == NULL || ctx->settings->posts == NULL)
- return 0;
-
- int rv = 0;
- size_t page = 1;
-
- bc_trie_t *variables = bc_trie_new(free);
- // not using posts_pagination because we set FILTER_PAGE anyway, and the
- // first value inserted in that function would be useless
- bc_trie_insert(variables, "FILTER_PER_PAGE",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "posts_per_page")));
- posts_ordering(ctx, variables, "html_order");
- bc_trie_insert(variables, "DATE_FORMAT",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "date_format")));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("pagination"));
- bc_trie_insert(variables, "MAKE_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->listing_entry_fctx, ctx->main_template_fctx, fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, ctx->listing_entry_fctx,
- ctx->main_template_fctx, fctx, ctx->posts_fctx, false);
- if (rv != 0)
- break;
- }
- }
-
- bc_trie_free(variables);
-
- return rv;
-}
-
-
-// PAGINATION TAGS RULE
-
-static bc_slist_t*
-pagination_tags_outputlist(bm_ctx_t *ctx)
-{
- if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
- return NULL;
-
- if (!posts_pagination_enabled(ctx, "posts_per_page"))
- return NULL;
-
- bc_trie_t *variables = bc_trie_new(free);
- posts_pagination(ctx, variables, "posts_per_page");
-
- const char *tag_prefix = bm_ctx_settings_lookup(ctx, "tag_prefix");
- const char *pagination_prefix = bm_ctx_settings_lookup(ctx, "pagination_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- bc_slist_t *rv = NULL;
-
- for (size_t k = 0; ctx->settings->tags[k] != NULL; k++) {
- bc_trie_t *local = bc_trie_new(free);
- bc_trie_insert(local, "FILTER_TAG", bc_strdup(ctx->settings->tags[k]));
-
- char *last_page = bm_exec_blogc_get_variable(ctx, variables, local,
- "LAST_PAGE", true, ctx->posts_fctx, false);
-
- bc_trie_free(local);
-
- if (last_page == NULL)
- continue;
-
- long pages = strtol(last_page, NULL, 10);
- free(last_page);
-
- for (size_t i = 0; i < pages; i++) {
- char *j = bc_strdup_printf("%d", i + 1);
- char *f = bm_generate_filename2(ctx->short_output_dir, tag_prefix,
- ctx->settings->tags[k], pagination_prefix, j, html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(j);
- free(f);
- }
- }
-
- bc_trie_free(variables);
-
- return rv;
-}
-
-static int
-pagination_tags_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- if (ctx == NULL || ctx->settings->posts == NULL || ctx->settings->tags == NULL)
- return 0;
-
- int rv = 0;
- size_t page = 1;
-
- bc_trie_t *variables = bc_trie_new(free);
- // not using posts_pagination because we set FILTER_PAGE anyway, and the
- // first value inserted in that function would be useless
- bc_trie_insert(variables, "FILTER_PER_PAGE",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "posts_per_page")));
- posts_ordering(ctx, variables, "html_order");
- bc_trie_insert(variables, "DATE_FORMAT",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "date_format")));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("pagination_tags"));
- bc_trie_insert(variables, "MAKE_TYPE", bc_strdup("post"));
-
- const char *tag_prefix = bm_ctx_settings_lookup(ctx, "tag_prefix");
- const char *pagination_prefix = bm_ctx_settings_lookup(ctx, "pagination_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- for (bc_slist_t *l = outputs; l != NULL; l = l->next) {
- bm_filectx_t *fctx = l->data;
- if (fctx == NULL)
- continue;
-
- // this is very expensive, but we don't have another way to detect the
- // tag and page from the file path right now :/
- char *tag = NULL;
- for (size_t i = 0; ctx->settings->tags[i] != NULL; i++) {
- bool b = false;
-
- // it is impossible to have more output files per tag than the whole
- // amount of output pages
- for (size_t k = 1; k <= bc_slist_length(outputs); k++) {
- char *j = bc_strdup_printf("%d", k);
- char *f = bm_generate_filename2(ctx->short_output_dir, tag_prefix,
- ctx->settings->tags[i], pagination_prefix, j, html_ext);
- free(j);
- if (0 == strcmp(fctx->short_path, f)) {
- tag = ctx->settings->tags[i];
- page = k;
- b = true;
- free(f);
- break;
- }
- free(f);
- }
- if (b)
- break;
- }
- if (tag == NULL)
- continue;
-
- bc_trie_insert(variables, "FILTER_TAG", bc_strdup(tag));
- bc_trie_insert(variables, "FILTER_PAGE", bc_strdup_printf("%zu", page));
-
- if (bm_rule_need_rebuild(ctx->posts_fctx, ctx->settings_fctx,
- ctx->listing_entry_fctx, ctx->main_template_fctx, fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, ctx->listing_entry_fctx,
- ctx->main_template_fctx, fctx, ctx->posts_fctx, 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;
-
- bc_slist_t *rv = NULL;
-
- const char *post_prefix = bm_ctx_settings_lookup(ctx, "post_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- for (size_t i = 0; ctx->settings->posts[i] != NULL; i++) {
- char *f = bm_generate_filename(ctx->short_output_dir, post_prefix,
- ctx->settings->posts[i], html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
- }
-
- return rv;
-}
-
-static int
-posts_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- 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(bm_ctx_settings_lookup(ctx, "date_format")));
- posts_ordering(ctx, variables, "html_order");
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("posts"));
- bc_trie_insert(variables, "MAKE_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 *s_fctx = s->data;
- bm_filectx_t *o_fctx = o->data;
- if (o_fctx == NULL)
- continue;
- if (bm_rule_need_rebuild(s, ctx->settings_fctx, NULL,
- ctx->main_template_fctx, o_fctx, true))
- {
- bc_trie_t *local = bc_trie_new(NULL);
- bc_trie_insert(local, "MAKE_SLUG", s_fctx->slug); // no need to copy
- rv = bm_exec_blogc(ctx, variables, local, false, NULL, ctx->main_template_fctx,
- o_fctx, s, true);
- bc_trie_free(local);
- 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;
-
- if (!posts_pagination_enabled(ctx, "posts_per_page"))
- return NULL;
-
- bc_slist_t *rv = NULL;
-
- const char *tag_prefix = bm_ctx_settings_lookup(ctx, "tag_prefix");
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- for (size_t i = 0; ctx->settings->tags[i] != NULL; i++) {
- char *f = bm_generate_filename(ctx->short_output_dir, tag_prefix,
- ctx->settings->tags[i], html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
- }
-
- return rv;
-}
-
-static int
-tags_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- 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);
- posts_pagination(ctx, variables, "posts_per_page");
- posts_ordering(ctx, variables, "html_order");
- bc_trie_insert(variables, "DATE_FORMAT",
- bc_strdup(bm_ctx_settings_lookup_str(ctx, "date_format")));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("tags"));
- bc_trie_insert(variables, "MAKE_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->listing_entry_fctx, ctx->main_template_fctx, fctx, false))
- {
- rv = bm_exec_blogc(ctx, variables, NULL, true, ctx->listing_entry_fctx,
- ctx->main_template_fctx, fctx, ctx->posts_fctx, 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;
-
- bc_slist_t *rv = NULL;
-
- const char *html_ext = bm_ctx_settings_lookup(ctx, "html_ext");
-
- for (size_t i = 0; ctx->settings->pages[i] != NULL; i++) {
- char *f = bm_generate_filename(ctx->short_output_dir, NULL,
- ctx->settings->pages[i], html_ext);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
- }
-
- return rv;
-}
-
-static int
-pages_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- 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(bm_ctx_settings_lookup(ctx, "date_format")));
- bc_trie_insert(variables, "MAKE_RULE", bc_strdup("pages"));
- bc_trie_insert(variables, "MAKE_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 *s_fctx = s->data;
- bm_filectx_t *o_fctx = o->data;
- if (o_fctx == NULL)
- continue;
- if (bm_rule_need_rebuild(s, ctx->settings_fctx, NULL,
- ctx->main_template_fctx, o_fctx, true))
- {
- bc_trie_t *local = bc_trie_new(NULL);
- bc_trie_insert(local, "MAKE_SLUG", s_fctx->slug); // no need to copy
- rv = bm_exec_blogc(ctx, variables, local, false, NULL, ctx->main_template_fctx,
- o_fctx, s, true);
- bc_trie_free(local);
- if (rv != 0)
- break;
- }
- }
-
- bc_trie_free(variables);
-
- return rv;
-}
-
-
-// COPY FILES RULE
-
-static bc_slist_t*
-copy_outputlist(bm_ctx_t *ctx)
-{
- if (ctx == NULL || ctx->settings->copy == NULL)
- return NULL;
-
- bc_slist_t *rv = NULL;
-
- // we iterate over ctx->copy_fctx list instead of ctx->settings->copy,
- // because bm_ctx_new() expands directories into its files, recursively.
- for (bc_slist_t *s = ctx->copy_fctx; s != NULL; s = s->next) {
- char *f = bc_strdup_printf("%s/%s", ctx->short_output_dir,
- ((bm_filectx_t*) s->data)->short_path);
- rv = bc_slist_append(rv, bm_filectx_new(ctx, f, NULL, NULL));
- free(f);
- }
-
- return rv;
-}
-
-static int
-copy_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- if (ctx == NULL || ctx->settings->copy == NULL)
- return 0;
-
- int rv = 0;
-
- bc_slist_t *s, *o;
-
- for (s = ctx->copy_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, NULL, o_fctx, true)) {
- rv = bm_exec_native_cp(s->data, o_fctx, ctx->verbose);
- if (rv != 0)
- break;
- }
- }
-
- return rv;
-}
-
-
-// CLEAN RULE
-
-static int
-clean_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- int rv = 0;
-
- bc_slist_t *files = bm_rule_list_built_files(ctx);
- for (bc_slist_t *l = files; l != NULL; l = l->next) {
- bm_filectx_t *fctx = l->data;
- if (fctx == NULL)
- continue;
-
- if (fctx->readable) {
- rv = bm_exec_native_rm(ctx->output_dir, fctx, ctx->verbose);
- if (rv != 0)
- break;
- }
- }
- bc_slist_free_full(files, (bc_free_func_t) bm_filectx_free);
-
- if (!bm_exec_native_is_empty_dir(ctx->output_dir, NULL)) {
- fprintf(stderr, "blogc-make: warning: output directory is not empty!\n");
- }
-
- return rv;
-}
-
-
-static int all_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args);
-
-
-// RUNSERVER RULE
-
-static int
-runserver_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- return bm_httpd_run(&ctx, all_exec, outputs, args);
-}
-
-
-// WATCH RULE
-
-static int
-watch_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- return bm_reloader_run(&ctx, all_exec, outputs, args);
-}
-
-
-// ATOM DUMP RULE
-
-static int
-atom_dump_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- char *content = bm_atom_generate(ctx->settings);
- if (content == NULL)
- return 1;
- printf("%s", content);
- free(content);
- return 0;
-}
-
-
-const bm_rule_t rules[] = {
- {
- .name = "all",
- .help = "run all build rules",
- .outputlist_func = NULL,
- .exec_func = all_exec,
- },
- {
- .name = "index",
- .help = "build website index from posts",
- .outputlist_func = index_outputlist,
- .exec_func = index_exec,
- },
- {
- .name = "atom",
- .help = "build main atom feed from posts",
- .outputlist_func = atom_outputlist,
- .exec_func = atom_exec,
- },
- {
- .name = "atom_tags",
- .help = "build atom feeds for each tag from posts",
- .outputlist_func = atom_tags_outputlist,
- .exec_func = atom_tags_exec,
- },
- {
- .name = "pagination",
- .help = "build pagination pages from posts",
- .outputlist_func = pagination_outputlist,
- .exec_func = pagination_exec,
- },
- {
- .name = "pagination_tags",
- .help = "build pagination pages for each tag from posts",
- .outputlist_func = pagination_tags_outputlist,
- .exec_func = pagination_tags_exec,
- },
- {
- .name = "posts",
- .help = "build individual pages for each post",
- .outputlist_func = posts_outputlist,
- .exec_func = posts_exec,
- },
- {
- .name = "tags",
- .help = "build post listings for each tag from posts",
- .outputlist_func = tags_outputlist,
- .exec_func = tags_exec,
- },
- {
- .name = "pages",
- .help = "build individual pages for each page",
- .outputlist_func = pages_outputlist,
- .exec_func = pages_exec,
- },
- {
- .name = "copy",
- .help = "copy static files from source directory to output directory",
- .outputlist_func = copy_outputlist,
- .exec_func = copy_exec,
- },
- {
- .name = "clean",
- .help = "clean built files and empty directories in output directory",
- .outputlist_func = NULL,
- .exec_func = clean_exec,
- },
- {
- .name = "runserver",
- .help = "run blogc-runserver pointing to output directory, if available,\n"
- " rebuilding as needed\n"
- " arguments: host (127.0.0.1), port (8080) and threads (20)",
- .outputlist_func = NULL,
- .exec_func = runserver_exec,
- },
- {
- .name = "watch",
- .help = "watch for changes in the source files, rebuilding as needed",
- .outputlist_func = NULL,
- .exec_func = watch_exec,
- },
- {
- .name = "atom_dump",
- .help = "dump default Atom feed template based on current settings",
- .outputlist_func = NULL,
- .exec_func = atom_dump_exec,
- },
- {NULL, NULL, NULL, NULL},
-};
-
-
-// ALL RULE
-
-static int
-all_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)
-{
- for (size_t i = 0; rules[i].name != NULL; i++) {
- if (rules[i].outputlist_func == NULL) {
- continue;
- }
-
- int rv = bm_rule_execute(ctx, &(rules[i]), NULL);
- if (rv != 0) {
- return rv;
- }
- }
-
- return 0;
-}
-
-
-bc_trie_t*
-bm_rule_parse_args(const char *sep)
-{
- if (sep == NULL || *sep == '\0' || *sep != ':')
- return NULL;
-
- bc_trie_t *rv = bc_trie_new(free);
- char *end = (char*) sep + 1;
- char *kv_sep;
- while (NULL != (kv_sep = strchr(end, '='))) {
- char *key = bc_strndup(end, kv_sep - end);
- end = kv_sep + 1;
- kv_sep = strchr(end, ',');
- if (kv_sep == NULL)
- kv_sep = strchr(end, '\0');
- char *value = bc_strndup(end, kv_sep - end);
- bc_trie_insert(rv, key, value);
- free(key);
- if (*kv_sep == '\0')
- break;
- end = kv_sep + 1;
- }
- if (kv_sep == NULL) {
- bc_trie_free(rv);
- return NULL;
- }
-
- return rv;
-}
-
-
-int
-bm_rule_executor(bm_ctx_t *ctx, bc_slist_t *rule_list)
-{
- if (ctx == NULL)
- return 1;
-
- const bm_rule_t *rule = NULL;
- int rv = 0;
-
- for (bc_slist_t *l = rule_list; l != NULL; l = l->next) {
-
- char *rule_str = l->data;
- char *sep = strchr(rule_str, ':');
-
- bc_trie_t *args = NULL;
- if (sep == NULL) {
- sep = strchr(rule_str, '\0');
- }
- else {
- args = bm_rule_parse_args(sep);
- if (args == NULL) {
- fprintf(stderr, "blogc-make: warning: failed to parse rule "
- "arguments, ignoring: %s\n", rule_str);
- }
- }
-
- rule = NULL;
- for (size_t i = 0; rules[i].name != NULL; i++) {
- if (strlen(rules[i].name) != (sep - rule_str))
- continue;
- if (0 == strncmp(rule_str, rules[i].name, sep - rule_str)) {
- rule = &(rules[i]);
- rv = bm_rule_execute(ctx, rule, args);
- if (rv != 0)
- return rv;
- break;
- }
- }
- if (rule == NULL) {
- fprintf(stderr, "blogc-make: error: rule not found: %.*s\n",
- (int) (sep - rule_str), rule_str);
- rv = 1;
- }
- }
-
- return rv;
-}
-
-
-int
-bm_rule_execute(bm_ctx_t *ctx, const bm_rule_t *rule, bc_trie_t *args)
-{
- if (ctx == NULL || rule == NULL)
- return 1;
-
- bc_slist_t *outputs = NULL;
- if (rule->outputlist_func != NULL) {
- outputs = rule->outputlist_func(ctx);
- }
-
- int rv = rule->exec_func(ctx, outputs, args);
-
- 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 *listing_entry, 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);
- if (listing_entry != NULL)
- s = bc_slist_append(s, listing_entry);
-
- 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->tv_sec == output->tv_sec) {
- if (source->tv_nsec > output->tv_nsec) {
- rv = true;
- break;
- }
- }
- else if (source->tv_sec > output->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].outputlist_func == NULL) {
- 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("\nhelper rules:\n");
- for (size_t i = 0; rules[i].name != NULL; i++) {
- if (rules[i].outputlist_func == NULL) {
- printf(" %-15s %s\n", rules[i].name, rules[i].help);
- }
- }
- printf("\nbuild rules:\n");
- for (size_t i = 0; rules[i].name != NULL; i++) {
- if (rules[i].outputlist_func != NULL) {
- printf(" %-15s %s\n", rules[i].name, rules[i].help);
- }
- }
-}
diff --git a/src/blogc-make/rules.h b/src/blogc-make/rules.h
deleted file mode 100644
index 9ff39ca..0000000
--- a/src/blogc-make/rules.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_RULES_H
-#define _MAKE_RULES_H
-
-#include <stdbool.h>
-#include "ctx.h"
-#include "../common/utils.h"
-
-typedef bc_slist_t* (*bm_rule_outputlist_func_t) (bm_ctx_t *ctx);
-typedef int (*bm_rule_exec_func_t) (bm_ctx_t *ctx, bc_slist_t *outputs,
- bc_trie_t *args);
-
-typedef struct {
- const char *name;
- const char *help;
- bm_rule_outputlist_func_t outputlist_func;
- bm_rule_exec_func_t exec_func;
-} bm_rule_t;
-
-bc_trie_t* bm_rule_parse_args(const char *sep);
-int bm_rule_executor(bm_ctx_t *ctx, bc_slist_t *rule_list);
-int bm_rule_execute(bm_ctx_t *ctx, const bm_rule_t *rule, bc_trie_t *args);
-bool bm_rule_need_rebuild(bc_slist_t *sources, bm_filectx_t *settings,
- bm_filectx_t *listing_entry, bm_filectx_t *template, bm_filectx_t *output,
- bool only_first_source);
-bc_slist_t* bm_rule_list_built_files(bm_ctx_t *ctx);
-void bm_rule_print_help(void);
-
-#endif /* _MAKE_RULES_H */
diff --git a/src/blogc-make/settings.c b/src/blogc-make/settings.c
deleted file mode 100644
index 1079fc0..0000000
--- a/src/blogc-make/settings.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <libgen.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include "../common/config-parser.h"
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "settings.h"
-
-
-static const struct default_settings_map {
- const char *key;
- const char *default_value;
-} default_settings[] = {
-
- // source
- {"content_dir", "content"},
- {"template_dir", "templates"},
- {"atom_template", NULL}, // default: atom.c
- {"main_template", "main.tmpl"},
- {"source_ext", ".txt"},
- {"listing_entry", NULL},
- {"posts_sort", NULL},
-
- // pagination
- {"pagination_prefix", "page"},
- {"posts_per_page", "10"},
- {"atom_posts_per_page", "10"},
-
- // html
- {"html_ext", "/index.html"},
- {"index_prefix", ""},
- {"post_prefix", "post"},
- {"tag_prefix", "tag"},
- {"html_order", "DESC"},
-
- // atom
- {"atom_prefix", "atom"},
- {"atom_ext", ".xml"},
- {"atom_order", "DESC"},
- {"atom_legacy_entry_id", NULL},
-
- // generic
- {"date_format", "%b %d, %Y, %I:%M %p GMT"},
- {"locale", NULL},
-
- {NULL, NULL},
-};
-
-
-static const char* required_global[] = {
- "AUTHOR_NAME",
- "AUTHOR_EMAIL",
- "SITE_TITLE",
- "SITE_TAGLINE",
- "BASE_DOMAIN",
- NULL,
-};
-
-
-static const char* list_sections[] = {
- "posts",
- "pages",
- "copy",
- "copy_files", // backward compatibility
- "tags",
- NULL,
-};
-
-
-bm_settings_t*
-bm_settings_parse(const char *content, size_t content_len, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- if (content == NULL)
- return NULL;
-
- bc_config_t *config = bc_config_parse(content, content_len, list_sections,
- err);
- if (config == NULL || (err != NULL && *err != NULL))
- return NULL;
-
- bm_settings_t *rv = bc_malloc(sizeof(bm_settings_t));
- rv->global = bc_trie_new(free);
- rv->settings = bc_trie_new(free);
- rv->posts = NULL;
- rv->pages = NULL;
- rv->copy = NULL;
- rv->tags = NULL;
-
- // this is some code for compatibility with the [environment] section,
- // even if I never released a version with it, but some people is using
- // it already.
- const char *section = NULL;
- char **global = bc_config_list_keys(config, "global");
- if (global != NULL) {
- section = "global";
- }
- else {
- global = bc_config_list_keys(config, "environment");
- if (global != NULL) {
- section = "environment";
- }
- else {
- section = "global";
- }
- }
-
- if (global != NULL) {
- for (size_t i = 0; global[i] != NULL; i++) {
- for (size_t j = 0; global[i][j] != '\0'; j++) {
- if (j == 0) {
- if (!(global[i][j] >= 'A' && global[i][j] <= 'Z')) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_SETTINGS,
- "Invalid [%s] key (first character must be uppercase): %s",
- section, global[i]);
- bc_strv_free(global);
- bm_settings_free(rv);
- rv = NULL;
- goto cleanup;
- }
- continue;
- }
- if (!((global[i][j] >= 'A' && global[i][j] <= 'Z') ||
- (global[i][j] >= '0' && global[i][j] <= '9') ||
- global[i][j] == '_')) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_SETTINGS,
- "Invalid [%s] key (must be uppercase with '_' and digits after first character): %s",
- section, global[i]);
- bc_strv_free(global);
- bm_settings_free(rv);
- rv = NULL;
- goto cleanup;
- }
- }
- bc_trie_insert(rv->global, global[i],
- bc_strdup(bc_config_get(config, section, global[i])));
- }
- }
- bc_strv_free(global);
-
- for (size_t i = 0; required_global[i] != NULL; i++) {
- const char *value = bc_trie_lookup(rv->global, required_global[i]);
- if (value == NULL || value[0] == '\0') {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_SETTINGS,
- "[%s] key required but not found or empty: %s", section,
- required_global[i]);
- bm_settings_free(rv);
- rv = NULL;
- goto cleanup;
- }
- }
-
- for (size_t i = 0; default_settings[i].key != NULL; i++) {
- const char *value = bc_config_get_with_default(
- config, "settings", default_settings[i].key,
- default_settings[i].default_value);
- if (value != NULL) {
- bc_trie_insert(rv->settings, default_settings[i].key,
- bc_strdup(value));
- }
- }
-
- rv->posts = bc_config_get_list(config, "posts");
- rv->pages = bc_config_get_list(config, "pages");
- rv->tags = bc_config_get_list(config, "tags");
-
- // this is for backward compatibility too.
- rv->copy = bc_config_get_list(config, "copy");
- if (rv->copy == NULL)
- rv->copy = bc_config_get_list(config, "copy_files");
-
-cleanup:
-
- bc_config_free(config);
-
- return rv;
-}
-
-
-void
-bm_settings_free(bm_settings_t *settings)
-{
- if (settings == NULL)
- return;
- bc_trie_free(settings->global);
- bc_trie_free(settings->settings);
- bc_strv_free(settings->posts);
- bc_strv_free(settings->pages);
- bc_strv_free(settings->copy);
- bc_strv_free(settings->tags);
- free(settings);
-}
diff --git a/src/blogc-make/settings.h b/src/blogc-make/settings.h
deleted file mode 100644
index 69fdbb6..0000000
--- a/src/blogc-make/settings.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_SETTINGS_H
-#define _MAKE_SETTINGS_H
-
-#include <stddef.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-
-typedef struct {
- bc_trie_t *global;
- bc_trie_t *settings;
- char **posts;
- char **pages;
- char **copy;
- char **tags;
-} bm_settings_t;
-
-bm_settings_t* bm_settings_parse(const char *content, size_t content_len,
- bc_error_t **err);
-void bm_settings_free(bm_settings_t *settings);
-
-#endif /* _MAKE_SETTINGS_H */
diff --git a/src/blogc-make/utils.c b/src/blogc-make/utils.c
deleted file mode 100644
index 8f69e44..0000000
--- a/src/blogc-make/utils.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <errno.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-
-char*
-bm_generate_filename(const char *dir, const char *prefix, const char *fname,
- const char *ext)
-{
- bool have_prefix = prefix != NULL && prefix[0] != '\0';
- bool have_fname = fname != NULL && fname[0] != '\0';
- bool have_ext = ext != NULL && ext[0] != '\0';
- bool have_ext_noslash = have_ext && ext[0] != '/';
- bool is_index = have_fname && have_ext && (
- (0 == strcmp(fname, "index")) && ext[0] == '/') && !have_prefix;
-
- bc_string_t *rv = bc_string_new();
-
- if (dir != NULL && (have_prefix || have_fname || have_ext))
- bc_string_append(rv, dir);
-
- if ((have_prefix || have_fname || have_ext_noslash) && !is_index)
- bc_string_append_c(rv, '/');
-
- if (have_prefix)
- bc_string_append(rv, prefix);
-
- // with fname we have posts, pages and tags
- if (have_fname) {
- if (have_prefix && have_fname && fname[0] != '/')
- bc_string_append_c(rv, '/');
- if (!is_index)
- bc_string_append(rv, fname);
- }
-
- // no fname means index
- else if (have_ext_noslash) {
- if (have_fname)
- bc_string_append_c(rv, '/');
- if (!have_prefix)
- bc_string_append(rv, "index");
- }
-
- if (have_ext)
- bc_string_append(rv, ext);
-
- if (rv->len == 0) {
- bc_string_free(rv, true);
- return NULL;
- }
-
- return bc_string_free(rv, false);
-}
-
-
-char*
-bm_generate_filename2(const char *dir, const char *prefix, const char *fname,
- const char *prefix2, const char *fname2, const char *ext)
-{
- bool have_prefix = prefix != NULL && prefix[0] != '\0';
- bool have_fname = fname != NULL && fname[0] != '\0';
- bool have_prefix2 = prefix2 != NULL && prefix2[0] != '\0';
-
- bc_string_t *p = bc_string_new();
-
- if (have_prefix)
- bc_string_append(p, prefix);
-
- if (have_prefix && (have_fname || have_prefix2))
- bc_string_append_c(p, '/');
-
- if (have_fname)
- bc_string_append(p, fname);
-
- if (have_fname && have_prefix2)
- bc_string_append_c(p, '/');
-
- if (have_prefix2)
- bc_string_append(p, prefix2);
-
- char *rv = bm_generate_filename(dir, p->str, fname2, ext);
- bc_string_free(p, true);
-
- return rv;
-}
-
-
-char*
-bm_abspath(const char *path, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- if (path[0] == '/') {
- return bc_strdup(path);
- }
-
- char cwd[PATH_MAX];
- if (NULL == getcwd(cwd, sizeof(cwd))) {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_UTILS,
- "Failed to detect absolute path (%s): %s", path, strerror(errno));
- return NULL;
- }
-
- if (cwd[0] != '/') {
- *err = bc_error_new_printf(BLOGC_MAKE_ERROR_UTILS,
- "Failed to get current working directory: %s", cwd);
- return NULL;
- }
-
- return bc_strdup_printf("%s/%s", cwd, path);
-}
diff --git a/src/blogc-make/utils.h b/src/blogc-make/utils.h
deleted file mode 100644
index cbcfc0e..0000000
--- a/src/blogc-make/utils.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MAKE_UTILS_H
-#define _MAKE_UTILS_H
-
-#include "../common/error.h"
-
-char* bm_generate_filename(const char *dir, const char *prefix, const char *fname,
- const char *ext);
-char* bm_generate_filename2(const char *dir, const char *prefix, const char *fname,
- const char *prefix2, const char *fname2, const char *ext);
-char* bm_abspath(const char *path, bc_error_t **err);
-
-#endif /* _MAKE_UTILS_H */
diff --git a/src/blogc-runserver.c b/src/blogc-runserver.c
new file mode 100644
index 0000000..2fbbbc7
--- /dev/null
+++ b/src/blogc-runserver.c
@@ -0,0 +1,386 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <event2/event.h>
+#include <event2/http.h>
+#include <event2/buffer.h>
+#include <magic.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "utils.h"
+
+
+/**
+ * this mapping is used to declare "supported" file types, that are forced over
+ * whatever detected by libmagic, but we will still use the charset provided by
+ * libmagic anyway. it also helps detecting index files when the client asks
+ * for a directory.
+ */
+static const struct content_type_map {
+ const char *mimetype;
+ const char *extension;
+ const char *index;
+} content_types[] = {
+ {"text/html", "html", "index.html"},
+ {"text/html", "htm", "index.htm"},
+ {"text/xml", "xml", "index.xml"},
+ {"text/plain", "txt", "index.txt"},
+ {"text/css", "css", NULL},
+ {"application/javascript", "js", NULL},
+ {NULL, NULL, NULL}
+};
+
+
+static magic_t magic_all = NULL;
+static magic_t magic_charset = NULL;
+
+
+static const char*
+get_extension(const char *filename)
+{
+ const char *ext = NULL;
+ unsigned int i;
+ for (i = strlen(filename); i > 0; i--) {
+ if (filename[i] == '.') {
+ ext = filename + i + 1;
+ break;
+ }
+ }
+ if (i == 0)
+ return NULL;
+ return ext;
+}
+
+
+static char*
+guess_content_type(const char *filename, int fd)
+{
+ int newfd;
+
+ // try "supported" types first, and just use libmagic for charset
+ const char *extension = get_extension(filename);
+ if (extension == NULL)
+ goto libmagic;
+ const char *supported = NULL;
+ for (unsigned int i = 0; content_types[i].extension != NULL; i++)
+ if (0 == strcmp(content_types[i].extension, extension))
+ supported = content_types[i].mimetype;
+ if (supported != NULL) {
+ newfd = dup(fd);
+ if (-1 != newfd) {
+ const char* charset = magic_descriptor(magic_charset, newfd);
+ close(newfd);
+ if (charset != NULL)
+ return sb_strdup_printf("%s; charset=%s", supported, charset);
+ }
+ return sb_strdup(supported);
+ }
+
+libmagic:
+
+ // fallback to use libmagic for everything
+ newfd = dup(fd);
+ if (-1 != newfd) {
+ const char* content_type = magic_descriptor(magic_all, newfd);
+ close(newfd);
+ if (content_type != NULL)
+ return sb_strdup(content_type);
+ }
+ return sb_strdup("application/octet-stream");
+}
+
+
+static void
+handler(struct evhttp_request *request, void *ptr)
+{
+ const char *root = ptr;
+ const char *uri = evhttp_request_get_uri(request);
+
+ struct evhttp_uri *decoded_uri = evhttp_uri_parse(uri);
+ if (decoded_uri == NULL) {
+ evhttp_send_error(request, 400, "Bad request");
+ return;
+ }
+
+ const char *path = evhttp_uri_get_path(decoded_uri);
+ if (path == NULL)
+ path = "/";
+
+ char *decoded_path = evhttp_uridecode(path, 0, NULL);
+ if (decoded_path == NULL) {
+ evhttp_send_error(request, 400, "Bad request");
+ goto point1;
+ }
+
+ char *abs_path = sb_strdup_printf("%s/%s", root, decoded_path);
+ char *real_path = realpath(abs_path, NULL);
+ free(abs_path);
+
+ if (real_path == NULL) {
+ evhttp_send_error(request, 404, "Not found");
+ goto point2;
+ }
+
+ char *real_root = realpath(root, NULL);
+ if (real_root == NULL) {
+ evhttp_send_error(request, 500, "Internal server error");
+ goto point3;
+ }
+
+ if (0 != strncmp(real_root, real_path, strlen(real_root))) {
+ evhttp_send_error(request, 404, "Not found");
+ goto point4;
+ }
+
+ struct stat st;
+ if (0 > stat(real_path, &st)) {
+ evhttp_send_error(request, 404, "Not found");
+ goto point4;
+ }
+
+ bool add_slash = false;
+
+ if (S_ISDIR(st.st_mode)) {
+ char *found = NULL;
+
+ for (unsigned int i = 0; content_types[i].mimetype != NULL; i++) {
+ if (content_types[i].index == NULL)
+ continue;
+ char *f = sb_strdup_printf("%s/%s", real_path,
+ content_types[i].index);
+ if (0 == access(f, F_OK)) {
+ found = sb_strdup(f);
+ break;
+ }
+ free(f);
+ }
+
+ if (found == NULL) {
+ evhttp_send_error(request, 403, "Forbidden");
+ goto point4;
+ }
+
+ size_t path_len = strlen(path);
+ if (path_len > 0 && path[path_len - 1] != '/')
+ add_slash = true;
+
+ free(real_path);
+ real_path = found;
+ }
+
+ int fd;
+ if ((fd = open(real_path, O_RDONLY)) < 0) {
+ evhttp_send_error(request, 500, "Internal server error");
+ goto point4;
+ }
+
+ char *type = guess_content_type(real_path, fd);
+
+ if (fstat(fd, &st) < 0) {
+ evhttp_send_error(request, 500, "Internal server error");
+ goto point5;
+ }
+
+ struct evkeyvalq *headers = evhttp_request_get_output_headers(request);
+
+ if (add_slash) {
+ char *tmp = sb_strdup_printf("%s/", path);
+ evhttp_add_header(headers, "Location", tmp);
+ free(tmp);
+ // production webservers usually returns 301 in such cases, but 302 is
+ // better for development/testing.
+ evhttp_send_reply(request, 302, "Found", NULL);
+ goto point5;
+ }
+
+ evhttp_add_header(headers, "Content-Type", type);
+ char *content_length = sb_strdup_printf("%zu", st.st_size);
+ evhttp_add_header(headers, "Content-Length", content_length);
+ free(content_length);
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_file(evb, fd, 0, st.st_size);
+ evhttp_send_reply(request, 200, "OK", evb);
+
+point5:
+ free(type);
+point4:
+ free(real_root);
+point3:
+ free(real_path);
+point2:
+ free(decoded_path);
+point1:
+ evhttp_uri_free(decoded_uri);
+}
+
+
+static int
+runserver(const char *address, unsigned short port, const char *root)
+{
+ struct event_base *base = event_base_new();
+ if (base == NULL) {
+ fprintf(stderr, "error: failed to initialize event base\n");
+ return 1;
+ }
+
+ struct evhttp *http = evhttp_new(base);
+ if (http == NULL) {
+ fprintf(stderr, "error: failed to initialize HTTP server\n");
+ return 1;
+ }
+
+ evhttp_set_gencb(http, handler, (char*) root);
+
+ evhttp_set_allowed_methods(http, EVHTTP_REQ_GET | EVHTTP_REQ_HEAD);
+
+ if (0 != evhttp_bind_socket(http, address, port)) {
+ fprintf(stderr, "error: failed to bind socket to %s:%d\n", address,
+ port);
+ return 1;
+ }
+
+ fprintf(stderr, " * Running on http://%s:%d/\n", address, port);
+
+ event_base_dispatch(base);
+
+ return 0;
+}
+
+
+static void
+print_help(void)
+{
+ printf(
+ "usage:\n"
+ " blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n"
+ " - A simple HTTP server to test blogc websites.\n"
+ "\n"
+ "positional arguments:\n"
+ " DOCROOT document root directory\n"
+ "\n"
+ "optional arguments:\n"
+ " -h show this help message and exit\n"
+ " -v show version and exit\n"
+ " -t HOST set server listen address (default: 127.0.0.1)\n"
+ " -p PORT set server listen port (default: 8080)\n");
+}
+
+
+static void
+print_usage(void)
+{
+ printf("usage: blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n");
+}
+
+
+int
+main(int argc, char **argv)
+{
+ signal(SIGPIPE, SIG_IGN);
+
+ int rv = 0;
+ char *host = NULL;
+ char *docroot = NULL;
+ unsigned short port = 8080;
+
+ unsigned int args = 0;
+
+ for (unsigned int i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'h':
+ print_help();
+ goto cleanup;
+ case 'v':
+ printf("%s\n", PACKAGE_STRING);
+ goto cleanup;
+ case 't':
+ if (argv[i][2] != '\0')
+ host = sb_strdup(argv[i] + 2);
+ else
+ host = sb_strdup(argv[++i]);
+ break;
+ case 'p':
+ if (argv[i][2] != '\0')
+ port = strtoul(argv[i] + 2, NULL, 10);
+ else
+ port = strtoul(argv[++i], NULL, 10);
+ break;
+ default:
+ print_usage();
+ fprintf(stderr, "blogc-runserver: error: invalid "
+ "argument: -%c\n", argv[i][1]);
+ rv = 2;
+ goto cleanup;
+ }
+ }
+ else {
+ if (args > 0) {
+ print_usage();
+ fprintf(stderr, "blogc-runserver: error: only one positional "
+ "argument allowed\n");
+ rv = 2;
+ goto cleanup;
+ }
+ args++;
+ docroot = sb_strdup(argv[i]);
+ }
+ }
+
+ if (docroot == NULL) {
+ print_usage();
+ fprintf(stderr, "blogc-runserver: error: document root directory "
+ "required\n");
+ rv = 2;
+ goto cleanup;
+ }
+
+ if (host == NULL)
+ host = sb_strdup("127.0.0.1");
+
+ magic_all = magic_open(MAGIC_MIME);
+ magic_charset = magic_open(MAGIC_MIME_ENCODING);
+ if (magic_all == NULL || magic_charset == NULL) {
+ fprintf(stderr, "error: failed to initialize libmagic\n");
+ rv = 1;
+ goto cleanup;
+ }
+
+ if ((0 != magic_load(magic_all, NULL)) ||
+ (0 != magic_load(magic_charset, NULL)))
+ {
+ fprintf(stderr, "error: failed to load libmagic data\n");
+ magic_close(magic_all);
+ magic_close(magic_charset);
+ rv = 1;
+ goto cleanup;
+ }
+
+ rv = runserver(host, port, docroot);
+
+ magic_close(magic_all);
+ magic_close(magic_charset);
+
+cleanup:
+ free(host);
+ free(docroot);
+
+ return rv;
+}
diff --git a/src/blogc-runserver/httpd-utils.c b/src/blogc-runserver/httpd-utils.c
deleted file mode 100644
index 9d374ae..0000000
--- a/src/blogc-runserver/httpd-utils.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 <string.h>
-#include <unistd.h>
-#include "../common/utils.h"
-#include "httpd-utils.h"
-
-
-char*
-br_readline(int socket)
-{
- bc_string_t *rv = bc_string_new();
- char buffer[READLINE_BUFFER_SIZE];
- ssize_t len;
- bool end = false;
-
- while ((len = read(socket, buffer, READLINE_BUFFER_SIZE)) > 0) {
- if (!end) {
- for (ssize_t i = 0; i < len; i++) {
- if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == '\0') {
- // we finished "recording", but still need to exhaust
- // request data.
- end = true;
- break;
- }
- bc_string_append_c(rv, buffer[i]);
- }
- }
- if (len < READLINE_BUFFER_SIZE) {
- break;
- }
- }
-
- return bc_string_free(rv, false);
-}
-
-
-int
-br_hextoi(const char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- return -1;
-}
-
-
-char*
-br_urldecode(const char *str)
-{
- bc_string_t *rv = bc_string_new();
-
- for (size_t i = 0; i < strlen(str); i++) {
- switch (str[i]) {
- case '%':
- if (i + 2 < strlen(str)) {
- int p1 = br_hextoi(str[i + 1]) * 16;
- int p2 = br_hextoi(str[i + 2]);
- if (p1 >= 0 && p2 >= 0) {
- bc_string_append_c(rv, p1 + p2);
- i += 2;
- continue;
- }
- }
- bc_string_append_c(rv, '%');
- break;
- case '+':
- bc_string_append_c(rv, ' ');
- break;
- default:
- bc_string_append_c(rv, str[i]);
- }
- }
-
- return bc_string_free(rv, false);
-}
-
-
-const char*
-br_get_extension(const char *filename)
-{
- const char *ext = NULL;
- size_t i;
- for (i = strlen(filename); i > 0; i--) {
- if (filename[i] == '.') {
- ext = filename + i + 1;
- break;
- }
- if ((filename[i] == '/') || (filename[i] == '\\'))
- return NULL;
- }
- if (i == 0)
- return NULL;
- return ext;
-}
diff --git a/src/blogc-runserver/httpd-utils.h b/src/blogc-runserver/httpd-utils.h
deleted file mode 100644
index 06ad6e1..0000000
--- a/src/blogc-runserver/httpd-utils.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _HTTPD_UTILS_H
-#define _HTTPD_UTILS_H
-
-#define READLINE_BUFFER_SIZE 2048
-
-char* br_readline(int socket);
-int br_hextoi(const char c);
-char* br_urldecode(const char *str);
-const char* br_get_extension(const char *filename);
-
-#endif /* _HTTPD_UTILS_H */
diff --git a/src/blogc-runserver/httpd.c b/src/blogc-runserver/httpd.c
deleted file mode 100644
index fe98288..0000000
--- a/src/blogc-runserver/httpd.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <pthread.h>
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "mime.h"
-#include "httpd-utils.h"
-
-#define LISTEN_BACKLOG 100
-
-typedef struct {
- pthread_t thread;
- bool initialized;
-} thread_data_t;
-
-typedef struct {
- size_t thread_id;
- int socket;
- char *ip;
- const char *docroot;
-} request_data_t;
-
-
-static void
-error(int socket, int status_code, const char *error)
-{
- char *str = bc_strdup_printf(
- "HTTP/1.0 %d %s\r\n"
- "Content-Type: text/html\r\n"
- "Content-Length: %zu\r\n"
- "Connection: close\r\n"
- "\r\n"
- "<h1>%s</h1>\n", status_code, error, strlen(error) + 10, error);
- size_t str_len = strlen(str);
- if (str_len != write(socket, str, str_len)) {
- fprintf(stderr, "warning: Failed to write full response header!\n");
- }
- free(str);
-}
-
-
-static void*
-handle_request(void *arg)
-{
- request_data_t *req = arg;
- size_t thread_id = req->thread_id;
- int client_socket = req->socket;
- char *ip = req->ip;
- const char *docroot = req->docroot;
- free(arg);
-
- char *conn_line = br_readline(client_socket);
- if (conn_line == NULL || conn_line[0] == '\0')
- goto point0;
-
- unsigned short status_code = 200;
-
- char **pieces = bc_str_split(conn_line, ' ', 3);
- if (bc_strv_length(pieces) != 3) {
- status_code = 400;
- error(client_socket, 400, "Bad Request");
- goto point1;
- }
-
- if (strcmp(pieces[0], "GET") != 0) {
- status_code = 405;
- error(client_socket, 405, "Method Not Allowed");
- goto point1;
- }
-
- char **pieces2 = bc_str_split(pieces[1], '?', 2);
- char *path = br_urldecode(pieces2[0]);
- bc_strv_free(pieces2);
-
- if (path == NULL) {
- status_code = 400;
- error(client_socket, 400, "Bad Request");
- goto point2;
- }
-
- char *abs_path = bc_strdup_printf("%s/%s", docroot, path);
- char *real_path = realpath(abs_path, NULL);
- free(abs_path);
-
- if (real_path == NULL) {
- if (errno == ENOENT) {
- status_code = 404;
- error(client_socket, 404, "Not Found");
- }
- else {
- status_code = 500;
- error(client_socket, 500, "Internal Server Error");
- }
- goto point2;
- }
-
- char *real_root = realpath(docroot, NULL);
- if (real_root == NULL) {
- status_code = 500;
- error(client_socket, 500, "Internal Server Error");
- goto point3;
- }
-
- if (0 != strncmp(real_root, real_path, strlen(real_root))) {
- status_code = 404;
- error(client_socket, 404, "Not Found");
- goto point4;
- }
-
- struct stat st;
- if (0 > stat(real_path, &st)) {
- status_code = 404;
- error(client_socket, 404, "Not Found");
- goto point4;
- }
-
- bool add_slash = false;
-
- if (S_ISDIR(st.st_mode)) {
- char *found = br_mime_guess_index(real_path);
-
- if (found == NULL) {
- status_code = 403;
- error(client_socket, 403, "Forbidden");
- goto point4;
- }
-
- size_t path_len = strlen(path);
- if (path_len > 0 && path[path_len - 1] != '/')
- add_slash = true;
-
- free(real_path);
- real_path = found;
- }
-
- if (0 != access(real_path, F_OK)) {
- status_code = 500;
- error(client_socket, 500, "Internal Server Error");
- goto point4;
- }
-
- if (add_slash) {
- // production webservers usually returns 301 in such cases, but 302 is
- // better for development/testing.
- char *tmp = bc_strdup_printf(
- "HTTP/1.0 302 Found\r\n"
- "Location: %s/\r\n"
- "Content-Length: 0\r\n"
- "Connection: close\r\n"
- "\r\n", path);
- status_code = 302;
- size_t tmp_len = strlen(tmp);
- if (tmp_len != write(client_socket, tmp, tmp_len)) {
- fprintf(stderr, "warning: Failed to write full response header!\n");
- }
- free(tmp);
- goto point4;
- }
-
- size_t len;
- bc_error_t *err = NULL;
- char* contents = bc_file_get_contents(real_path, false, &len, &err);
- if (err != NULL) {
- status_code = 500;
- error(client_socket, 500, "Internal Server Error");
- bc_error_free(err);
- goto point4;
- }
-
- char *out = bc_strdup_printf(
- "HTTP/1.0 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: %zu\r\n"
- "Connection: close\r\n"
- "\r\n", br_mime_guess_content_type(real_path), len);
- size_t out_len = strlen(out);
- if (out_len != write(client_socket, out, out_len)) {
- fprintf(stderr, "warning: Failed to write full response header!\n");
- }
- free(out);
-
- if (len != write(client_socket, contents, len)) {
- fprintf(stderr, "warning: Failed to write full response body!\n");
- }
- free(contents);
-
-point4:
- free(real_root);
-point3:
- free(real_path);
-point2:
- free(path);
-point1:
- fprintf(stderr, "[Thread-%zu] %s - - \"%s\" %d\n", thread_id + 1,
- ip, conn_line, status_code);
- free(conn_line);
- bc_strv_free(pieces);
-point0:
- free(ip);
- close(client_socket);
- return NULL;
-}
-
-
-static char*
-br_httpd_get_ip(int af, const struct sockaddr *addr)
-{
- char host[INET6_ADDRSTRLEN];
- if (af == AF_INET6) {
- struct sockaddr_in6 *a = (struct sockaddr_in6*) addr;
- inet_ntop(af, &(a->sin6_addr), host, INET6_ADDRSTRLEN);
- }
- else {
- struct sockaddr_in *a = (struct sockaddr_in*) addr;
- inet_ntop(af, &(a->sin_addr), host, INET6_ADDRSTRLEN);
- }
- return bc_strdup(host);
-}
-
-
-static uint16_t
-br_httpd_get_port(int af, const struct sockaddr *addr)
-{
- in_port_t port = 0;
- if (af == AF_INET6) {
- struct sockaddr_in6 *a = (struct sockaddr_in6*) addr;
- port = a->sin6_port;
- }
- else {
- struct sockaddr_in *a = (struct sockaddr_in*) addr;
- port = a->sin_port;
- }
- return ntohs(port);
-}
-
-
-int
-br_httpd_run(const char *host, const char *port, const char *docroot,
- size_t max_threads)
-{
- int err;
- struct addrinfo *result;
- struct addrinfo hints = {
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_flags = AI_PASSIVE,
- .ai_protocol = 0,
- .ai_canonname = NULL,
- .ai_addr = NULL,
- .ai_next = NULL,
- };
- if (0 != (err = getaddrinfo(host, port, &hints, &result))) {
- fprintf(stderr, "Failed to get host:port info: %s\n",
- gai_strerror(err));
- return 1;
- }
-
- thread_data_t threads[max_threads];
- for (size_t i = 0; i < max_threads; i++)
- threads[i].initialized = false;
-
- int rv = 0;
-
- struct addrinfo *rp;
- int server_socket = 0;
-
- int ai_family = 0;
- char *final_host = NULL;
- uint16_t final_port = 0;
-
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- final_host = br_httpd_get_ip(rp->ai_family, rp->ai_addr);
- final_port = br_httpd_get_port(rp->ai_family, rp->ai_addr);
- server_socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (server_socket == -1) {
- if (rp->ai_next == NULL) {
- fprintf(stderr, "Failed to open server socket (%s:%d): %s\n",
- final_host, final_port, strerror(errno));
- rv = 1;
- goto cleanup0;
- }
- continue;
- }
- int value = 1;
- if (0 > setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &value,
- sizeof(int)))
- {
- if (rp->ai_next == NULL) {
- fprintf(stderr, "Failed to set socket option (%s:%d): %s\n",
- final_host, final_port, strerror(errno));
- rv = 1;
- goto cleanup;
- }
- close(server_socket);
- continue;
- }
- if (0 == bind(server_socket, rp->ai_addr, rp->ai_addrlen)) {
- ai_family = rp->ai_family;
- break;
- }
- else {
- if (rp->ai_next == NULL) {
- fprintf(stderr, "Failed to bind to server socket (%s:%d): %s\n",
- final_host, final_port, strerror(errno));
- rv = 1;
- goto cleanup;
- }
- }
- free(final_host);
- close(server_socket);
- }
-
- if (-1 == listen(server_socket, LISTEN_BACKLOG)) {
- fprintf(stderr, "Failed to listen to server socket (%s:%d): %s\n",
- final_host, final_port, strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- fprintf(stderr, " * Running on http://");
- if (ai_family == AF_INET6)
- fprintf(stderr, "[%s]", final_host);
- else
- fprintf(stderr, "%s", final_host);
- if (final_port != 80)
- fprintf(stderr, ":%d", final_port);
- fprintf(stderr, "/ (max threads: %zu)\n"
- "\n"
- "WARNING!!! This is a development server, DO NOT RUN IT IN PRODUCTION!\n"
- "\n", max_threads);
-
- size_t current_thread = 0;
-
- while (1) {
- struct sockaddr_in6 addr6;
- struct sockaddr_in addr;
-
- socklen_t addrlen;
- struct sockaddr *client_addr = NULL;
-
- if (ai_family == AF_INET6) {
- addrlen = sizeof(addr6);
- client_addr = (struct sockaddr*) &addr6;
- }
- else {
- addrlen = sizeof(addr);
- client_addr = (struct sockaddr*) &addr;
- }
-
- int client_socket = accept(server_socket, client_addr, &addrlen);
- if (client_socket == -1) {
- fprintf(stderr, "Failed to accept connection: %s\n", strerror(errno));
- rv = 1;
- goto cleanup;
- }
-
- request_data_t *arg = malloc(sizeof(request_data_t));
- arg->thread_id = current_thread;
- arg->socket = client_socket;
- arg->ip = br_httpd_get_ip(ai_family, client_addr);
- arg->docroot = docroot;
-
- if (threads[current_thread].initialized) {
- if (pthread_join(threads[current_thread].thread, NULL) != 0) {
- fprintf(stderr, "Failed to join thread\n");
- free(arg->ip);
- free(arg);
- rv = 1;
- goto cleanup;
- }
- }
-
- if (pthread_create(&(threads[current_thread].thread), NULL,
- handle_request, arg) != 0)
- {
- fprintf(stderr, "Failed to create thread\n");
- rv = 1;
- goto cleanup;
- }
-
- threads[current_thread++].initialized = true;
-
- if (current_thread >= max_threads)
- current_thread = 0;
- }
-
-cleanup:
- close(server_socket);
-
-cleanup0:
- free(final_host);
- freeaddrinfo(result);
- return rv;
-}
diff --git a/src/blogc-runserver/httpd.h b/src/blogc-runserver/httpd.h
deleted file mode 100644
index b71a8d9..0000000
--- a/src/blogc-runserver/httpd.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _HTTPD_H
-#define _HTTPD_H
-
-int br_httpd_run(const char *host, const char *port, const char *docroot,
- size_t max_threads);
-
-#endif /* _HTTPD_H */
diff --git a/src/blogc-runserver/main.c b/src/blogc-runserver/main.c
deleted file mode 100644
index 1c5be29..0000000
--- a/src/blogc-runserver/main.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "../common/utils.h"
-#include "httpd.h"
-
-
-static void
-print_help(const char *default_host, const char *default_port)
-{
- printf(
- "usage:\n"
- " blogc-runserver [-h] [-v] [-t HOST] [-p PORT] [-m THREADS] DOCROOT\n"
- " - A simple HTTP server to test blogc websites.\n"
- "\n"
- "positional arguments:\n"
- " DOCROOT document root directory\n"
- "\n"
- "optional arguments:\n"
- " -h show this help message and exit\n"
- " -v show version and exit\n"
- " -t HOST set server listen address (default: %s)\n"
- " -p PORT set server listen port (default: %s)\n"
- " -m THREADS set maximum number of threads to spawn (default: 20)\n",
- default_host, default_port);
-}
-
-
-static void
-print_usage(void)
-{
- printf("usage: blogc-runserver [-h] [-v] [-t HOST] [-p PORT] [-m THREADS] DOCROOT\n");
-}
-
-
-int
-main(int argc, char **argv)
-{
- struct sigaction new_action;
- new_action.sa_handler = SIG_IGN;
- sigemptyset(&new_action.sa_mask);
- new_action.sa_flags = 0;
- sigaction(SIGPIPE, &new_action, NULL);
-
- int rv = 0;
- char *host = NULL;
- char *port = NULL;
- char *docroot = NULL;
- size_t max_threads = 20;
- char *ptr;
- char *endptr;
-
- char *tmp_host = getenv("BLOGC_RUNSERVER_DEFAULT_HOST");
- char *default_host = bc_strdup(tmp_host != NULL ? tmp_host : "127.0.0.1");
- char *tmp_port = getenv("BLOGC_RUNSERVER_DEFAULT_PORT");
- char *default_port = bc_strdup(tmp_port != NULL ? tmp_port : "8080");
-
- size_t args = 0;
-
- for (size_t i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- switch (argv[i][1]) {
- case 'h':
- print_help(default_host, default_port);
- goto cleanup;
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- goto cleanup;
- case 't':
- if (argv[i][2] != '\0')
- host = bc_strdup(argv[i] + 2);
- else
- host = bc_strdup(argv[++i]);
- break;
- case 'p':
- if (argv[i][2] != '\0')
- port = bc_strdup(argv[i] + 2);
- else
- port = bc_strdup(argv[++i]);
- break;
- case 'm':
- if (argv[i][2] != '\0')
- ptr = argv[i] + 2;
- else
- ptr = argv[++i];
- max_threads = strtoul(ptr, &endptr, 10);
- if (*ptr != '\0' && *endptr != '\0')
- fprintf(stderr, "blogc-runserver: warning: invalid value "
- "for -m argument: %s. using %zu instead\n", ptr, max_threads);
- break;
- default:
- print_usage();
- fprintf(stderr, "blogc-runserver: error: invalid "
- "argument: -%c\n", argv[i][1]);
- rv = 1;
- goto cleanup;
- }
- }
- else {
- if (args > 0) {
- print_usage();
- fprintf(stderr, "blogc-runserver: error: only one positional "
- "argument allowed\n");
- rv = 1;
- goto cleanup;
- }
- args++;
- docroot = bc_strdup(argv[i]);
- }
- }
-
- if (docroot == NULL) {
- print_usage();
- fprintf(stderr, "blogc-runserver: error: document root directory "
- "required\n");
- rv = 1;
- goto cleanup;
- }
-
- if (max_threads <= 0 || max_threads > 1000) {
- print_usage();
- fprintf(stderr, "blogc-runserver: error: invalid value for -m. "
- "Must be integer > 0 and <= 1000\n");
- rv = 1;
- goto cleanup;
- }
-
- rv = br_httpd_run(
- host != NULL ? host : default_host,
- port != NULL ? port : default_port,
- docroot, max_threads);
-
-cleanup:
- free(default_host);
- free(default_port);
- free(host);
- free(port);
- free(docroot);
-
- return rv;
-}
diff --git a/src/blogc-runserver/mime.c b/src/blogc-runserver/mime.c
deleted file mode 100644
index 636c496..0000000
--- a/src/blogc-runserver/mime.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "../common/utils.h"
-#include "httpd-utils.h"
-
-
-// mime types with index should be in the begin of the list. first NULL
-// index aborts the lookup, for optimization
-static const struct content_type_map {
- const char *mimetype;
- const char *extension;
- const char *index;
-} content_types[] = {
-
- // with index
- {"text/html", "html", "index.html"},
- {"text/html", "htm", "index.htm"},
- {"text/html", "shtml", "index.shtml"},
- {"text/xml", "xml", "index.xml"},
- {"text/plain", "txt", "index.txt"},
- {"application/xhtml+xml", "xhtml", "index.xhtml"},
-
- // without index
- {"text/css", "css", NULL},
- {"image/gif", "gif", NULL},
- {"image/jpeg", "jpeg", NULL},
- {"image/jpeg", "jpg", NULL},
- {"application/javascript", "js", NULL},
- {"application/atom+xml", "atom", NULL},
- {"application/rss+xml", "rss", NULL},
- {"text/mathml", "mml", NULL},
- {"text/vnd.sun.j2me.app-descriptor", "jad", NULL},
- {"text/vnd.wap.wml", "wml", NULL},
- {"text/x-component", "htc", NULL},
- {"image/png", "png", NULL},
- {"image/tiff", "tif", NULL},
- {"image/tiff", "tiff", NULL},
- {"image/vnd.wap.wbmp", "wbmp", NULL},
- {"image/x-icon", "ico", NULL},
- {"image/x-jng", "jng", NULL},
- {"image/x-ms-bmp", "bmp", NULL},
- {"image/svg+xml", "svg", NULL},
- {"image/svg+xml", "svgz", NULL},
- {"image/webp", "webp", NULL},
- {"application/font-woff", "woff", NULL},
- {"application/java-archive", "jar", NULL},
- {"application/java-archive", "war", NULL},
- {"application/java-archive", "ear", NULL},
- {"application/json", "json", NULL},
- {"application/mac-binhex40", "hqx", NULL},
- {"application/msword", "doc", NULL},
- {"application/pdf", "pdf", NULL},
- {"application/postscript", "ps", NULL},
- {"application/postscript", "eps", NULL},
- {"application/postscript", "ai", NULL},
- {"application/rtf", "rtf", NULL},
- {"application/vnd.apple.mpegurl", "m3u8", NULL},
- {"application/vnd.ms-excel", "xls", NULL},
- {"application/vnd.ms-fontobject", "eot", NULL},
- {"application/vnd.ms-powerpoint", "ppt", NULL},
- {"application/vnd.wap.wmlc", "wmlc", NULL},
- {"application/vnd.google-earth.kml+xml", "kml", NULL},
- {"application/vnd.google-earth.kmz", "kmz", NULL},
- {"application/x-7z-compressed", "7z", NULL},
- {"application/x-cocoa", "cco", NULL},
- {"application/x-java-archive-diff", "jardiff", NULL},
- {"application/x-java-jnlp-file", "jnlp", NULL},
- {"application/x-makeself", "run", NULL},
- {"application/x-perl", "pl", NULL},
- {"application/x-perl", "pm", NULL},
- {"application/x-pilot", "prc", NULL},
- {"application/x-pilot", "pdb", NULL},
- {"application/x-rar-compressed", "rar", NULL},
- {"application/x-redhat-package-manager", "rpm", NULL},
- {"application/x-sea", "sea", NULL},
- {"application/x-shockwave-flash", "swf", NULL},
- {"application/x-stuffit", "sit", NULL},
- {"application/x-tcl", "tcl", NULL},
- {"application/x-tcl", "tk", NULL},
- {"application/x-x509-ca-cert", "der", NULL},
- {"application/x-x509-ca-cert", "pem", NULL},
- {"application/x-x509-ca-cert", "crt", NULL},
- {"application/x-xpinstall", "xpi", NULL},
- {"application/xspf+xml", "xspf", NULL},
- {"application/zip", "zip", NULL},
- {"application/octet-stream", "bin", NULL},
- {"application/octet-stream", "exe", NULL},
- {"application/octet-stream", "dll", NULL},
- {"application/octet-stream", "deb", NULL},
- {"application/octet-stream", "dmg", NULL},
- {"application/octet-stream", "iso", NULL},
- {"application/octet-stream", "img", NULL},
- {"application/octet-stream", "msi", NULL},
- {"application/octet-stream", "msp", NULL},
- {"application/octet-stream", "msm", NULL},
- {"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", NULL},
- {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", NULL},
- {"application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", NULL},
- {"audio/midi", "mid", NULL},
- {"audio/midi", "midi", NULL},
- {"audio/midi", "kar", NULL},
- {"audio/mpeg", "mp3", NULL},
- {"audio/ogg", "ogg", NULL},
- {"audio/x-m4a", "m4a", NULL},
- {"audio/x-realaudio", "ra", NULL},
- {"video/3gpp", "3gpp", NULL},
- {"video/3gpp", "3gp", NULL},
- {"video/mp2t", "ts", NULL},
- {"video/mp4", "mp4", NULL},
- {"video/mpeg", "mpeg", NULL},
- {"video/mpeg", "mpg", NULL},
- {"video/quicktime", "mov", NULL},
- {"video/webm", "webm", NULL},
- {"video/x-flv", "flv", NULL},
- {"video/x-m4v", "m4v", NULL},
- {"video/x-mng", "mng", NULL},
- {"video/x-ms-asf", "asx", NULL},
- {"video/x-ms-asf", "asf", NULL},
- {"video/x-ms-wmv", "wmv", NULL},
- {"video/x-msvideo", "avi", NULL},
- {NULL, NULL, NULL}
-};
-
-
-const char*
-br_mime_guess_content_type(const char *filename)
-{
- const char *extension = br_get_extension(filename);
- if (extension == NULL)
- goto default_type;
- for (size_t i = 0; content_types[i].extension != NULL; i++) {
- if (0 == strcmp(content_types[i].extension, extension)) {
- return content_types[i].mimetype;
- }
- }
-
-default_type:
- return "application/octet-stream";
-}
-
-
-char*
-br_mime_guess_index(const char *path)
-{
- char *found = NULL;
- for (size_t i = 0; content_types[i].index != NULL; i++) {
- char *f = bc_strdup_printf("%s/%s", path, content_types[i].index);
- if (0 == access(f, F_OK)) {
- found = f;
- break;
- }
- free(f);
- }
- return found;
-}
diff --git a/src/blogc-runserver/mime.h b/src/blogc-runserver/mime.h
deleted file mode 100644
index b9fb013..0000000
--- a/src/blogc-runserver/mime.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _MIME_H
-#define _MIME_H
-
-const char* br_mime_guess_content_type(const char *filename);
-char* br_mime_guess_index(const char *path);
-
-#endif /* _MIME_H */
diff --git a/src/blogc.c b/src/blogc.c
new file mode 100644
index 0000000..c90070d
--- /dev/null
+++ b/src/blogc.c
@@ -0,0 +1,297 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif /* HAVE_SYS_STAT_H */
+
+#include <errno.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "template-parser.h"
+#include "loader.h"
+#include "renderer.h"
+#include "error.h"
+#include "utf8.h"
+#include "utils.h"
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "Unknown"
+#endif
+
+
+static void
+blogc_print_help(void)
+{
+ printf(
+ "usage:\n"
+ " blogc [-h] [-v] [-d] [-l] [-D KEY=VALUE ...] [-p KEY] [-t TEMPLATE]\n"
+ " [-o OUTPUT] [SOURCE ...] - A blog compiler.\n"
+ "\n"
+ "positional arguments:\n"
+ " SOURCE source file(s)\n"
+ "\n"
+ "optional arguments:\n"
+ " -h show this help message and exit\n"
+ " -v show version and exit\n"
+ " -d enable debug\n"
+ " -l build listing page, from multiple source files\n"
+ " -D KEY=VALUE set global configuration parameter\n"
+ " -p KEY show the value of a global configuration parameter\n"
+ " after source parsing and exit\n"
+ " -t TEMPLATE template file\n"
+ " -o OUTPUT output file\n");
+}
+
+
+static void
+blogc_print_usage(void)
+{
+ printf(
+ "usage: blogc [-h] [-v] [-d] [-l] [-D KEY=VALUE ...] [-p KEY] [-t TEMPLATE]\n"
+ " [-o OUTPUT] [SOURCE ...]\n");
+}
+
+
+static void
+blogc_mkdir_recursive(const char *filename)
+{
+ char *fname = sb_strdup(filename);
+ for (char *tmp = fname; *tmp != '\0'; tmp++) {
+ if (*tmp != '/' && *tmp != '\\')
+ continue;
+#ifdef HAVE_SYS_STAT_H
+ char bkp = *tmp;
+ *tmp = '\0';
+ if ((strlen(fname) > 0) &&
+#if defined(WIN32) || defined(_WIN32)
+ (-1 == mkdir(fname)) &&
+#else
+ (-1 == mkdir(fname, 0777)) &&
+#endif
+ (errno != EEXIST))
+ {
+ fprintf(stderr, "blogc: error: failed to create output "
+ "directory (%s): %s\n", fname, strerror(errno));
+ free(fname);
+ exit(2);
+ }
+ *tmp = bkp;
+#else
+ // FIXME: show this warning only if actually trying to create a directory.
+ fprintf(stderr, "blogc: warning: can't create output directories "
+ "for your platform. please create the directories yourself.\n");
+ break;
+#endif
+ }
+ free(fname);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ setlocale(LC_ALL, "");
+
+ int rv = 0;
+
+ bool debug = false;
+ bool listing = false;
+ char *template = NULL;
+ char *output = NULL;
+ char *print = NULL;
+ char *tmp = NULL;
+ char **pieces = NULL;
+
+ sb_slist_t *sources = NULL;
+ sb_trie_t *config = sb_trie_new(free);
+ sb_trie_insert(config, "BLOGC_VERSION", sb_strdup(PACKAGE_VERSION));
+
+ for (unsigned int i = 1; i < argc; i++) {
+ tmp = NULL;
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'h':
+ blogc_print_help();
+ goto cleanup;
+ case 'v':
+ printf("%s\n", PACKAGE_STRING);
+ goto cleanup;
+ case 'd':
+ debug = true;
+ break;
+ case 'l':
+ listing = true;
+ break;
+ case 't':
+ if (argv[i][2] != '\0')
+ template = sb_strdup(argv[i] + 2);
+ else if (i + 1 < argc)
+ template = sb_strdup(argv[++i]);
+ break;
+ case 'o':
+ if (argv[i][2] != '\0')
+ output = sb_strdup(argv[i] + 2);
+ else if (i + 1 < argc)
+ output = sb_strdup(argv[++i]);
+ break;
+ case 'p':
+ if (argv[i][2] != '\0')
+ print = sb_strdup(argv[i] + 2);
+ else if (i + 1 < argc)
+ print = sb_strdup(argv[++i]);
+ break;
+ case 'D':
+ if (argv[i][2] != '\0')
+ tmp = argv[i] + 2;
+ else if (i + 1 < argc)
+ tmp = argv[++i];
+ if (tmp != NULL) {
+ if (!blogc_utf8_validate((uint8_t*) tmp, strlen(tmp))) {
+ fprintf(stderr, "blogc: error: invalid value for "
+ "-D (must be valid UTF-8 string): %s\n", tmp);
+ goto cleanup;
+ }
+ pieces = sb_str_split(tmp, '=', 2);
+ if (sb_strv_length(pieces) != 2) {
+ fprintf(stderr, "blogc: error: invalid value for "
+ "-D (must have an '='): %s\n", tmp);
+ sb_strv_free(pieces);
+ rv = 2;
+ goto cleanup;
+ }
+ for (unsigned int j = 0; pieces[0][j] != '\0'; j++) {
+ if (!((pieces[0][j] >= 'A' && pieces[0][j] <= 'Z') ||
+ pieces[0][j] == '_'))
+ {
+ fprintf(stderr, "blogc: error: invalid value "
+ "for -D (configuration key must be uppercase "
+ "with '_'): %s\n", pieces[0]);
+ sb_strv_free(pieces);
+ rv = 2;
+ goto cleanup;
+ }
+ }
+ sb_trie_insert(config, pieces[0], sb_strdup(pieces[1]));
+ sb_strv_free(pieces);
+ pieces = NULL;
+ }
+ break;
+ default:
+ blogc_print_usage();
+ fprintf(stderr, "blogc: error: invalid argument: -%c\n",
+ argv[i][1]);
+ rv = 2;
+ goto cleanup;
+ }
+ }
+ else
+ sources = sb_slist_append(sources, sb_strdup(argv[i]));
+ }
+
+ if (!listing && sb_slist_length(sources) == 0) {
+ blogc_print_usage();
+ fprintf(stderr, "blogc: error: one source file is required\n");
+ rv = 2;
+ goto cleanup;
+ }
+
+ if (!listing && sb_slist_length(sources) > 1) {
+ blogc_print_usage();
+ fprintf(stderr, "blogc: error: only one source file should be provided, "
+ "if running without '-l'\n");
+ rv = 2;
+ goto cleanup;
+ }
+
+ blogc_error_t *err = NULL;
+
+ sb_slist_t *s = blogc_source_parse_from_files(config, sources, &err);
+ if (err != NULL) {
+ blogc_error_print(err);
+ rv = 2;
+ goto cleanup2;
+ }
+
+ if (print != NULL) {
+ const char *val = sb_trie_lookup(config, print);
+ if (val == NULL) {
+ fprintf(stderr, "blogc: error: configuration variable not found: %s\n",
+ print);
+ rv = 2;
+ }
+ else {
+ printf("%s\n", val);
+ }
+ goto cleanup2;
+ }
+
+ if (template == NULL) {
+ blogc_print_usage();
+ fprintf(stderr, "blogc: error: argument -t is required when rendering content\n");
+ rv = 2;
+ goto cleanup2;
+ }
+
+ sb_slist_t* l = blogc_template_parse_from_file(template, &err);
+ if (err != NULL) {
+ blogc_error_print(err);
+ rv = 2;
+ goto cleanup3;
+ }
+
+ if (debug)
+ blogc_debug_template(l);
+
+ char *out = blogc_render(l, s, config, listing);
+
+ bool write_to_stdout = (output == NULL || (0 == strcmp(output, "-")));
+
+ FILE *fp = stdout;
+ if (!write_to_stdout) {
+ blogc_mkdir_recursive(output);
+ fp = fopen(output, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "blogc: error: failed to open output file (%s): %s\n",
+ output, strerror(errno));
+ rv = 2;
+ goto cleanup4;
+ }
+ }
+
+ if (out != NULL)
+ fprintf(fp, "%s", out);
+
+ if (!write_to_stdout)
+ fclose(fp);
+
+cleanup4:
+ free(out);
+cleanup3:
+ blogc_template_free_stmts(l);
+cleanup2:
+ sb_slist_free_full(s, (sb_free_func_t) sb_trie_free);
+ blogc_error_free(err);
+cleanup:
+ sb_trie_free(config);
+ free(template);
+ free(output);
+ free(print);
+ sb_slist_free_full(sources, free);
+ return rv;
+}
diff --git a/src/blogc/content-parser.h b/src/blogc/content-parser.h
deleted file mode 100644
index a321155..0000000
--- a/src/blogc/content-parser.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _CONTENT_PARSER_H
-#define _CONTENT_PARSER_H
-
-#include <stddef.h>
-#include <stdbool.h>
-#include "../common/utils.h"
-
-char* blogc_slugify(const char *str);
-char* blogc_htmlentities(const char *str);
-char* blogc_fix_description(const char *paragraph);
-char* blogc_content_parse_inline(const char *src);
-bool blogc_is_ordered_list_item(const char *str, size_t prefix_len);
-char* blogc_content_parse(const char *src, size_t *end_excerpt,
- char **first_header, char **description, char **endl,
- bc_slist_t **headers);
-
-#endif /* _CONTENT_PARSER_H */
diff --git a/src/blogc/debug.c b/src/blogc/debug.c
deleted file mode 100644
index 11e7973..0000000
--- a/src/blogc/debug.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdio.h>
-
-#include "template-parser.h"
-#include "../common/utils.h"
-#include "debug.h"
-
-
-static const char*
-get_operator(blogc_template_operator_t op)
-{
- if (op & BLOGC_TEMPLATE_OP_NEQ)
- return "!=";
- if (op & BLOGC_TEMPLATE_OP_EQ) {
- if (op & BLOGC_TEMPLATE_OP_LT)
- return "<=";
- else if (op & BLOGC_TEMPLATE_OP_GT)
- return ">=";
- return "==";
- }
- if (op & BLOGC_TEMPLATE_OP_LT)
- return "<";
- else if (op & BLOGC_TEMPLATE_OP_GT)
- return ">";
- return "";
-}
-
-
-void
-blogc_debug_template(bc_slist_t *ast)
-{
- for (bc_slist_t *tmp = ast; tmp != NULL; tmp = tmp->next) {
- blogc_template_node_t *data = tmp->data;
- fprintf(stderr, "DEBUG: <TEMPLATE ");
- switch (data->type) {
- case BLOGC_TEMPLATE_NODE_IFDEF:
- fprintf(stderr, "IFDEF: %s", data->data[0]);
- break;
- case BLOGC_TEMPLATE_NODE_IFNDEF:
- fprintf(stderr, "IFNDEF: %s", data->data[0]);
- break;
- case BLOGC_TEMPLATE_NODE_IF:
- fprintf(stderr, "IF: %s %s %s", data->data[0],
- get_operator(data->op), data->data[1]);
- break;
- case BLOGC_TEMPLATE_NODE_ELSE:
- fprintf(stderr, "ELSE");
- break;
- case BLOGC_TEMPLATE_NODE_ENDIF:
- fprintf(stderr, "ENDIF");
- break;
- case BLOGC_TEMPLATE_NODE_FOREACH:
- fprintf(stderr, "FOREACH: %s", data->data[0]);
- break;
- case BLOGC_TEMPLATE_NODE_ENDFOREACH:
- fprintf(stderr, "ENDFOREACH");
- break;
- case BLOGC_TEMPLATE_NODE_BLOCK:
- fprintf(stderr, "BLOCK: %s", data->data[0]);
- break;
- case BLOGC_TEMPLATE_NODE_ENDBLOCK:
- fprintf(stderr, "ENDBLOCK");
- break;
- case BLOGC_TEMPLATE_NODE_VARIABLE:
- fprintf(stderr, "VARIABLE: %s", data->data[0]);
- break;
- case BLOGC_TEMPLATE_NODE_CONTENT:
- fprintf(stderr, "CONTENT: `%s`", data->data[0]);
- break;
- }
- fprintf(stderr, ">\n");
- }
-}
diff --git a/src/blogc/filelist-parser.c b/src/blogc/filelist-parser.c
deleted file mode 100644
index 984f25f..0000000
--- a/src/blogc/filelist-parser.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include "../common/utils.h"
-
-
-typedef enum {
- LINE_START = 1,
- LINE,
-} blogc_filelist_parser_state_t;
-
-
-bc_slist_t*
-blogc_filelist_parse(const char *src, size_t src_len)
-{
- size_t current = 0;
- bc_slist_t *rv = NULL;
- bc_string_t *line = bc_string_new();
- blogc_filelist_parser_state_t state = LINE_START;
-
- while (current < src_len) {
- char c = src[current];
- bool is_last = current == src_len - 1;
-
- switch (state) {
-
- case LINE_START:
- if (c == '#') {
- while (current < src_len) {
- if (src[current] == '\r' || src[current] == '\n')
- break;
- current++;
- }
- break;
- }
- if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
- break;
- state = LINE;
- continue;
-
- case LINE:
- if (c == '\r' || c == '\n' || is_last) {
- if (is_last && c != '\r' && c != '\n')
- bc_string_append_c(line, c);
- rv = bc_slist_append(rv, bc_str_strip(line->str));
- bc_string_free(line, false);
- line = bc_string_new();
- state = LINE_START;
- break;
- }
- bc_string_append_c(line, c);
- break;
-
- }
-
- current++;
- }
-
- bc_string_free(line, true);
-
- return rv;
-}
diff --git a/src/blogc/filelist-parser.h b/src/blogc/filelist-parser.h
deleted file mode 100644
index b068554..0000000
--- a/src/blogc/filelist-parser.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _FILELIST_PARSER_H
-#define _FILELIST_PARSER_H
-
-#include "../common/utils.h"
-
-bc_slist_t* blogc_filelist_parse(const char *src, size_t src_len);
-
-#endif /* _FILELIST_PARSER_H */
diff --git a/src/blogc/funcvars.c b/src/blogc/funcvars.c
deleted file mode 100644
index 6f0700b..0000000
--- a/src/blogc/funcvars.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 <stdlib.h>
-#include <string.h>
-
-#include "funcvars.h"
-#include "rusage.h"
-#include "sysinfo.h"
-#include "../common/utils.h"
-
-
-static const struct func_map {
- const char *variable;
- const blogc_funcvars_func_t func;
-} funcs[] = {
-
-#ifdef HAVE_RUSAGE
- {"BLOGC_RUSAGE_CPU_TIME", blogc_rusage_inject},
- {"BLOGC_RUSAGE_MEMORY", blogc_rusage_inject},
-#endif
-
-#ifdef HAVE_SYSINFO_HOSTNAME
- {"BLOGC_SYSINFO_HOSTNAME", blogc_sysinfo_inject_hostname},
-#endif
-
-#ifdef HAVE_SYSINFO_DATETIME
- {"BLOGC_SYSINFO_DATETIME", blogc_sysinfo_inject_datetime},
-#endif
-
- {"BLOGC_SYSINFO_USERNAME", blogc_sysinfo_inject_username},
- {"BLOGC_SYSINFO_INSIDE_DOCKER", blogc_sysinfo_inject_inside_docker},
- {NULL, NULL},
-};
-
-
-void
-blogc_funcvars_eval(bc_trie_t *global, const char *name)
-{
- if (global == NULL || name == NULL)
- return;
-
- // protect against evaluating the same function twice in the same global
- // context
- if (NULL != bc_trie_lookup(global, name))
- return;
-
- for (size_t i = 0; funcs[i].variable != NULL; i++) {
- if (0 == strcmp(name, funcs[i].variable)) {
- funcs[i].func(global);
- return;
- }
- }
-
- return;
-}
diff --git a/src/blogc/funcvars.h b/src/blogc/funcvars.h
deleted file mode 100644
index aae1bc3..0000000
--- a/src/blogc/funcvars.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef ___FUNCVARS_H
-#define ___FUNCVARS_H
-
-#include <stdbool.h>
-#include "../common/utils.h"
-
-typedef void (*blogc_funcvars_func_t) (bc_trie_t*);
-
-void blogc_funcvars_eval(bc_trie_t *global, const char *name);
-
-#endif /* ___FUNCVARS_H */
diff --git a/src/blogc/loader.c b/src/blogc/loader.c
deleted file mode 100644
index af30a1b..0000000
--- a/src/blogc/loader.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <math.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "datetime-parser.h"
-#include "source-parser.h"
-#include "template-parser.h"
-#include "loader.h"
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "../common/sort.h"
-
-
-char*
-blogc_get_filename(const char *f)
-{
- if (f == NULL)
- return NULL;
-
- if (strlen(f) == 0)
- return NULL;
-
- char *filename = bc_strdup(f);
-
- // keep a pointer to original string
- char *tmp = filename;
-
- bool removed_dot = false;
- for (int i = strlen(tmp); i >= 0 ; i--) {
-
- // remove last extension
- if (!removed_dot && tmp[i] == '.') {
- tmp[i] = '\0';
- removed_dot = true;
- continue;
- }
-
- if (tmp[i] == '/' || tmp[i] == '\\') {
- tmp += i + 1;
- break;
- }
- }
-
- char *final_filename = bc_strdup(tmp);
- free(filename);
-
- return final_filename;
-}
-
-
-bc_slist_t*
-blogc_template_parse_from_file(const char *f, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- size_t len;
- char *s = bc_file_get_contents(f, true, &len, err);
- if (s == NULL)
- return NULL;
- bc_slist_t *rv = blogc_template_parse(s, len, err);
- free(s);
- return rv;
-}
-
-
-bc_trie_t*
-blogc_source_parse_from_file(bc_trie_t *conf, const char *f, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- size_t len;
- char *s = bc_file_get_contents(f, true, &len, err);
- if (s == NULL)
- return NULL;
-
- int toctree_maxdepth = -1;
- const char *maxdepth = bc_trie_lookup(conf, "TOCTREE_MAXDEPTH");
- if (maxdepth != NULL) {
- char *endptr;
- toctree_maxdepth = strtol(maxdepth, &endptr, 10);
- if (*maxdepth != '\0' && *endptr != '\0') {
- fprintf(stderr, "warning: invalid value for 'TOCTREE_MAXDEPTH' "
- "variable: %s. using %d instead\n", maxdepth, toctree_maxdepth);
- }
- }
-
- bc_trie_t *rv = blogc_source_parse(s, len, toctree_maxdepth, err);
-
- // set FILENAME variable
- if (rv != NULL) {
- char *filename = blogc_get_filename(f);
- if (filename != NULL)
- bc_trie_insert(rv, "FILENAME", filename);
- }
-
- free(s);
- return rv;
-}
-
-
-static int
-sort_source(const void *a, const void *b)
-{
- const char *ca = bc_trie_lookup((bc_trie_t*) a, "c");
- const char *cb = bc_trie_lookup((bc_trie_t*) b, "c");
-
- if (ca == NULL || cb == NULL) {
- return 0; // wat
- }
-
- unsigned long la = strtoul(ca, NULL, 10);
- unsigned long lb = strtoul(cb, NULL, 10);
-
- return (int) (lb - la);
-}
-
-
-static int
-sort_source_reverse(const void *a, const void *b)
-{
- return sort_source(b, a);
-}
-
-
-bc_slist_t*
-blogc_source_parse_from_files(bc_trie_t *conf, bc_slist_t *l, bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- bool sort = bc_str_to_bool(bc_trie_lookup(conf, "FILTER_SORT"));
-
- bc_slist_t* sources = NULL;
- bc_error_t *tmp_err = NULL;
- size_t with_date = 0;
- for (bc_slist_t *tmp = l; tmp != NULL; tmp = tmp->next) {
- char *f = tmp->data;
- bc_trie_t *s = blogc_source_parse_from_file(conf, f, &tmp_err);
- if (s == NULL) {
- *err = bc_error_new_printf(BLOGC_ERROR_LOADER,
- "An error occurred while parsing source file: %s\n\n%s",
- f, tmp_err->msg);
- bc_error_free(tmp_err);
- bc_slist_free_full(sources, (bc_free_func_t) bc_trie_free);
- return NULL;
- }
-
- const char *date = bc_trie_lookup(s, "DATE");
- if (date != NULL) {
- with_date++;
- }
-
- if (sort) {
- if (date == NULL) {
- *err = bc_error_new_printf(BLOGC_ERROR_LOADER,
- "'FILTER_SORT' requires that 'DATE' variable is set for "
- "every source file: %s", f);
- bc_trie_free(s);
- bc_slist_free_full(sources, (bc_free_func_t) bc_trie_free);
- return NULL;
- }
-
- char *timestamp = blogc_convert_datetime(date, "%s", &tmp_err);
- if (timestamp == NULL) {
- *err = bc_error_new_printf(BLOGC_ERROR_LOADER,
- "An error occurred while parsing 'DATE' variable: %s"
- "\n\n%s", f, tmp_err->msg);
- bc_error_free(tmp_err);
- bc_trie_free(s);
- bc_slist_free_full(sources, (bc_free_func_t) bc_trie_free);
- return NULL;
- }
-
- bc_trie_insert(s, "c", timestamp);
- }
-
- sources = bc_slist_append(sources, s);
- }
-
- if (with_date > 0 && with_date < bc_slist_length(l)) {
- *err = bc_error_new_printf(BLOGC_ERROR_LOADER,
- "'DATE' variable provided for at least one source file, but not "
- "for all source files. It must be provided for all files.");
- bc_slist_free_full(sources, (bc_free_func_t) bc_trie_free);
- return NULL;
- }
-
- bool reverse = bc_str_to_bool(bc_trie_lookup(conf, "FILTER_REVERSE"));
-
- if (sort) {
- sources = bc_slist_sort(sources,
- (bc_sort_func_t) (reverse ? sort_source_reverse : sort_source));
- }
- else if (reverse) {
- bc_slist_t *tmp_sources = NULL;
- for (bc_slist_t *tmp = sources; tmp != NULL; tmp = tmp->next) {
- tmp_sources = bc_slist_prepend(tmp_sources, tmp->data);
- }
- bc_slist_t *tmp = sources;
- sources = tmp_sources;
- bc_slist_free(tmp);
- }
-
- const char *filter_tag = bc_trie_lookup(conf, "FILTER_TAG");
- const char *filter_page = bc_trie_lookup(conf, "FILTER_PAGE");
- const char *filter_per_page = bc_trie_lookup(conf, "FILTER_PER_PAGE");
-
- const char *ptr;
- char *endptr;
-
- ptr = filter_page != NULL ? filter_page : "";
- long page = strtol(ptr, &endptr, 10);
- if (*ptr != '\0' && *endptr != '\0')
- fprintf(stderr, "warning: invalid value for 'FILTER_PAGE' variable: "
- "%s. using %ld instead\n", ptr, page);
- if (page <= 0)
- page = 1;
-
- ptr = filter_per_page != NULL ? filter_per_page : "10";
- long per_page = strtol(ptr, &endptr, 10);
- if (*ptr != '\0' && *endptr != '\0')
- fprintf(stderr, "warning: invalid value for 'FILTER_PER_PAGE' variable: "
- "%s. using %ld instead\n", ptr, per_page);
- if (per_page < 0)
- per_page = 0;
-
- // poor man's pagination
- size_t start = (page - 1) * per_page;
- size_t end = start + per_page;
- size_t counter = 0;
-
- bc_slist_t *rv = NULL;
- for (bc_slist_t *tmp = sources; tmp != NULL; tmp = tmp->next) {
- bc_trie_t *s = tmp->data;
- if (filter_tag != NULL) {
- const char *tags_str = bc_trie_lookup(s, "TAGS");
- // if user wants to filter by tag and no tag is provided, skip it
- if (tags_str == NULL) {
- bc_trie_free(s);
- continue;
- }
- char **tags = bc_str_split(tags_str, ' ', 0);
- bool found = false;
- for (size_t i = 0; tags[i] != NULL; i++) {
- if (tags[i][0] == '\0')
- continue;
- if (0 == strcmp(tags[i], filter_tag))
- found = true;
- }
- bc_strv_free(tags);
- if (!found) {
- bc_trie_free(s);
- continue;
- }
- }
- if (filter_page != NULL) {
- if (counter < start || counter >= end) {
- counter++;
- bc_trie_free(s);
- continue;
- }
- counter++;
- }
- rv = bc_slist_append(rv, s);
- }
-
- bc_slist_free(sources);
-
- bool first = true;
- for (bc_slist_t *tmp = rv; tmp != NULL; tmp = tmp->next) {
- bc_trie_t *s = tmp->data;
- if (first) {
- const char *val = bc_trie_lookup(s, "DATE");
- if (val != NULL)
- bc_trie_insert(conf, "DATE_FIRST", bc_strdup(val));
- val = bc_trie_lookup(s, "FILENAME");
- if (val != NULL)
- bc_trie_insert(conf, "FILENAME_FIRST", bc_strdup(val));
- first = false;
- }
- if (tmp->next == NULL) { // last
- const char *val = bc_trie_lookup(s, "DATE");
- if (val != NULL)
- bc_trie_insert(conf, "DATE_LAST", bc_strdup(val));
- val = bc_trie_lookup(s, "FILENAME");
- if (val != NULL)
- bc_trie_insert(conf, "FILENAME_LAST", bc_strdup(val));
- }
- }
-
- if (filter_page != NULL) {
- size_t last_page = ceilf(((float) counter) / per_page);
- bc_trie_insert(conf, "CURRENT_PAGE", bc_strdup_printf("%ld", page));
- if (page > 1)
- bc_trie_insert(conf, "PREVIOUS_PAGE", bc_strdup_printf("%ld", page - 1));
- if (page < last_page)
- bc_trie_insert(conf, "NEXT_PAGE", bc_strdup_printf("%ld", page + 1));
- if (bc_slist_length(rv) > 0)
- bc_trie_insert(conf, "FIRST_PAGE", bc_strdup("1"));
- if (last_page > 0)
- bc_trie_insert(conf, "LAST_PAGE", bc_strdup_printf("%d", last_page));
- }
-
- return rv;
-}
diff --git a/src/blogc/loader.h b/src/blogc/loader.h
deleted file mode 100644
index fe88730..0000000
--- a/src/blogc/loader.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _LOADER_H
-#define _LOADER_H
-
-#include "../common/error.h"
-#include "../common/utils.h"
-
-char* blogc_get_filename(const char *f);
-bc_slist_t* blogc_template_parse_from_file(const char *f, bc_error_t **err);
-bc_trie_t* blogc_source_parse_from_file(bc_trie_t *conf, const char *f,
- bc_error_t **err);
-bc_slist_t* blogc_source_parse_from_files(bc_trie_t *conf, bc_slist_t *l,
- bc_error_t **err);
-
-#endif /* _LOADER_H */
diff --git a/src/blogc/main.c b/src/blogc/main.c
deleted file mode 100644
index 9bfa9cf..0000000
--- a/src/blogc/main.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif /* HAVE_SYS_STAT_H */
-
-#ifdef HAVE_SYSEXITS_H
-#include <sysexits.h>
-#else
-#define EX_CONFIG 78
-#endif /* HAVE_SYSEXITS_H */
-
-#include <errno.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "debug.h"
-#include "filelist-parser.h"
-#include "template-parser.h"
-#include "loader.h"
-#include "renderer.h"
-#include "../common/error.h"
-#include "../common/utf8.h"
-#include "../common/utils.h"
-#include "../common/stdin.h"
-
-#ifdef MAKE_EMBEDDED
-extern int bm_main(int argc, char **argv);
-#endif
-
-#ifndef PACKAGE_VERSION
-#define PACKAGE_VERSION "Unknown"
-#endif
-
-
-static void
-blogc_print_help(void)
-{
- printf(
- "usage:\n"
- " blogc "
-#ifdef MAKE_EMBEDDED
- "[-m] "
-#endif
- "[-h] [-v] [-d] [-i] [-l [-e SOURCE]] [-D KEY=VALUE ...] [-p KEY]\n"
- " [-t TEMPLATE] [-o OUTPUT] [SOURCE ...] - A blog compiler.\n"
- "\n"
- "positional arguments:\n"
- " SOURCE source file(s)\n"
- "\n"
- "optional arguments:\n"
- " -h show this help message and exit\n"
- " -v show version and exit\n"
- " -d enable debug\n"
- " -i read list of source files from standard input\n"
- " -l build listing page, from multiple source files\n"
- " -e SOURCE source file with content for listing page. requires '-l'\n"
- " -D KEY=VALUE set global variable\n"
- " -p KEY show the value of a variable after source parsing and exit\n"
- " -t TEMPLATE template file\n"
- " -o OUTPUT output file\n"
-#ifdef MAKE_EMBEDDED
- " -m call and pass arguments to embedded blogc-make\n"
-#endif
- );
-}
-
-
-static void
-blogc_print_usage(void)
-{
- printf(
- "usage: blogc "
-#ifdef MAKE_EMBEDDED
- "[-m] "
-#endif
- "[-h] [-v] [-d] [-i] [-l [-e SOURCE]] [-D KEY=VALUE ...] [-p KEY]\n"
- " [-t TEMPLATE] [-o OUTPUT] [SOURCE ...]\n");
-}
-
-
-static void
-blogc_mkdir_recursive(const char *filename)
-{
- char *fname = bc_strdup(filename);
- for (char *tmp = fname; *tmp != '\0'; tmp++) {
- if (*tmp != '/' && *tmp != '\\')
- continue;
-#ifdef HAVE_SYS_STAT_H
- char bkp = *tmp;
- *tmp = '\0';
- if ((strlen(fname) > 0) &&
-#if defined(WIN32) || defined(_WIN32)
- (-1 == mkdir(fname)) &&
-#else
- (-1 == mkdir(fname, 0777)) &&
-#endif
- (errno != EEXIST))
- {
- fprintf(stderr, "blogc: error: failed to create output "
- "directory (%s): %s\n", fname, strerror(errno));
- free(fname);
- exit(2);
- }
- *tmp = bkp;
-#else
- // FIXME: show this warning only if actually trying to create a directory.
- fprintf(stderr, "blogc: warning: can't create output directories "
- "for your platform. please create the directories yourself.\n");
- break;
-#endif
- }
- free(fname);
-}
-
-
-int
-main(int argc, char **argv)
-{
- setlocale(LC_ALL, "");
-
- int rv = 0;
-
-#ifdef MAKE_EMBEDDED
- bool embedded = false;
-#endif
- bool debug = false;
- bool input_stdin = false;
- bool listing = false;
- char *template = NULL;
- char *output = NULL;
- char *print = NULL;
- char *tmp = NULL;
- char **pieces = NULL;
-
- bc_slist_t *sources = NULL;
- bc_slist_t *listing_entries = NULL;
- bc_slist_t *listing_entries_source = NULL;
- bc_trie_t *config = bc_trie_new(free);
- bc_trie_insert(config, "BLOGC_VERSION", bc_strdup(PACKAGE_VERSION));
-
- for (size_t i = 1; i < argc; i++) {
- tmp = NULL;
- if (argv[i][0] == '-') {
- switch (argv[i][1]) {
- case 'h':
- blogc_print_help();
- goto cleanup;
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- goto cleanup;
- case 'd':
- debug = true;
- break;
- case 'i':
- input_stdin = true;
- break;
- case 'l':
- listing = true;
- break;
- case 'e':
- if (argv[i][2] != '\0')
- listing_entries = bc_slist_append(listing_entries, bc_strdup(argv[i] + 2));
- else if (i + 1 < argc)
- listing_entries = bc_slist_append(listing_entries, bc_strdup(argv[++i]));
- break;
- case 't':
- if (argv[i][2] != '\0')
- template = bc_strdup(argv[i] + 2);
- else if (i + 1 < argc)
- template = bc_strdup(argv[++i]);
- break;
- case 'o':
- if (argv[i][2] != '\0')
- output = bc_strdup(argv[i] + 2);
- else if (i + 1 < argc)
- output = bc_strdup(argv[++i]);
- break;
- case 'p':
- if (argv[i][2] != '\0')
- print = bc_strdup(argv[i] + 2);
- else if (i + 1 < argc)
- print = bc_strdup(argv[++i]);
- break;
- case 'D':
- if (argv[i][2] != '\0')
- tmp = argv[i] + 2;
- else if (i + 1 < argc)
- tmp = argv[++i];
- if (tmp != NULL) {
- if (!bc_utf8_validate((uint8_t*) tmp, strlen(tmp))) {
- fprintf(stderr, "blogc: error: invalid value for "
- "-D (must be valid UTF-8 string): %s\n", tmp);
- goto cleanup;
- }
- pieces = bc_str_split(tmp, '=', 2);
- if (bc_strv_length(pieces) != 2) {
- fprintf(stderr, "blogc: error: invalid value for "
- "-D (must have an '='): %s\n", tmp);
- bc_strv_free(pieces);
- rv = 1;
- goto cleanup;
- }
- for (size_t j = 0; pieces[0][j] != '\0'; j++) {
- char c = pieces[0][j];
- if (j == 0) {
- if (!(c >= 'A' && c <= 'Z')) {
- fprintf(stderr, "blogc: error: invalid value "
- "for -D (first character in configuration "
- "key must be uppercase): %s\n", pieces[0]);
- bc_strv_free(pieces);
- rv = 1;
- goto cleanup;
- }
- continue;
- }
- if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
- fprintf(stderr, "blogc: error: invalid value "
- "for -D (configuration key must be uppercase "
- "with '_' and digits after first character): %s\n",
- pieces[0]);
- bc_strv_free(pieces);
- rv = 1;
- goto cleanup;
- }
- }
- bc_trie_insert(config, pieces[0], bc_strdup(pieces[1]));
- bc_strv_free(pieces);
- pieces = NULL;
- }
- break;
-#ifdef MAKE_EMBEDDED
- case 'm':
- embedded = true;
- break;
-#endif
- default:
- blogc_print_usage();
- fprintf(stderr, "blogc: error: invalid argument: -%c\n",
- argv[i][1]);
- rv = 1;
- goto cleanup;
- }
- }
- else {
- sources = bc_slist_append(sources, bc_strdup(argv[i]));
- }
-
-#ifdef MAKE_EMBEDDED
- if (embedded) {
- rv = bm_main(argc, argv);
- goto cleanup;
- }
-#endif
-
- }
-
- if (input_stdin) {
- size_t input_len;
- char *input = bc_stdin_read(&input_len);
- bc_slist_t *input_list = blogc_filelist_parse(input, input_len);
- free(input);
- sources = bc_slist_append_list(sources, input_list);
- }
-
- if (!listing && bc_slist_length(sources) == 0) {
- blogc_print_usage();
- fprintf(stderr, "blogc: error: one source file is required\n");
- rv = 1;
- goto cleanup;
- }
-
- if (!listing && bc_slist_length(sources) > 1) {
- blogc_print_usage();
- fprintf(stderr, "blogc: error: only one source file should be provided, "
- "if running without '-l'\n");
- rv = 1;
- goto cleanup;
- }
-
- bc_error_t *err = NULL;
-
- bc_slist_t *s = blogc_source_parse_from_files(config, sources, &err);
- if (err != NULL) {
- bc_error_print(err, "blogc");
- rv = 1;
- goto cleanup2;
- }
-
- if (listing) {
- for (bc_slist_t *tmp = listing_entries; tmp != NULL; tmp = tmp->next) {
- if (0 == strlen(tmp->data)) {
- listing_entries_source = bc_slist_append(listing_entries_source, NULL);
- continue;
- }
- bc_trie_t *e = blogc_source_parse_from_file(config, tmp->data, &err);
- if (err != NULL) {
- bc_error_print(err, "blogc");
- rv = 1;
- goto cleanup2;
- }
- listing_entries_source = bc_slist_append(listing_entries_source, e);
- }
- }
-
- if (print != NULL) {
- bc_trie_t *local = NULL;
- if (!listing && s != NULL) {
- local = s->data;
- }
- char *val = blogc_format_variable(print, config, local, NULL, NULL);
- if (val == NULL) {
- fprintf(stderr, "blogc: error: variable not found: %s\n",
- print);
- rv = EX_CONFIG;
- }
- else {
- printf("%s\n", val);
- }
- free(val);
- goto cleanup2;
- }
-
- if (template == NULL) {
- blogc_print_usage();
- fprintf(stderr, "blogc: error: argument -t is required when rendering content\n");
- rv = 1;
- goto cleanup2;
- }
-
- bc_slist_t* l = blogc_template_parse_from_file(template, &err);
- if (err != NULL) {
- bc_error_print(err, "blogc");
- rv = 1;
- goto cleanup3;
- }
-
- if (debug)
- blogc_debug_template(l);
-
- char *out = blogc_render(l, s, listing_entries_source, config, listing);
-
- bool write_to_stdout = (output == NULL || (0 == strcmp(output, "-")));
-
- FILE *fp = stdout;
- if (!write_to_stdout) {
- blogc_mkdir_recursive(output);
- fp = fopen(output, "w");
- if (fp == NULL) {
- fprintf(stderr, "blogc: error: failed to open output file (%s): %s\n",
- output, strerror(errno));
- rv = 1;
- goto cleanup4;
- }
- }
-
- if (out != NULL)
- fprintf(fp, "%s", out);
-
- if (!write_to_stdout)
- fclose(fp);
-
-cleanup4:
- free(out);
-cleanup3:
- blogc_template_free_ast(l);
-cleanup2:
- bc_slist_free_full(s, (bc_free_func_t) bc_trie_free);
- bc_error_free(err);
-cleanup:
- bc_trie_free(config);
- free(template);
- free(output);
- free(print);
- bc_slist_free_full(listing_entries, free);
- bc_slist_free_full(listing_entries_source, (bc_free_func_t) bc_trie_free);
- bc_slist_free_full(sources, free);
- return rv;
-}
diff --git a/src/blogc/renderer.h b/src/blogc/renderer.h
deleted file mode 100644
index 77c660a..0000000
--- a/src/blogc/renderer.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _RENDERER_H
-#define _RENDERER_H
-
-#include <stdbool.h>
-#include "../common/utils.h"
-
-const char* blogc_get_variable(const char *name, bc_trie_t *global, bc_trie_t *local);
-char* blogc_format_date(const char *date, bc_trie_t *global, bc_trie_t *local);
-char* blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
- const char *foreach_name, bc_slist_t *foreach_var);
-bc_slist_t* blogc_split_list_variable(const char *name, bc_trie_t *global,
- bc_trie_t *local);
-char* blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
- bc_trie_t *config, bool listing);
-
-#endif /* _RENDERER_H */
diff --git a/src/blogc/rusage.c b/src/blogc/rusage.c
deleted file mode 100644
index a38848d..0000000
--- a/src/blogc/rusage.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif /* HAVE_SYS_TIME_H */
-
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif /* HAVE_SYS_RESOURCE_H */
-
-#include <stdlib.h>
-#include "../common/utils.h"
-#include "rusage.h"
-
-
-blogc_rusage_t*
-blogc_rusage_get(void)
-{
-#ifndef HAVE_RUSAGE
- return NULL;
-#else
- struct rusage usage;
- if (0 != getrusage(RUSAGE_SELF, &usage))
- return NULL;
-
- blogc_rusage_t *rv = bc_malloc(sizeof(blogc_rusage_t));
- rv->cpu_time = (
- (usage.ru_utime.tv_sec * 1000000) + usage.ru_utime.tv_usec +
- (usage.ru_stime.tv_sec * 1000000) + usage.ru_stime.tv_usec);
- rv->memory = usage.ru_maxrss;
-
- return rv;
-#endif
-}
-
-
-char*
-blogc_rusage_format_cpu_time(long long time)
-{
- if (time >= 1000000)
- return bc_strdup_printf("%.3fs", ((float) time) / 1000000.0);
-
- // this is a special case: some systems may report the cpu time rounded up to the
- // milisecond. it is useless to show ".000" in this case.
- if (time >= 1000)
- return bc_strdup_printf("%.*fms", time % 1000 ? 3 : 0, ((float) time) / 1000.0);
-
- return bc_strdup_printf("%dus", time);
-}
-
-
-char*
-blogc_rusage_format_memory(long mem)
-{
- if (mem >= 1048576)
- return bc_strdup_printf("%.3fGB", ((float) mem) / 1048576.0);
- if (mem >= 1024)
- return bc_strdup_printf("%.3fMB", ((float) mem) / 1024.0);
- return bc_strdup_printf("%dKB", mem);
-}
-
-
-void
-blogc_rusage_inject(bc_trie_t *global)
-{
- blogc_rusage_t *usage = blogc_rusage_get();
- if (usage == NULL)
- return;
-
- bc_trie_insert(global, "BLOGC_RUSAGE_CPU_TIME",
- blogc_rusage_format_cpu_time(usage->cpu_time));
- bc_trie_insert(global, "BLOGC_RUSAGE_MEMORY",
- blogc_rusage_format_memory(usage->memory));
-
- free(usage);
-}
diff --git a/src/blogc/rusage.h b/src/blogc/rusage.h
deleted file mode 100644
index 6344066..0000000
--- a/src/blogc/rusage.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef ___RUSAGE_H
-#define ___RUSAGE_H
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_SYS_TIME_H
-#ifdef HAVE_SYS_RESOURCE_H
-#define HAVE_RUSAGE 1
-#endif
-#endif
-
-#include "../common/utils.h"
-
-typedef struct {
- long long cpu_time; // in microseconds
- long memory; // in kilobytes
-} blogc_rusage_t;
-
-blogc_rusage_t* blogc_rusage_get(void);
-
-char* blogc_rusage_format_cpu_time(long long time);
-char* blogc_rusage_format_memory(long mem);
-
-void blogc_rusage_inject(bc_trie_t *global);
-
-#endif /* ___RUSAGE_H */
diff --git a/src/blogc/sysinfo.c b/src/blogc/sysinfo.c
deleted file mode 100644
index 3c30996..0000000
--- a/src/blogc/sysinfo.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#ifdef HAVE_TIME_H
-#include <time.h>
-#endif /* HAVE_TIME_H */
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include "../common/error.h"
-#include "../common/file.h"
-#include "../common/utils.h"
-#include "sysinfo.h"
-
-
-char*
-blogc_sysinfo_get_hostname(void)
-{
-#ifndef HAVE_SYSINFO_HOSTNAME
- return NULL;
-#else
- char buf[1024]; // could be 256 according to gethostname(2), but *shrug*.
- buf[1023] = '\0';
- if (-1 == gethostname(buf, 1024))
- return NULL;
-
-#ifdef HAVE_NETDB_H
- struct hostent *h = gethostbyname(buf);
- if (h != NULL && h->h_name != NULL)
- return bc_strdup(h->h_name);
-#endif
-
- // FIXME: return FQDN instead of local host name
- return bc_strdup(buf);
-#endif
-}
-
-
-void
-blogc_sysinfo_inject_hostname(bc_trie_t *global)
-{
- char *hostname = blogc_sysinfo_get_hostname();
- if (hostname == NULL)
- return;
-
- bc_trie_insert(global, "BLOGC_SYSINFO_HOSTNAME", hostname);
-}
-
-
-char*
-blogc_sysinfo_get_username(void)
-{
- return bc_strdup(getenv("LOGNAME"));
-}
-
-
-void
-blogc_sysinfo_inject_username(bc_trie_t *global)
-{
- char *username = blogc_sysinfo_get_username();
- if (username == NULL)
- return;
-
- bc_trie_insert(global, "BLOGC_SYSINFO_USERNAME", username);
-}
-
-
-char*
-blogc_sysinfo_get_datetime(void)
-{
-#ifndef HAVE_SYSINFO_DATETIME
- return NULL;
-#else
- time_t tmp;
- if (-1 == time(&tmp))
- return NULL;
-
- struct tm *t = gmtime(&tmp);
- if (t == NULL)
- return NULL;
-
- char buf[1024];
- if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", t))
- return NULL;
-
- return bc_strdup(buf);
-#endif
-}
-
-
-void
-blogc_sysinfo_inject_datetime(bc_trie_t *global)
-{
- char *t = blogc_sysinfo_get_datetime();
- if (t == NULL)
- return;
-
- bc_trie_insert(global, "BLOGC_SYSINFO_DATETIME", t);
-}
-
-
-// it is obviously impossible that the same process runs inside and outside
-// docker at the same time, then an unprotected global variable should be fine
-// here
-static bool inside_docker_evaluated = false;
-static bool inside_docker = false;
-
-bool
-blogc_sysinfo_get_inside_docker(void)
-{
- if (inside_docker_evaluated)
- return inside_docker;
- inside_docker_evaluated = true;
-
- size_t len;
- bc_error_t *err = NULL;
- char *contents = bc_file_get_contents("/proc/1/cgroup", false, &len, &err);
- if (err != NULL) {
- bc_error_free(err);
- inside_docker = false;
- return inside_docker;
- }
-
- bool inside_docker = NULL != strstr(contents, "/docker/");
- free(contents);
- return inside_docker;
-}
-
-
-void
-blogc_sysinfo_inject_inside_docker(bc_trie_t *global)
-{
- if (blogc_sysinfo_get_inside_docker())
- bc_trie_insert(global, "BLOGC_SYSINFO_INSIDE_DOCKER", bc_strdup("1"));
-}
diff --git a/src/blogc/sysinfo.h b/src/blogc/sysinfo.h
deleted file mode 100644
index b249661..0000000
--- a/src/blogc/sysinfo.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef ___SYSINFO_H
-#define ___SYSINFO_H
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_UNISTD_H
-#ifdef HAVE_GETHOSTNAME
-#define HAVE_SYSINFO_HOSTNAME 1
-#endif /* HAVE_GETHOSTNAME */
-#endif /* HAVE_UNISTD_H */
-
-#ifdef HAVE_TIME_H
-#define HAVE_SYSINFO_DATETIME 1
-#endif /* HAVE_TIME_H */
-
-#include <stdbool.h>
-#include "../common/utils.h"
-
-char* blogc_sysinfo_get_hostname(void);
-void blogc_sysinfo_inject_hostname(bc_trie_t *global);
-char* blogc_sysinfo_get_username(void);
-void blogc_sysinfo_inject_username(bc_trie_t *global);
-char* blogc_sysinfo_get_datetime(void);
-void blogc_sysinfo_inject_datetime(bc_trie_t *global);
-bool blogc_sysinfo_get_inside_docker(void);
-void blogc_sysinfo_inject_inside_docker(bc_trie_t *global);
-
-#endif /* ___SYSINFO_H */
diff --git a/src/blogc/template-parser.h b/src/blogc/template-parser.h
deleted file mode 100644
index 6183016..0000000
--- a/src/blogc/template-parser.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _TEMPLATE_PARSER_H
-#define _TEMPLATE_PARSER_H
-
-#include <stddef.h>
-#include "../common/error.h"
-#include "../common/utils.h"
-
-/*
- * note: whitespace cleaners are NOT added to AST. we fix strings right during
- * template parsing. renderer does not need to care about it, for the sake of
- * simplicity.
- *
- * another note: technically this is not an AST, because it is not a tree. duh!
- */
-typedef enum {
- BLOGC_TEMPLATE_NODE_IFDEF = 1,
- BLOGC_TEMPLATE_NODE_IFNDEF,
- BLOGC_TEMPLATE_NODE_IF,
- BLOGC_TEMPLATE_NODE_ELSE,
- BLOGC_TEMPLATE_NODE_ENDIF,
- BLOGC_TEMPLATE_NODE_FOREACH,
- BLOGC_TEMPLATE_NODE_ENDFOREACH,
- BLOGC_TEMPLATE_NODE_BLOCK,
- BLOGC_TEMPLATE_NODE_ENDBLOCK,
- BLOGC_TEMPLATE_NODE_VARIABLE,
- BLOGC_TEMPLATE_NODE_CONTENT,
-} blogc_template_node_type_t;
-
-typedef enum {
- BLOGC_TEMPLATE_OP_NEQ = 1 << 0,
- BLOGC_TEMPLATE_OP_EQ = 1 << 1,
- BLOGC_TEMPLATE_OP_LT = 1 << 2,
- BLOGC_TEMPLATE_OP_GT = 1 << 3,
-} blogc_template_operator_t;
-
-typedef struct {
- blogc_template_node_type_t type;
- blogc_template_operator_t op;
-
- // 2 slots to store node data.
- char *data[2];
-
- bc_slist_t *childs;
-} blogc_template_node_t;
-
-bc_slist_t* blogc_template_parse(const char *src, size_t src_len,
- bc_error_t **err);
-void blogc_template_free_ast(bc_slist_t *ast);
-
-#endif /* _TEMPLATE_PARSER_H */
diff --git a/src/blogc/toctree.c b/src/blogc/toctree.c
deleted file mode 100644
index 307c62c..0000000
--- a/src/blogc/toctree.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <stdlib.h>
-#include "../common/utils.h"
-#include "toctree.h"
-
-bc_slist_t*
-blogc_toctree_append(bc_slist_t *headers, size_t level, const char *slug, const char *text)
-{
- if (level == 0)
- return headers;
-
- blogc_toctree_header_t *t = bc_malloc(sizeof(blogc_toctree_header_t));
- t->level = level;
- t->slug = bc_strdup(slug);
- t->text = bc_strdup(text);
- return bc_slist_append(headers, t);
-}
-
-
-char*
-blogc_toctree_render(bc_slist_t *headers, int maxdepth, const char *endl)
-{
- if (headers == NULL || maxdepth == 0)
- return NULL;
-
- // find lower level
- size_t lower_level = 0;
- for (bc_slist_t *l = headers; l != NULL; l = l->next) {
- size_t lv = ((blogc_toctree_header_t*) l->data)->level;
- if (lower_level == 0 || lower_level > lv) {
- lower_level = lv;
- }
- }
-
- if (lower_level == 0)
- return NULL;
-
- // render
- bc_string_t *rv = bc_string_new();
- bc_string_append_printf(rv, "<ul>%s", endl == NULL ? "\n" : endl);
- size_t spacing = 4;
- size_t current_level = lower_level;
- for (bc_slist_t *l = headers; l != NULL; l = l->next) {
- blogc_toctree_header_t *t = l->data;
- if (t->level - lower_level >= maxdepth) {
- continue;
- }
- while (current_level > t->level) {
- spacing -= 4;
- bc_string_append_printf(rv, "%*s</ul>%s", spacing, "",
- endl == NULL ? "\n" : endl);
- current_level--;
- }
- while (current_level < t->level) {
- bc_string_append_printf(rv, "%*s<ul>%s", spacing, "",
- endl == NULL ? "\n" : endl);
- current_level++;
- spacing += 4;
- }
- bc_string_append_printf(rv, "%*s<li>", spacing, "");
- if (t->slug != NULL) {
- bc_string_append_printf(rv, "<a href=\"#%s\">%s</a>", t->slug,
- t->text != NULL ? t->text : "");
- }
- else {
- bc_string_append(rv, t->text);
- }
- bc_string_append_printf(rv, "</li>%s", endl == NULL ? "\n" : endl);
- }
-
- // close leftovers
- while (current_level >= lower_level) {
- spacing -= 4;
- bc_string_append_printf(rv, "%*s</ul>%s", spacing, "",
- endl == NULL ? "\n" : endl);
- current_level--;
- }
-
- return bc_string_free(rv, false);
-}
-
-
-static void
-free_header(blogc_toctree_header_t *h)
-{
- free(h->slug);
- free(h->text);
- free(h);
-}
-
-
-void
-blogc_toctree_free(bc_slist_t *l)
-{
- bc_slist_free_full(l, (bc_free_func_t) free_header);
-}
diff --git a/src/blogc/toctree.h b/src/blogc/toctree.h
deleted file mode 100644
index 460119b..0000000
--- a/src/blogc/toctree.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef ___TOCTREE_H
-#define ___TOCTREE_H
-
-#include "../common/utils.h"
-
-typedef struct {
- size_t level;
- char *slug;
- char *text;
-} blogc_toctree_header_t;
-
-bc_slist_t* blogc_toctree_append(bc_slist_t *headers, size_t level,
- const char *slug, const char *text);
-char* blogc_toctree_render(bc_slist_t *headers, int maxdepth,
- const char *endl);
-void blogc_toctree_free(bc_slist_t *l);
-
-#endif /* ___TOCTREE_H */
diff --git a/src/common/compat.c b/src/common/compat.c
deleted file mode 100644
index f7394c8..0000000
--- a/src/common/compat.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif /* HAVE_SYS_WAIT_H */
-
-#include <signal.h>
-#include "compat.h"
-
-
-int
-bc_compat_status_code(int waitstatus)
-{
- int rv = waitstatus;
-#if defined(WIFEXITED) && defined(WEXITSTATUS) && defined(WIFSIGNALED) && defined(WTERMSIG)
- if (WIFEXITED(waitstatus)) {
- rv = WEXITSTATUS(waitstatus);
- }
- else if (WIFSIGNALED(waitstatus)) {
- rv = WTERMSIG(waitstatus);
- rv += 128;
- }
-#elif defined(WIN32) || defined(_WIN32)
- if (waitstatus == 3) {
- rv = SIGTERM + 128; // can't get signal on windows.
- }
-#endif
- return rv;
-}
diff --git a/src/common/compat.h b/src/common/compat.h
deleted file mode 100644
index 1e743db..0000000
--- a/src/common/compat.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _COMPAT_H
-#define _COMPAT_H
-
-int bc_compat_status_code(int waitstatus);
-
-#endif /* _COMPAT_H */
diff --git a/src/common/config-parser.c b/src/common/config-parser.c
deleted file mode 100644
index fbb79af..0000000
--- a/src/common/config-parser.c
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 <stdlib.h>
-#include <string.h>
-#include "error.h"
-#include "utils.h"
-#include "config-parser.h"
-
-
-typedef enum {
- CONFIG_START = 1,
- CONFIG_SECTION_START,
- CONFIG_SECTION,
- CONFIG_SECTION_KEY,
- CONFIG_SECTION_VALUE_START,
- CONFIG_SECTION_VALUE_QUOTE,
- CONFIG_SECTION_VALUE_POST_QUOTED,
- CONFIG_SECTION_VALUE,
- CONFIG_SECTION_LIST_START,
- CONFIG_SECTION_LIST_QUOTE,
- CONFIG_SECTION_LIST_POST_QUOTED,
- CONFIG_SECTION_LIST,
-} bc_configparser_state_t;
-
-typedef enum {
- CONFIG_SECTION_TYPE_MAP = 1,
- CONFIG_SECTION_TYPE_LIST,
-} bc_configparser_section_type_t;
-
-typedef struct {
- bc_configparser_section_type_t type;
- void *data;
-} bc_configparser_section_t;
-
-
-static void
-free_section(bc_configparser_section_t *section)
-{
- if (section == NULL)
- return;
-
- switch (section->type) {
- case CONFIG_SECTION_TYPE_MAP:
- bc_trie_free(section->data);
- break;
- case CONFIG_SECTION_TYPE_LIST:
- bc_slist_free_full(section->data, free);
- break;
- }
- free(section);
-}
-
-
-bc_config_t*
-bc_config_parse(const char *src, size_t src_len, const char *list_sections[],
- bc_error_t **err)
-{
- if (err == NULL || *err != NULL)
- return NULL;
-
- size_t current = 0;
- size_t start = 0;
-
- bc_configparser_section_t *section = NULL;
-
- char *section_name = NULL;
- char *key = NULL;
- bc_string_t *value = NULL;
- bool escaped = false;
-
- bc_config_t *rv = bc_malloc(sizeof(bc_config_t));
- rv->root = bc_trie_new((bc_free_func_t) free_section);
-
- bc_configparser_state_t state = CONFIG_START;
-
- while (current < src_len) {
- char c = src[current];
- bool is_last = current == src_len - 1;
-
- if (escaped) {
- bc_string_append_c(value, c);
- escaped = false;
- current++;
- continue;
- }
-
- if (value != NULL && c == '\\') {
- escaped = true;
- current++;
- continue;
- }
-
- switch (state) {
-
- case CONFIG_START:
- if (c == '#' || c == ';') {
- while (current < src_len) {
- if (src[current] == '\r' || src[current] == '\n')
- break;
- current++;
- }
- break;
- }
- if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
- break;
- if (c == '[') {
- state = CONFIG_SECTION_START;
- break;
- }
- if (section != NULL) {
- start = current;
- switch (section->type) {
- case CONFIG_SECTION_TYPE_MAP:
- state = CONFIG_SECTION_KEY;
- break;
- case CONFIG_SECTION_TYPE_LIST:
- state = CONFIG_SECTION_LIST_START;
- if (value == NULL)
- value = bc_string_new();
- break;
- }
- continue;
- }
- *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
- current, "File must start with section.");
- break;
-
- case CONFIG_SECTION_START:
- start = current;
- state = CONFIG_SECTION;
- break;
-
- case CONFIG_SECTION:
- if (c == ']') {
- section_name = bc_strndup(src + start, current - start);
- section = bc_malloc(sizeof(bc_configparser_section_t));
- section->type = CONFIG_SECTION_TYPE_MAP;
- if (list_sections != NULL) {
- for (size_t i = 0; list_sections[i] != NULL; i++) {
- if (0 == strcmp(section_name, list_sections[i])) {
- section->type = CONFIG_SECTION_TYPE_LIST;
- break;
- }
- }
- }
- switch (section->type) {
- case CONFIG_SECTION_TYPE_MAP:
- section->data = bc_trie_new(free);
- break;
- case CONFIG_SECTION_TYPE_LIST:
- section->data = NULL;
- break;
- }
- bc_trie_insert(rv->root, section_name, section);
- free(section_name);
- section_name = NULL;
- state = CONFIG_START;
- break;
- }
- if (c != '\r' && c != '\n')
- break;
- *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
- current, "Section names can't have new lines.");
- break;
-
- case CONFIG_SECTION_KEY:
- if (c == '=') {
- key = bc_strndup(src + start, current - start);
- state = CONFIG_SECTION_VALUE_START;
- if (is_last) {
- bc_trie_insert(section->data, bc_str_strip(key),
- bc_strdup(""));
- free(key);
- key = NULL;
- break;
- }
- if (value == NULL)
- value = bc_string_new();
- break;
- }
- if (c != '\r' && c != '\n' && !is_last)
- break;
- // key without value, should we support it?
- size_t end = is_last && c != '\n' && c != '\r' ? src_len :
- current;
- key = bc_strndup(src + start, end - start);
- *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
- current, "Key without value: %s.", key);
- free(key);
- key = NULL;
- break;
-
- case CONFIG_SECTION_VALUE_START:
- if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
- break;
- if (c == '"') {
- state = CONFIG_SECTION_VALUE_QUOTE;
- break;
- }
- if (c == '\r' || c == '\n' || is_last) {
- state = CONFIG_SECTION_VALUE;
- continue;
- }
- bc_string_append_c(value, c);
- state = CONFIG_SECTION_VALUE;
- break;
-
- case CONFIG_SECTION_VALUE_QUOTE:
- if (c == '"') {
- bc_trie_insert(section->data, bc_str_strip(key),
- bc_string_free(value, false));
- free(key);
- key = NULL;
- value = NULL;
- state = CONFIG_SECTION_VALUE_POST_QUOTED;
- break;
- }
- bc_string_append_c(value, c);
- break;
-
- case CONFIG_SECTION_VALUE_POST_QUOTED:
- if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
- break;
- if (c == '\r' || c == '\n' || is_last) {
- state = CONFIG_START;
- break;
- }
- *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
- current, "Invalid value for key, should not have anything "
- "after quotes.");
- break;
-
- case CONFIG_SECTION_VALUE:
- if (c == '\r' || c == '\n' || is_last) {
- if (is_last && c != '\r' && c != '\n')
- bc_string_append_c(value, c);
- bc_trie_insert(section->data, bc_str_strip(key),
- bc_strdup(bc_str_rstrip(value->str)));
- free(key);
- key = NULL;
- bc_string_free(value, true);
- value = NULL;
- state = CONFIG_START;
- break;
- }
- bc_string_append_c(value, c);
- break;
-
- case CONFIG_SECTION_LIST_START:
- if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
- break;
- if (c == '"') {
- state = CONFIG_SECTION_LIST_QUOTE;
- break;
- }
- bc_string_append_c(value, c);
- state = CONFIG_SECTION_LIST;
- break;
-
- case CONFIG_SECTION_LIST_QUOTE:
- if (c == '"') {
- section->data = bc_slist_append(section->data,
- bc_string_free(value, false));
- value = NULL;
- state = CONFIG_SECTION_LIST_POST_QUOTED;
- break;
-
- }
- bc_string_append_c(value, c);
- break;
-
- case CONFIG_SECTION_LIST_POST_QUOTED:
- if (c == ' ' || c == '\t' || c == '\f' || c == '\v')
- break;
- if (c == '\r' || c == '\n' || is_last) {
- state = CONFIG_START;
- break;
- }
- *err = bc_error_parser(BC_ERROR_CONFIG_PARSER, src, src_len,
- current, "Invalid value for list item, should not have "
- "anything after quotes.");
- break;
-
- case CONFIG_SECTION_LIST:
- if (c == '\r' || c == '\n' || is_last) {
- if (is_last && c != '\r' && c != '\n')
- bc_string_append_c(value, c);
- section->data = bc_slist_append(section->data,
- bc_strdup(bc_str_strip(value->str)));
- bc_string_free(value, true);
- value = NULL;
- state = CONFIG_START;
- break;
-
- }
- bc_string_append_c(value, c);
- break;
-
- }
-
- if (*err != NULL) {
- bc_config_free(rv);
- rv = NULL;
- break;
- }
-
- current++;
- }
-
- free(section_name);
- free(key);
- bc_string_free(value, true);
-
- return rv;
-}
-
-
-static void
-list_keys(const char *key, const char *value, bc_slist_t **l)
-{
- *l = bc_slist_append(*l, bc_strdup(key));
-}
-
-
-char**
-bc_config_list_sections(bc_config_t *config)
-{
- if (config == NULL)
- return NULL;
-
- bc_slist_t *l = NULL;
- bc_trie_foreach(config->root, (bc_trie_foreach_func_t) list_keys, &l);
-
- char **rv = bc_malloc(sizeof(char*) * (bc_slist_length(l) + 1));
-
- size_t i = 0;
- for (bc_slist_t *tmp = l; tmp != NULL; tmp = tmp->next, i++)
- rv[i] = tmp->data;
- rv[i] = NULL;
-
- bc_slist_free(l);
-
- return rv;
-}
-
-
-char**
-bc_config_list_keys(bc_config_t *config, const char *section)
-{
- if (config == NULL)
- return NULL;
-
- const bc_configparser_section_t *s = bc_trie_lookup(config->root, section);
- if (s == NULL)
- return NULL;
-
- if (s->type != CONFIG_SECTION_TYPE_MAP)
- return NULL;
-
- bc_slist_t *l = NULL;
- bc_trie_foreach(s->data, (bc_trie_foreach_func_t) list_keys, &l);
-
- char **rv = bc_malloc(sizeof(char*) * (bc_slist_length(l) + 1));
-
- size_t i = 0;
- for (bc_slist_t *tmp = l; tmp != NULL; tmp = tmp->next, i++)
- rv[i] = tmp->data;
- rv[i] = NULL;
-
- bc_slist_free(l);
-
- return rv;
-}
-
-
-const char*
-bc_config_get(bc_config_t *config, const char *section, const char *key)
-{
- if (config == NULL)
- return NULL;
-
- const bc_configparser_section_t *s = bc_trie_lookup(config->root, section);
- if (s == NULL)
- return NULL;
-
- if (s->type != CONFIG_SECTION_TYPE_MAP)
- return NULL;
-
- return bc_trie_lookup(s->data, key);
-}
-
-
-const char*
-bc_config_get_with_default(bc_config_t *config, const char *section, const char *key,
- const char *default_)
-{
- const char *rv = bc_config_get(config, section, key);
- if (rv == NULL)
- return default_;
- return rv;
-}
-
-
-char**
-bc_config_get_list(bc_config_t *config, const char *section)
-{
- if (config == NULL)
- return NULL;
-
- const bc_configparser_section_t *s = bc_trie_lookup(config->root, section);
- if (s == NULL)
- return NULL;
-
- if (s->type != CONFIG_SECTION_TYPE_LIST)
- return NULL;
-
- char **rv = bc_malloc(sizeof(char*) * (bc_slist_length(s->data) + 1));
-
- size_t i = 0;
- for (bc_slist_t *tmp = s->data; tmp != NULL; tmp = tmp->next, i++)
- rv[i] = bc_strdup(tmp->data);
- rv[i] = NULL;
-
- return rv;
-}
-
-
-void
-bc_config_free(bc_config_t *config)
-{
- if (config == NULL)
- return;
- bc_trie_free(config->root);
- free(config);
-}
diff --git a/src/common/config-parser.h b/src/common/config-parser.h
deleted file mode 100644
index 0b75ff8..0000000
--- a/src/common/config-parser.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _CONFIG_PARSER_H
-#define _CONFIG_PARSER_H
-
-#include <stddef.h>
-#include "utils.h"
-#include "error.h"
-
-typedef struct {
- bc_trie_t *root;
-} bc_config_t;
-
-bc_config_t* bc_config_parse(const char *src, size_t src_len,
- const char *list_sections[], bc_error_t **err);
-char** bc_config_list_sections(bc_config_t *config);
-char** bc_config_list_keys(bc_config_t *config, const char *section);
-const char* bc_config_get(bc_config_t *config, const char *section,
- const char *key);
-const char* bc_config_get_with_default(bc_config_t *config, const char *section,
- const char *key, const char *default_);
-char** bc_config_get_list(bc_config_t *config, const char *section);
-void bc_config_free(bc_config_t *config);
-
-#endif /* _CONFIG_PARSER_H */
diff --git a/src/common/error.h b/src/common/error.h
deleted file mode 100644
index b85f006..0000000
--- a/src/common/error.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _ERROR_H
-#define _ERROR_H
-
-#include <stddef.h>
-
-// error handling is centralized here for the sake of simplicity :/
-typedef enum {
-
- // errors for src/common
- BC_ERROR_CONFIG_PARSER = 1,
- BC_ERROR_FILE,
-
- // errors for src/blogc
- BLOGC_ERROR_SOURCE_PARSER = 100,
- BLOGC_ERROR_TEMPLATE_PARSER,
- BLOGC_ERROR_LOADER,
- BLOGC_WARNING_DATETIME_PARSER,
-
- // errors for src/blogc-make
- BLOGC_MAKE_ERROR_SETTINGS = 300,
- BLOGC_MAKE_ERROR_EXEC,
- BLOGC_MAKE_ERROR_ATOM,
- BLOGC_MAKE_ERROR_UTILS,
-
-} bc_error_type_t;
-
-typedef struct {
- char *msg;
- bc_error_type_t type;
-} bc_error_t;
-
-bc_error_t* bc_error_new(bc_error_type_t type, const char *msg);
-bc_error_t* bc_error_new_printf(bc_error_type_t type, const char *format, ...);
-bc_error_t* bc_error_parser(bc_error_type_t type, const char *src,
- size_t src_len, size_t current, const char *format, ...);
-void bc_error_print(bc_error_t *err, const char *prefix);
-void bc_error_free(bc_error_t *err);
-
-#endif /* _ERROR_H */
diff --git a/src/common/file.c b/src/common/file.c
deleted file mode 100644
index 1e55f64..0000000
--- a/src/common/file.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include "file.h"
-#include "error.h"
-#include "utf8.h"
-#include "utils.h"
-
-
-char*
-bc_file_get_contents(const char *path, bool utf8, size_t *len, bc_error_t **err)
-{
- if (path == NULL || len == NULL || err == NULL || *err != NULL)
- return NULL;
-
- *len = 0;
- FILE *fp = fopen(path, "r");
-
- if (fp == NULL) {
- int tmp_errno = errno;
- *err = bc_error_new_printf(BC_ERROR_FILE,
- "Failed to open file (%s): %s", path, strerror(tmp_errno));
- return NULL;
- }
-
- bc_string_t *str = bc_string_new();
- char buffer[BC_FILE_CHUNK_SIZE];
- char *tmp;
-
- while (!feof(fp)) {
- size_t read_len = fread(buffer, sizeof(char), BC_FILE_CHUNK_SIZE, fp);
-
- tmp = buffer;
-
- if (utf8 && str->len == 0 && read_len > 0) {
- // skipping BOM before validation, for performance. should be safe
- // enough
- size_t skip = bc_utf8_skip_bom((uint8_t*) buffer, read_len);
- read_len -= skip;
- tmp += skip;
- }
-
- *len += read_len;
- bc_string_append_len(str, tmp, read_len);
- }
- fclose(fp);
-
- if (utf8 && !bc_utf8_validate_str(str)) {
- *err = bc_error_new_printf(BC_ERROR_FILE,
- "File content is not valid UTF-8: %s", path);
- bc_string_free(str, true);
- return NULL;
- }
-
- return bc_string_free(str, false);
-}
diff --git a/src/common/file.h b/src/common/file.h
deleted file mode 100644
index d002bf7..0000000
--- a/src/common/file.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _FILE_H
-#define _FILE_H
-
-#include <stddef.h>
-#include <stdbool.h>
-#include "error.h"
-
-#define BC_FILE_CHUNK_SIZE 1024
-
-char* bc_file_get_contents(const char *path, bool utf8, size_t *len, bc_error_t **err);
-
-#endif /* _FILE_H */
diff --git a/src/common/sort.c b/src/common/sort.c
deleted file mode 100644
index 3e24706..0000000
--- a/src/common/sort.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 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 "utils.h"
-#include "sort.h"
-
-
-bc_slist_t*
-bc_slist_sort(bc_slist_t *l, bc_sort_func_t cmp)
-{
- if (l == NULL) {
- return NULL;
- }
-
- bool swapped = false;
- bc_slist_t *lptr = NULL;
- bc_slist_t *rptr = NULL;
-
- do {
- swapped = false;
- lptr = l;
-
- while (lptr->next != rptr) {
- if (0 < cmp(lptr->data, lptr->next->data)) {
- void *tmp = lptr->data;
- lptr->data = lptr->next->data;
- lptr->next->data = tmp;
- swapped = true;
- }
-
- lptr = lptr->next;
- }
-
- rptr = lptr;
- } while(swapped);
-
- return l;
-}
diff --git a/src/common/sort.h b/src/common/sort.h
deleted file mode 100644
index 9f4dda2..0000000
--- a/src/common/sort.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _SORT_H
-#define _SORT_H
-
-#include "utils.h"
-
-typedef int (*bc_sort_func_t) (const void *a, const void *b);
-
-bc_slist_t* bc_slist_sort(bc_slist_t *l, bc_sort_func_t cmp);
-
-#endif /* _SORT_H */
diff --git a/src/common/stdin.c b/src/common/stdin.c
deleted file mode 100644
index 556c7cf..0000000
--- a/src/common/stdin.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 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 "utils.h"
-#include "stdin.h"
-
-
-// splitted in single file to make it easier to test
-char*
-bc_stdin_read(size_t *len)
-{
- if (len == NULL)
- return NULL;
-
- int c;
- bc_string_t *rv = bc_string_new();
- while (EOF != (c = fgetc(stdin)))
- bc_string_append_c(rv, c);
- *len = rv->len;
- return bc_string_free(rv, false);
-}
diff --git a/src/common/stdin.h b/src/common/stdin.h
deleted file mode 100644
index b27ecaf..0000000
--- a/src/common/stdin.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _STDIN_H
-#define _STDIN_H
-
-#include <stddef.h>
-
-char* bc_stdin_read(size_t *len);
-
-#endif /* _STDIN_H */
diff --git a/src/common/utils.h b/src/common/utils.h
deleted file mode 100644
index 4bdab8c..0000000
--- a/src/common/utils.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifndef _UTILS_H
-#define _UTILS_H
-
-#include <stddef.h>
-#include <stdarg.h>
-#include <stdbool.h>
-
-
-// memory
-
-typedef void (*bc_free_func_t) (void *ptr);
-
-void* bc_malloc(size_t size);
-void* bc_realloc(void *ptr, size_t size);
-
-
-// slist
-
-typedef struct _bc_slist_t {
- struct _bc_slist_t *next;
- void *data;
-} bc_slist_t;
-
-bc_slist_t* bc_slist_append(bc_slist_t *l, void *data);
-bc_slist_t* bc_slist_prepend(bc_slist_t *l, void *data);
-bc_slist_t* bc_slist_append_list(bc_slist_t *l, bc_slist_t *n);
-void bc_slist_free(bc_slist_t *l);
-void bc_slist_free_full(bc_slist_t *l, bc_free_func_t free_func);
-size_t bc_slist_length(bc_slist_t *l);
-
-
-// strfuncs
-
-char* bc_strdup(const char *s);
-char* bc_strndup(const char *s, size_t n);
-char* bc_strdup_vprintf(const char *format, va_list ap);
-char* bc_strdup_printf(const char *format, ...);
-bool bc_isspace(int c);
-bool bc_str_starts_with(const char *str, const char *prefix);
-bool bc_str_ends_with(const char *str, const char *suffix);
-char* bc_str_lstrip(char *str);
-char* bc_str_rstrip(char *str);
-char* bc_str_strip(char *str);
-char** bc_str_split(const char *str, char c, size_t max_pieces);
-char* bc_str_replace(const char *str, const char search, const char *replace);
-char* bc_str_find(const char *str, char c);
-bool bc_str_to_bool(const char *str);
-void bc_strv_free(char **strv);
-char* bc_strv_join(char **strv, const char *separator);
-size_t bc_strv_length(char **strv);
-
-
-// string
-
-typedef struct {
- char *str;
- size_t len;
- size_t allocated_len;
-} bc_string_t;
-
-bc_string_t* bc_string_new(void);
-char* bc_string_free(bc_string_t *str, bool free_str);
-bc_string_t* bc_string_dup(bc_string_t *str);
-bc_string_t* bc_string_append_len(bc_string_t *str, const char *suffix, size_t len);
-bc_string_t* bc_string_append(bc_string_t *str, const char *suffix);
-bc_string_t* bc_string_append_c(bc_string_t *str, char c);
-bc_string_t* bc_string_append_printf(bc_string_t *str, const char *format, ...);
-bc_string_t* bc_string_append_escaped(bc_string_t *str, const char *suffix);
-
-
-// trie
-
-typedef struct _bc_trie_node_t {
- char key;
- void *data;
- struct _bc_trie_node_t *next, *child;
-} bc_trie_node_t;
-
-struct _bc_trie_t {
- bc_trie_node_t *root;
- bc_free_func_t free_func;
-};
-
-typedef struct _bc_trie_t bc_trie_t;
-
-typedef void (*bc_trie_foreach_func_t)(const char *key, void *data,
- void *user_data);
-
-bc_trie_t* bc_trie_new(bc_free_func_t free_func);
-void bc_trie_free(bc_trie_t *trie);
-void bc_trie_insert(bc_trie_t *trie, const char *key, void *data);
-void* bc_trie_lookup(bc_trie_t *trie, const char *key);
-size_t bc_trie_size(bc_trie_t *trie);
-void bc_trie_foreach(bc_trie_t *trie, bc_trie_foreach_func_t func,
- void *user_data);
-
-
-// shell
-
-char* bc_shell_quote(const char *command);
-
-#endif /* _UTILS_H */
diff --git a/src/blogc/content-parser.c b/src/content-parser.c
index a42f6f6..410fc90 100644
--- a/src/blogc/content-parser.c
+++ b/src/content-parser.c
@@ -1,18 +1,18 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-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 <stdlib.h>
#include <string.h>
#include "content-parser.h"
-#include "toctree.h"
-#include "../common/utils.h"
+#include "utils.h"
// this is a half ass implementation of a markdown-like syntax. bugs are
// expected. feel free to improve the parser and add new features.
@@ -23,7 +23,7 @@ blogc_slugify(const char *str)
{
if (str == NULL)
return NULL;
- char *new_str = bc_strdup(str);
+ char *new_str = sb_strdup(str);
int diff = 'a' - 'A'; // just to avoid magic numbers
for (size_t i = 0; new_str[i] != '\0'; i++) {
if (new_str[i] >= 'a' && new_str[i] <= 'z')
@@ -61,13 +61,13 @@ htmlentities(char c)
static void
-htmlentities_append(bc_string_t *str, char c)
+htmlentities_append(sb_string_t *str, char c)
{
const char *e = htmlentities(c);
if (e == NULL)
- bc_string_append_c(str, c);
+ sb_string_append_c(str, c);
else
- bc_string_append(str, e);
+ sb_string_append(str, e);
}
@@ -76,10 +76,10 @@ blogc_htmlentities(const char *str)
{
if (str == NULL)
return NULL;
- bc_string_t *rv = bc_string_new();
+ sb_string_t *rv = sb_string_new();
for (size_t i = 0; str[i] != '\0'; i++)
htmlentities_append(rv, str[i]);
- return bc_string_free(rv, false);
+ return sb_string_free(rv, false);
}
@@ -88,7 +88,7 @@ blogc_fix_description(const char *paragraph)
{
if (paragraph == NULL)
return NULL;
- bc_string_t *rv = bc_string_new();
+ sb_string_t *rv = sb_string_new();
bool last = false;
bool newline = false;
char *tmp = NULL;
@@ -102,12 +102,12 @@ blogc_fix_description(const char *paragraph)
case '\n':
if (newline)
break;
- tmp = bc_strndup(paragraph + start, current - start);
- bc_string_append(rv, bc_str_strip(tmp));
+ tmp = sb_strndup(paragraph + start, current - start);
+ sb_string_append(rv, sb_str_strip(tmp));
free(tmp);
tmp = NULL;
if (!last)
- bc_string_append_c(rv, ' ');
+ sb_string_append_c(rv, ' ');
start = current + 1;
newline = true;
break;
@@ -118,8 +118,8 @@ blogc_fix_description(const char *paragraph)
break;
current++;
}
- tmp = blogc_htmlentities(bc_str_strip(rv->str));
- bc_string_free(rv, true);
+ tmp = blogc_htmlentities(sb_str_strip(rv->str));
+ sb_string_free(rv, true);
return tmp;
}
@@ -139,8 +139,7 @@ typedef enum {
CONTENT_CODE,
CONTENT_CODE_START,
CONTENT_CODE_END,
- CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE_OR_EMPHASIS,
- CONTENT_HORIZONTAL_RULE_OR_EMPHASIS,
+ CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE,
CONTENT_HORIZONTAL_RULE,
CONTENT_UNORDERED_LIST_START,
CONTENT_UNORDERED_LIST_END,
@@ -191,7 +190,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
size_t start_link = 0;
char *link1 = NULL;
- bc_string_t *rv = bc_string_new();
+ sb_string_t *rv = sb_string_new();
blogc_content_parser_inline_state_t state = CONTENT_INLINE_START;
@@ -245,15 +244,15 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
state = CONTENT_INLINE_ASTERISK_DOUBLE;
break;
}
- tmp = bc_str_find(src + current, '*');
+ tmp = sb_str_find(src + current, '*');
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '*');
+ sb_string_append_c(rv, '*');
state = CONTENT_INLINE_START;
continue;
}
tmp2 = blogc_content_parse_inline_internal(
src + current, (tmp - src) - current);
- bc_string_append_printf(rv, "<em>%s</em>", tmp2);
+ sb_string_append_printf(rv, "<em>%s</em>", tmp2);
current = tmp - src;
tmp = NULL;
free(tmp2);
@@ -264,21 +263,21 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_ASTERISK_DOUBLE:
tmp = src + current;
do {
- tmp = bc_str_find(tmp, '*');
+ tmp = sb_str_find(tmp, '*');
if (((tmp - src) < src_len) && *(tmp + 1) == '*') {
break;
}
tmp++;
} while (tmp != NULL && (tmp - src) < src_len);
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '*');
- bc_string_append_c(rv, '*');
+ sb_string_append_c(rv, '*');
+ sb_string_append_c(rv, '*');
state = CONTENT_INLINE_START;
continue;
}
tmp2 = blogc_content_parse_inline_internal(
src + current, (tmp - src) - current);
- bc_string_append_printf(rv, "<strong>%s</strong>", tmp2);
+ sb_string_append_printf(rv, "<strong>%s</strong>", tmp2);
current = tmp - src + 1;
tmp = NULL;
free(tmp2);
@@ -291,15 +290,15 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
state = CONTENT_INLINE_UNDERSCORE_DOUBLE;
break;
}
- tmp = bc_str_find(src + current, '_');
+ tmp = sb_str_find(src + current, '_');
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '_');
+ sb_string_append_c(rv, '_');
state = CONTENT_INLINE_START;
continue;
}
tmp2 = blogc_content_parse_inline_internal(
src + current, (tmp - src) - current);
- bc_string_append_printf(rv, "<em>%s</em>", tmp2);
+ sb_string_append_printf(rv, "<em>%s</em>", tmp2);
current = tmp - src;
tmp = NULL;
free(tmp2);
@@ -310,21 +309,21 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_UNDERSCORE_DOUBLE:
tmp = src + current;
do {
- tmp = bc_str_find(tmp, '_');
+ tmp = sb_str_find(tmp, '_');
if (((tmp - src) < src_len) && *(tmp + 1) == '_') {
break;
}
tmp++;
} while (tmp != NULL && (tmp - src) < src_len);
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '_');
- bc_string_append_c(rv, '_');
+ sb_string_append_c(rv, '_');
+ sb_string_append_c(rv, '_');
state = CONTENT_INLINE_START;
continue;
}
tmp2 = blogc_content_parse_inline_internal(
src + current, (tmp - src) - current);
- bc_string_append_printf(rv, "<strong>%s</strong>", tmp2);
+ sb_string_append_printf(rv, "<strong>%s</strong>", tmp2);
current = tmp - src + 1;
tmp = NULL;
free(tmp2);
@@ -337,19 +336,19 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
state = CONTENT_INLINE_BACKTICKS_DOUBLE;
break;
}
- tmp = bc_str_find(src + current, '`');
+ tmp = sb_str_find(src + current, '`');
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '`');
+ sb_string_append_c(rv, '`');
state = CONTENT_INLINE_START;
continue;
}
- tmp3 = bc_strndup(src + current, (tmp - src) - current);
+ tmp3 = sb_strndup(src + current, (tmp - src) - current);
tmp2 = blogc_htmlentities(tmp3);
free(tmp3);
tmp3 = NULL;
- bc_string_append(rv, "<code>");
- bc_string_append(rv, tmp2);
- bc_string_append(rv, "</code>");
+ sb_string_append(rv, "<code>");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append(rv, "</code>");
current = tmp - src;
tmp = NULL;
free(tmp2);
@@ -360,25 +359,25 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_BACKTICKS_DOUBLE:
tmp = src + current;
do {
- tmp = bc_str_find(tmp, '`');
+ tmp = sb_str_find(tmp, '`');
if (((tmp - src) < src_len) && *(tmp + 1) == '`') {
break;
}
tmp++;
} while (tmp != NULL && (tmp - src) < src_len);
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '`');
- bc_string_append_c(rv, '`');
+ sb_string_append_c(rv, '`');
+ sb_string_append_c(rv, '`');
state = CONTENT_INLINE_START;
continue;
}
- tmp3 = bc_strndup(src + current, (tmp - src) - current);
+ tmp3 = sb_strndup(src + current, (tmp - src) - current);
tmp2 = blogc_htmlentities(tmp3);
free(tmp3);
tmp3 = NULL;
- bc_string_append(rv, "<code>");
- bc_string_append(rv, tmp2);
- bc_string_append(rv, "</code>");
+ sb_string_append(rv, "<code>");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append(rv, "</code>");
current = tmp - src + 1;
tmp = NULL;
free(tmp2);
@@ -399,24 +398,24 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_LINK_AUTO:
tmp = src + current;
do {
- tmp = bc_str_find(tmp, ']');
+ tmp = sb_str_find(tmp, ']');
if (((tmp - src) < src_len) && *(tmp + 1) == ']') {
break;
}
tmp++;
} while (tmp != NULL && (tmp - src) < src_len);
if (tmp == NULL || ((tmp - src) >= src_len)) {
- bc_string_append_c(rv, '[');
- bc_string_append_c(rv, '[');
+ sb_string_append_c(rv, '[');
+ sb_string_append_c(rv, '[');
state = CONTENT_INLINE_START;
continue;
}
- tmp2 = bc_strndup(src + current, (tmp - src) - current);
- bc_string_append(rv, "<a href=\"");
- bc_string_append_escaped(rv, tmp2);
- bc_string_append(rv, "\">");
- bc_string_append_escaped(rv, tmp2);
- bc_string_append(rv, "</a>");
+ tmp2 = sb_strndup(src + current, (tmp - src) - current);
+ sb_string_append(rv, "<a href=\"");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append(rv, "\">");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append(rv, "</a>");
current = tmp - src + 1;
tmp = NULL;
free(tmp2);
@@ -435,7 +434,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
}
if (c == ']') {
if (--count == 0) {
- link1 = bc_strndup(src + start_link, current - start_link);
+ link1 = sb_strndup(src + start_link, current - start_link);
state = CONTENT_INLINE_LINK_URL_START;
}
}
@@ -449,7 +448,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
start = current + 1;
break;
}
- bc_string_append_c(rv, '[');
+ sb_string_append_c(rv, '[');
state = CONTENT_INLINE_START;
current = start_link;
start_link = 0;
@@ -461,13 +460,13 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
break;
}
if (c == ')') {
- tmp2 = bc_strndup(src + start, current - start);
+ tmp2 = sb_strndup(src + start, current - start);
tmp3 = blogc_content_parse_inline(link1);
free(link1);
link1 = NULL;
- bc_string_append(rv, "<a href=\"");
- bc_string_append_escaped(rv, tmp2);
- bc_string_append_printf(rv, "\">%s</a>", tmp3);
+ sb_string_append(rv, "<a href=\"");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append_printf(rv, "\">%s</a>", tmp3);
free(tmp2);
tmp2 = NULL;
free(tmp3);
@@ -484,7 +483,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
start_link = current + 1;
break;
}
- bc_string_append_c(rv, '!');
+ sb_string_append_c(rv, '!');
state = CONTENT_INLINE_START;
continue;
@@ -494,7 +493,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
break;
}
if (c == ']') {
- link1 = bc_strndup(src + start_link, current - start_link);
+ link1 = sb_strndup(src + start_link, current - start_link);
state = CONTENT_INLINE_IMAGE_URL_START;
}
break;
@@ -507,8 +506,8 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
start = current + 1;
break;
}
- bc_string_append_c(rv, '!');
- bc_string_append_c(rv, '[');
+ sb_string_append_c(rv, '!');
+ sb_string_append_c(rv, '[');
state = CONTENT_INLINE_START;
current = start_link;
start_link = 0;
@@ -520,12 +519,12 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
break;
}
if (c == ')') {
- tmp2 = bc_strndup(src + start, current - start);
- bc_string_append(rv, "<img src=\"");
- bc_string_append_escaped(rv, tmp2);
- bc_string_append(rv, "\" alt=\"");
- bc_string_append_escaped(rv, link1);
- bc_string_append(rv, "\">");
+ tmp2 = sb_strndup(src + start, current - start);
+ sb_string_append(rv, "<img src=\"");
+ sb_string_append_escaped(rv, tmp2);
+ sb_string_append(rv, "\" alt=\"");
+ sb_string_append_escaped(rv, link1);
+ sb_string_append(rv, "\">");
free(tmp2);
tmp2 = NULL;
free(link1);
@@ -538,31 +537,31 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_ENDASH:
if (c == '-') {
if (is_last) {
- bc_string_append(rv, "&ndash;");
+ sb_string_append(rv, "&ndash;");
state = CONTENT_INLINE_START; // wat
break;
}
state = CONTENT_INLINE_EMDASH;
break;
}
- bc_string_append_c(rv, '-');
+ sb_string_append_c(rv, '-');
state = CONTENT_INLINE_START;
continue;
case CONTENT_INLINE_EMDASH:
if (c == '-') {
- bc_string_append(rv, "&mdash;");
+ sb_string_append(rv, "&mdash;");
state = CONTENT_INLINE_START;
break;
}
- bc_string_append(rv, "&ndash;");
+ sb_string_append(rv, "&ndash;");
state = CONTENT_INLINE_START;
continue;
case CONTENT_INLINE_LINE_BREAK_START:
if (c == ' ') {
if (is_last) {
- bc_string_append(rv, "<br />");
+ sb_string_append(rv, "<br />");
state = CONTENT_INLINE_START; // wat
break;
}
@@ -570,14 +569,14 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
state = CONTENT_INLINE_LINE_BREAK;
break;
}
- bc_string_append_c(rv, ' ');
+ sb_string_append_c(rv, ' ');
state = CONTENT_INLINE_START;
continue;
case CONTENT_INLINE_LINE_BREAK:
if (c == ' ') {
if (is_last) {
- bc_string_append(rv, "<br />");
+ sb_string_append(rv, "<br />");
state = CONTENT_INLINE_START; // wat
break;
}
@@ -585,12 +584,12 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
break;
}
if (c == '\n' || c == '\r') {
- bc_string_append_printf(rv, "<br />%c", c);
+ sb_string_append_printf(rv, "<br />%c", c);
state = CONTENT_INLINE_START;
break;
}
for (size_t i = 0; i < count; i++)
- bc_string_append_c(rv, ' ');
+ sb_string_append_c(rv, ' ');
state = CONTENT_INLINE_START;
continue;
}
@@ -605,14 +604,14 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
case CONTENT_INLINE_IMAGE_ALT:
case CONTENT_INLINE_IMAGE_URL_START:
case CONTENT_INLINE_IMAGE_URL:
- bc_string_append_c(rv, '!');
+ sb_string_append_c(rv, '!');
case CONTENT_INLINE_LINK_CONTENT:
case CONTENT_INLINE_LINK_URL_START:
case CONTENT_INLINE_LINK_URL:
tmp2 = blogc_content_parse_inline(src + start_link);
- bc_string_append_c(rv, '[');
- bc_string_append_escaped(rv, tmp2); // no need to free, as it wil be done below.
+ sb_string_append_c(rv, '[');
+ sb_string_append_escaped(rv, tmp2); // no need to free, as it wil be done below.
break;
// add all the other states here explicitly, so the compiler helps us
@@ -637,7 +636,7 @@ blogc_content_parse_inline_internal(const char *src, size_t src_len)
free(tmp3);
free(link1);
- return bc_string_free(rv, false);
+ return sb_string_free(rv, false);
}
@@ -675,9 +674,36 @@ blogc_is_ordered_list_item(const char *str, size_t prefix_len)
}
-char*
-blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
- char **description, char **endl, bc_slist_t **headers)
+static blogc_content_node_t*
+block_node_new(blogc_content_block_type_t type, char *content, sb_trie_t *parameters)
+{
+ blogc_content_node_t *rv = sb_malloc(sizeof(blogc_content_node_t));
+ rv->node_type = BLOGC_CONTENT_BLOCK;
+ rv->type.block_type = type;
+ rv->content = content;
+ rv->parameters = parameters;
+ rv->child = NULL;
+ rv->next = NULL;
+ return rv;
+}
+
+
+static blogc_content_node_t*
+inline_node_new(blogc_content_inline_type_t type, char *content, sb_trie_t *parameters)
+{
+ blogc_content_node_t *rv = sb_malloc(sizeof(blogc_content_node_t));
+ rv->node_type = BLOGC_CONTENT_INLINE;
+ rv->type.inline_type = type;
+ rv->content = content;
+ rv->parameters = parameters;
+ rv->child = NULL;
+ rv->next = NULL;
+ return rv;
+}
+
+
+blogc_content_node_t*
+blogc_content_parse_ast(const char *src, char **nl)
{
// src is always nul-terminated.
size_t src_len = strlen(src);
@@ -686,47 +712,30 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
size_t start = 0;
size_t start2 = 0;
size_t end = 0;
- size_t eend = 0;
size_t real_end = 0;
- size_t header_level = 0;
+ unsigned int header_level = 0;
char *prefix = NULL;
size_t prefix_len = 0;
char *tmp = NULL;
char *tmp2 = NULL;
char *parsed = NULL;
- char *slug = NULL;
-
- char *line_ending = NULL;
- bool line_ending_found = false;
- if (endl != NULL) {
- if (*endl != NULL) {
- line_ending_found = true;
- }
- else {
- *endl = bc_malloc(3 * sizeof(char));
- }
- line_ending = *endl;
- }
- else {
- line_ending = bc_malloc(3 * sizeof(char));
- }
// this isn't empty because we need some reasonable default value in the
// unlikely case that we need to print some line ending before evaluating
// the "real" value.
- if (!line_ending_found) {
- line_ending[0] = '\n';
- line_ending[1] = '\0';
- }
+ char line_ending[3] = "\n";
+ bool line_ending_found = false;
char d = '\0';
- bc_slist_t *lines = NULL;
- bc_slist_t *lines2 = NULL;
+ sb_slist_t *lines = NULL;
+ sb_slist_t *lines2 = NULL;
- bc_string_t *rv = bc_string_new();
- bc_string_t *tmp_str = NULL;
+ sb_string_t *tmp_str = NULL;
+
+ blogc_content_node_t *ast = NULL;
+ blogc_content_node_t *last = NULL;
blogc_content_parser_state_t state = CONTENT_START_LINE;
@@ -764,11 +773,8 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
break;
start = current;
if (c == '.') {
- if (end_excerpt != NULL) {
- eend = rv->len; // fuck it
- state = CONTENT_EXCERPT;
- break;
- }
+ state = CONTENT_EXCERPT;
+ break;
}
if (c == '#') {
header_level = 1;
@@ -777,7 +783,7 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
}
if (c == '*' || c == '+' || c == '-') {
start2 = current;
- state = CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE_OR_EMPHASIS;
+ state = CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE;
d = c;
break;
}
@@ -804,27 +810,28 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
break;
case CONTENT_EXCERPT:
- if (end_excerpt != NULL) {
- if (c == '.')
- break;
- if (c == '\n' || c == '\r') {
- state = CONTENT_EXCERPT_END;
- break;
- }
+ if (c == '.')
+ break;
+ if (c == '\n' || c == '\r') {
+ state = CONTENT_EXCERPT_END;
+ break;
}
- eend = 0;
state = CONTENT_PARAGRAPH;
break;
case CONTENT_EXCERPT_END:
- if (end_excerpt != NULL) {
- if (c == '\n' || c == '\r') {
- *end_excerpt = eend;
- state = CONTENT_START_LINE;
- break;
+ if (c == '\n' || c == '\r') {
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_EXCERPT, NULL, NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_EXCERPT, NULL, NULL);
+ last = last->next;
}
+ state = CONTENT_START_LINE;
+ break;
}
- eend = 0;
state = CONTENT_PARAGRAPH_END;
break;
@@ -853,23 +860,17 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
end = is_last && c != '\n' && c != '\r' ? src_len :
(real_end != 0 ? real_end : current);
- tmp = bc_strndup(src + start, end - start);
- if (first_header != NULL && *first_header == NULL)
- *first_header = blogc_htmlentities(tmp);
- parsed = blogc_content_parse_inline(tmp);
- slug = blogc_slugify(tmp);
- if (headers != NULL)
- *headers = blogc_toctree_append(*headers, header_level, slug, parsed);
- if (slug == NULL)
- bc_string_append_printf(rv, "<h%d>%s</h%d>%s",
- header_level, parsed, header_level, line_ending);
- else
- bc_string_append_printf(rv, "<h%d id=\"%s\">%s</h%d>%s",
- header_level, slug, parsed, header_level,
- line_ending);
- free(slug);
- free(parsed);
- parsed = NULL;
+ tmp = sb_strndup(src + start, end - start);
+ sb_trie_t *t = sb_trie_new(free);
+ sb_trie_insert(t, "level", sb_strdup_printf("%d", header_level));
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_HEADER, blogc_content_parse_inline(tmp), t);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_HEADER, blogc_content_parse_inline(tmp), t); // TODO: inline-me
+ last = last->next;
+ }
free(tmp);
tmp = NULL;
state = CONTENT_START_LINE;
@@ -888,10 +889,16 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_HTML_END:
if (c == '\n' || c == '\r' || is_last) {
- tmp = bc_strndup(src + start, end - start);
- bc_string_append_printf(rv, "%s%s", tmp, line_ending);
- free(tmp);
- tmp = NULL;
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_RAW,
+ sb_strndup(src + start, end - start), NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_RAW,
+ sb_strndup(src + start, end - start), NULL);
+ last = last->next;
+ }
state = CONTENT_START_LINE;
start = current;
}
@@ -902,7 +909,7 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_BLOCKQUOTE:
if (c == ' ' || c == '\t')
break;
- prefix = bc_strndup(src + start, current - start);
+ prefix = sb_strndup(src + start, current - start);
state = CONTENT_BLOCKQUOTE_START;
break;
@@ -910,16 +917,16 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
end = is_last && c != '\n' && c != '\r' ? src_len :
(real_end != 0 ? real_end : current);
- tmp = bc_strndup(src + start2, end - start2);
- if (bc_str_starts_with(tmp, prefix)) {
- lines = bc_slist_append(lines, bc_strdup(tmp + strlen(prefix)));
+ tmp = sb_strndup(src + start2, end - start2);
+ if (sb_str_starts_with(tmp, prefix)) {
+ lines = sb_slist_append(lines, sb_strdup(tmp + strlen(prefix)));
state = CONTENT_BLOCKQUOTE_END;
}
else {
state = CONTENT_PARAGRAPH;
free(prefix);
prefix = NULL;
- bc_slist_free_full(lines, free);
+ sb_slist_free_full(lines, free);
lines = NULL;
if (is_last) {
free(tmp);
@@ -935,21 +942,25 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_BLOCKQUOTE_END:
if (c == '\n' || c == '\r' || is_last) {
- tmp_str = bc_string_new();
- for (bc_slist_t *l = lines; l != NULL; l = l->next)
- bc_string_append_printf(tmp_str, "%s%s", l->data,
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines; l != NULL; l = l->next)
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
- // do not propagate title and description to blockquote parsing,
- // because we just want paragraphs from first level of
- // content.
- tmp = blogc_content_parse(tmp_str->str, NULL, NULL, NULL, endl, NULL);
- bc_string_append_printf(rv, "<blockquote>%s</blockquote>%s",
- tmp, line_ending);
- free(tmp);
- tmp = NULL;
- bc_string_free(tmp_str, true);
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_BLOCKQUOTE,
+ NULL, NULL);
+ ast->child = blogc_content_parse_ast(tmp_str->str, nl);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_BLOCKQUOTE,
+ NULL, NULL);
+ last->next->child = blogc_content_parse_ast(tmp_str->str, nl);
+ last = last->next;
+ }
+ sb_string_free(tmp_str, true);
tmp_str = NULL;
- bc_slist_free_full(lines, free);
+ sb_slist_free_full(lines, free);
lines = NULL;
free(prefix);
prefix = NULL;
@@ -965,7 +976,7 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_CODE:
if (c == ' ' || c == '\t')
break;
- prefix = bc_strndup(src + start, current - start);
+ prefix = sb_strndup(src + start, current - start);
state = CONTENT_CODE_START;
break;
@@ -973,16 +984,16 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
end = is_last && c != '\n' && c != '\r' ? src_len :
(real_end != 0 ? real_end : current);
- tmp = bc_strndup(src + start2, end - start2);
- if (bc_str_starts_with(tmp, prefix)) {
- lines = bc_slist_append(lines, bc_strdup(tmp + strlen(prefix)));
+ tmp = sb_strndup(src + start2, end - start2);
+ if (sb_str_starts_with(tmp, prefix)) {
+ lines = sb_slist_append(lines, sb_strdup(tmp + strlen(prefix)));
state = CONTENT_CODE_END;
}
else {
state = CONTENT_PARAGRAPH;
free(prefix);
prefix = NULL;
- bc_slist_free_full(lines, free);
+ sb_slist_free_full(lines, free);
lines = NULL;
free(tmp);
tmp = NULL;
@@ -998,18 +1009,26 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_CODE_END:
if (c == '\n' || c == '\r' || is_last) {
- bc_string_append(rv, "<pre><code>");
- for (bc_slist_t *l = lines; l != NULL; l = l->next) {
- char *tmp_line = blogc_htmlentities(l->data);
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines; l != NULL; l = l->next) {
if (l->next == NULL)
- bc_string_append_printf(rv, "%s", tmp_line);
+ sb_string_append_printf(tmp_str, "%s", l->data);
else
- bc_string_append_printf(rv, "%s%s", tmp_line,
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
- free(tmp_line);
}
- bc_string_append_printf(rv, "</code></pre>%s", line_ending);
- bc_slist_free_full(lines, free);
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_CODE,
+ sb_string_free(tmp_str, false), NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_CODE,
+ sb_string_free(tmp_str, false), NULL);
+ last = last->next;
+ }
+ tmp_str = NULL;
+ sb_slist_free_full(lines, free);
lines = NULL;
free(prefix);
prefix = NULL;
@@ -1022,33 +1041,17 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
}
break;
- case CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE_OR_EMPHASIS:
- if (current == start+1) {
- if (c == d) { // horizontal rule or '**' emphasis
- state = CONTENT_HORIZONTAL_RULE_OR_EMPHASIS;
- break;
- }
- else if (c != ' ' && c != '\t' && d == '*') { // is '*' emphasis
- state = CONTENT_PARAGRAPH;
- break;
- }
- }
- if (c == ' ' || c == '\t')
- break;
- prefix = bc_strndup(src + start, current - start);
- state = CONTENT_UNORDERED_LIST_START;
- break;
-
- case CONTENT_HORIZONTAL_RULE_OR_EMPHASIS:
- // 3rd '-' or '*' required for a horizontal rule
+ case CONTENT_UNORDERED_LIST_OR_HORIZONTAL_RULE:
if (c == d) {
state = CONTENT_HORIZONTAL_RULE;
if (is_last)
continue;
break;
}
- // is '**' emphasis
- state = CONTENT_PARAGRAPH;
+ if (c == ' ' || c == '\t')
+ break;
+ prefix = sb_strndup(src + start, current - start);
+ state = CONTENT_UNORDERED_LIST_START;
break;
case CONTENT_HORIZONTAL_RULE:
@@ -1056,7 +1059,16 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
break;
}
if (c == '\n' || c == '\r' || is_last) {
- bc_string_append_printf(rv, "<hr />%s", line_ending);
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_HORIZONTAL_RULE,
+ NULL, NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_HORIZONTAL_RULE,
+ NULL, NULL);
+ last = last->next;
+ }
state = CONTENT_START_LINE;
start = current;
d = '\0';
@@ -1069,30 +1081,30 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
end = is_last && c != '\n' && c != '\r' ? src_len :
(real_end != 0 ? real_end : current);
- tmp = bc_strndup(src + start2, end - start2);
- tmp2 = bc_strdup_printf("%-*s", strlen(prefix), "");
- if (bc_str_starts_with(tmp, prefix)) {
+ tmp = sb_strndup(src + start2, end - start2);
+ tmp2 = sb_strdup_printf("%-*s", strlen(prefix), "");
+ if (sb_str_starts_with(tmp, prefix)) {
if (lines2 != NULL) {
- tmp_str = bc_string_new();
- for (bc_slist_t *l = lines2; l != NULL; l = l->next) {
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines2; l != NULL; l = l->next) {
if (l->next == NULL)
- bc_string_append_printf(tmp_str, "%s", l->data);
+ sb_string_append_printf(tmp_str, "%s", l->data);
else
- bc_string_append_printf(tmp_str, "%s%s", l->data,
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
}
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines2, free);
lines2 = NULL;
parsed = blogc_content_parse_inline(tmp_str->str);
- bc_string_free(tmp_str, true);
- lines = bc_slist_append(lines, bc_strdup(parsed));
+ sb_string_free(tmp_str, true);
+ lines = sb_slist_append(lines, sb_strdup(parsed));
free(parsed);
parsed = NULL;
}
- lines2 = bc_slist_append(lines2, bc_strdup(tmp + strlen(prefix)));
+ lines2 = sb_slist_append(lines2, sb_strdup(tmp + strlen(prefix)));
}
- else if (bc_str_starts_with(tmp, tmp2)) {
- lines2 = bc_slist_append(lines2, bc_strdup(tmp + strlen(prefix)));
+ else if (sb_str_starts_with(tmp, tmp2)) {
+ lines2 = sb_slist_append(lines2, sb_strdup(tmp + strlen(prefix)));
}
else {
state = CONTENT_PARAGRAPH_END;
@@ -1102,8 +1114,8 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
tmp2 = NULL;
free(prefix);
prefix = NULL;
- bc_slist_free_full(lines, free);
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines, free);
+ sb_slist_free_full(lines2, free);
lines = NULL;
if (is_last)
continue;
@@ -1122,28 +1134,46 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
if (lines2 != NULL) {
// FIXME: avoid repeting the code below
- tmp_str = bc_string_new();
- for (bc_slist_t *l = lines2; l != NULL; l = l->next) {
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines2; l != NULL; l = l->next) {
if (l->next == NULL)
- bc_string_append_printf(tmp_str, "%s", l->data);
+ sb_string_append_printf(tmp_str, "%s", l->data);
else
- bc_string_append_printf(tmp_str, "%s%s", l->data,
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
}
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines2, free);
lines2 = NULL;
parsed = blogc_content_parse_inline(tmp_str->str);
- bc_string_free(tmp_str, true);
- lines = bc_slist_append(lines, bc_strdup(parsed));
+ sb_string_free(tmp_str, true);
+ lines = sb_slist_append(lines, sb_strdup(parsed));
free(parsed);
parsed = NULL;
}
- bc_string_append_printf(rv, "<ul>%s", line_ending);
- for (bc_slist_t *l = lines; l != NULL; l = l->next)
- bc_string_append_printf(rv, "<li>%s</li>%s", l->data,
- line_ending);
- bc_string_append_printf(rv, "</ul>%s", line_ending);
- bc_slist_free_full(lines, free);
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_UNORDERED_LIST,
+ NULL, NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_UNORDERED_LIST,
+ NULL, NULL);
+ last = last->next;
+ }
+ blogc_content_node_t *last_list = NULL;
+ for (sb_slist_t *l = lines; l != NULL; l = l->next) {
+ if (last_list == NULL) {
+ last->child = block_node_new(BLOGC_CONTENT_BLOCK_LIST_ITEM,
+ l->data, NULL);
+ last_list = last->child;
+ }
+ else {
+ last_list->next = block_node_new(BLOGC_CONTENT_BLOCK_LIST_ITEM,
+ l->data, NULL);
+ last_list = last_list->next;
+ }
+ }
+ sb_slist_free(lines);
lines = NULL;
free(prefix);
prefix = NULL;
@@ -1180,30 +1210,30 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
end = is_last && c != '\n' && c != '\r' ? src_len :
(real_end != 0 ? real_end : current);
- tmp = bc_strndup(src + start2, end - start2);
- tmp2 = bc_strdup_printf("%-*s", prefix_len, "");
+ tmp = sb_strndup(src + start2, end - start2);
+ tmp2 = sb_strdup_printf("%-*s", prefix_len, "");
if (blogc_is_ordered_list_item(tmp, prefix_len)) {
if (lines2 != NULL) {
- tmp_str = bc_string_new();
- for (bc_slist_t *l = lines2; l != NULL; l = l->next) {
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines2; l != NULL; l = l->next) {
if (l->next == NULL)
- bc_string_append_printf(tmp_str, "%s", l->data);
+ sb_string_append_printf(tmp_str, "%s", l->data);
else
- bc_string_append_printf(tmp_str, "%s%s", l->data,
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
}
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines2, free);
lines2 = NULL;
parsed = blogc_content_parse_inline(tmp_str->str);
- bc_string_free(tmp_str, true);
- lines = bc_slist_append(lines, bc_strdup(parsed));
+ sb_string_free(tmp_str, true);
+ lines = sb_slist_append(lines, sb_strdup(parsed));
free(parsed);
parsed = NULL;
}
- lines2 = bc_slist_append(lines2, bc_strdup(tmp + prefix_len));
+ lines2 = sb_slist_append(lines2, sb_strdup(tmp + prefix_len));
}
- else if (bc_str_starts_with(tmp, tmp2)) {
- lines2 = bc_slist_append(lines2, bc_strdup(tmp + prefix_len));
+ else if (sb_str_starts_with(tmp, tmp2)) {
+ lines2 = sb_slist_append(lines2, sb_strdup(tmp + prefix_len));
}
else {
state = CONTENT_PARAGRAPH_END;
@@ -1213,8 +1243,8 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
tmp2 = NULL;
free(parsed);
parsed = NULL;
- bc_slist_free_full(lines, free);
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines, free);
+ sb_slist_free_full(lines2, free);
lines = NULL;
if (is_last)
continue;
@@ -1233,28 +1263,46 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
if (c == '\n' || c == '\r' || is_last) {
if (lines2 != NULL) {
// FIXME: avoid repeting the code below
- tmp_str = bc_string_new();
- for (bc_slist_t *l = lines2; l != NULL; l = l->next) {
+ tmp_str = sb_string_new();
+ for (sb_slist_t *l = lines2; l != NULL; l = l->next) {
if (l->next == NULL)
- bc_string_append_printf(tmp_str, "%s", l->data);
+ sb_string_append_printf(tmp_str, "%s", l->data);
else
- bc_string_append_printf(tmp_str, "%s%s", l->data,
+ sb_string_append_printf(tmp_str, "%s%s", l->data,
line_ending);
}
- bc_slist_free_full(lines2, free);
+ sb_slist_free_full(lines2, free);
lines2 = NULL;
parsed = blogc_content_parse_inline(tmp_str->str);
- bc_string_free(tmp_str, true);
- lines = bc_slist_append(lines, bc_strdup(parsed));
+ sb_string_free(tmp_str, true);
+ lines = sb_slist_append(lines, sb_strdup(parsed));
free(parsed);
parsed = NULL;
}
- bc_string_append_printf(rv, "<ol>%s", line_ending);
- for (bc_slist_t *l = lines; l != NULL; l = l->next)
- bc_string_append_printf(rv, "<li>%s</li>%s", l->data,
- line_ending);
- bc_string_append_printf(rv, "</ol>%s", line_ending);
- bc_slist_free_full(lines, free);
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_ORDERED_LIST,
+ NULL, NULL);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_ORDERED_LIST,
+ NULL, NULL);
+ last = last->next;
+ }
+ blogc_content_node_t *last_list = NULL;
+ for (sb_slist_t *l = lines; l != NULL; l = l->next) {
+ if (last_list == NULL) {
+ last->child = block_node_new(BLOGC_CONTENT_BLOCK_LIST_ITEM,
+ l->data, NULL);
+ last_list = last->child;
+ }
+ else {
+ last_list->next = block_node_new(BLOGC_CONTENT_BLOCK_LIST_ITEM,
+ l->data, NULL);
+ last_list = last_list->next;
+ }
+ }
+ sb_slist_free(lines);
lines = NULL;
free(prefix);
prefix = NULL;
@@ -1278,16 +1326,19 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
case CONTENT_PARAGRAPH_END:
if (c == '\n' || c == '\r' || is_last) {
- tmp = bc_strndup(src + start, end - start);
- if (description != NULL && *description == NULL)
- *description = blogc_fix_description(tmp);
- parsed = blogc_content_parse_inline(tmp);
- bc_string_append_printf(rv, "<p>%s</p>%s", parsed,
- line_ending);
- free(parsed);
- parsed = NULL;
- free(tmp);
- tmp = NULL;
+ char *tmp2 = sb_strndup(src + start, end - start);
+ sb_trie_t *t = sb_trie_new(free);
+ sb_trie_insert(t, "parsed", blogc_content_parse_inline(tmp2));
+ if (ast == NULL) {
+ ast = block_node_new(BLOGC_CONTENT_BLOCK_PARAGRAPH,
+ tmp2, t);
+ last = ast;
+ }
+ else {
+ last->next = block_node_new(BLOGC_CONTENT_BLOCK_PARAGRAPH,
+ tmp2, t);
+ last = last->next;
+ }
state = CONTENT_START_LINE;
start = current;
}
@@ -1300,9 +1351,159 @@ blogc_content_parse(const char *src, size_t *end_excerpt, char **first_header,
current++;
}
- if (endl == NULL) {
- free(line_ending);
+ if (nl != NULL && *nl == NULL)
+ *nl = sb_strdup(line_ending);
+
+ return ast;
+}
+
+
+void
+blogc_content_free_ast(blogc_content_node_t *ast)
+{
+ if (ast == NULL)
+ return;
+ free(ast->content);
+ sb_trie_free(ast->parameters);
+ blogc_content_free_ast(ast->child);
+ blogc_content_free_ast(ast->next);
+ free(ast);
+}
+
+
+char*
+blogc_content_parse(const char *src, char **excerpt, char **description)
+{
+ char *nl = NULL;
+ blogc_content_node_t *c = blogc_content_parse_ast(src, &nl);
+ char *rv = blogc_content_render_html(c, nl, excerpt, description);
+ free(nl);
+ blogc_content_free_ast(c);
+ return rv;
+}
+
+
+char*
+blogc_content_render_html(blogc_content_node_t *ast, char *nl, char **excerpt,
+ char **description)
+{
+ sb_string_t *rv = sb_string_new();
+ char *tmp = NULL;
+ for (blogc_content_node_t *l = ast; l != NULL; l = l->next) {
+ switch (l->node_type) {
+ case BLOGC_CONTENT_BLOCK:
+ switch (l->type.block_type) {
+ case BLOGC_CONTENT_BLOCK_RAW:
+ sb_string_append_printf(rv, "%s%s", l->content, nl);
+ break;
+ case BLOGC_CONTENT_BLOCK_HEADER:
+ tmp = blogc_slugify(l->content);
+ sb_string_append_printf(rv, "<h%s id=\"%s\">%s</h%s>%s",
+ sb_trie_lookup(l->parameters, "level"), tmp, l->content,
+ sb_trie_lookup(l->parameters, "level"), nl);
+ free(tmp);
+ tmp = NULL;
+ break;
+ case BLOGC_CONTENT_BLOCK_BLOCKQUOTE:
+ tmp = blogc_content_render_html(l->child, nl, NULL, NULL);
+ sb_string_append_printf(rv, "<blockquote>%s</blockquote>%s",
+ tmp, nl);
+ free(tmp);
+ tmp = NULL;
+ break;
+ case BLOGC_CONTENT_BLOCK_CODE:
+ tmp = blogc_htmlentities(l->content);
+ sb_string_append_printf(rv, "<pre><code>%s</code></pre>%s",
+ tmp, nl);
+ free(tmp);
+ tmp = NULL;
+ break;
+ case BLOGC_CONTENT_BLOCK_HORIZONTAL_RULE:
+ sb_string_append_printf(rv, "<hr />%s", nl);
+ break;
+ case BLOGC_CONTENT_BLOCK_UNORDERED_LIST:
+ tmp = blogc_content_render_html(l->child, nl, NULL, NULL);
+ sb_string_append_printf(rv, "<ul>%s%s</ul>%s", nl,
+ tmp, nl);
+ free(tmp);
+ tmp = NULL;
+ break;
+ case BLOGC_CONTENT_BLOCK_ORDERED_LIST:
+ tmp = blogc_content_render_html(l->child, nl, NULL, NULL);
+ sb_string_append_printf(rv, "<ol>%s%s</ol>%s", nl,
+ tmp, nl);
+ free(tmp);
+ tmp = NULL;
+ break;
+ case BLOGC_CONTENT_BLOCK_LIST_ITEM:
+ sb_string_append_printf(rv, "<li>%s</li>%s",
+ l->content, nl);
+ break;
+ case BLOGC_CONTENT_BLOCK_PARAGRAPH:
+ if (description != NULL && *description == NULL)
+ *description = blogc_fix_description(l->content);
+ sb_string_append_printf(rv, "<p>%s</p>%s",
+ sb_trie_lookup(l->parameters, "parsed"), nl);
+ break;
+ case BLOGC_CONTENT_BLOCK_EXCERPT:
+ if (excerpt != NULL && *excerpt == NULL)
+ *excerpt = sb_strdup(rv->str);
+ break;
+ }
+ break;
+ case BLOGC_CONTENT_INLINE:
+ break;
+ }
}
+ return sb_string_free(rv, false);
+}
+
- return bc_string_free(rv, false);
+void
+blogc_content_debug(blogc_content_node_t *ast)
+{
+ for (blogc_content_node_t *l = ast; l != NULL; l = l->next) {
+ switch (l->node_type) {
+ case BLOGC_CONTENT_BLOCK:
+ fprintf(stderr, "DEBUG: <CONTENT BLOCK ");
+ switch (l->type.block_type) {
+ case BLOGC_CONTENT_BLOCK_RAW:
+ fprintf(stderr, "RAW: `%s`", l->content);
+ break;
+ case BLOGC_CONTENT_BLOCK_HEADER:
+ fprintf(stderr, "HEADER: \"%s\"", l->content);
+ break;
+ case BLOGC_CONTENT_BLOCK_BLOCKQUOTE:
+ fprintf(stderr, "BLOCKQUOTE");
+ break;
+ case BLOGC_CONTENT_BLOCK_CODE:
+ fprintf(stderr, "CODE: `%s`", l->content);
+ break;
+ case BLOGC_CONTENT_BLOCK_HORIZONTAL_RULE:
+ fprintf(stderr, "HORIZONTAL_RULE");
+ break;
+ case BLOGC_CONTENT_BLOCK_UNORDERED_LIST:
+ fprintf(stderr, "UNORDERED_LIST");
+ break;
+ case BLOGC_CONTENT_BLOCK_ORDERED_LIST:
+ fprintf(stderr, "ORDERED_LIST");
+ break;
+ case BLOGC_CONTENT_BLOCK_LIST_ITEM:
+ fprintf(stderr, "LIST_ITEM: `%s`", l->content);
+ break;
+ case BLOGC_CONTENT_BLOCK_PARAGRAPH:
+ fprintf(stderr, "PARAGRAPH: `%s`", l->content);
+ break;
+ case BLOGC_CONTENT_BLOCK_EXCERPT:
+ fprintf(stderr, "EXCERPT");
+ break;
+ }
+ fprintf(stderr, ">\n");
+ if (l->child != NULL)
+ blogc_content_debug(l->child);
+ break;
+ case BLOGC_CONTENT_INLINE:
+ break;
+ }
+ }
}
diff --git a/src/content-parser.h b/src/content-parser.h
new file mode 100644
index 0000000..3a72317
--- /dev/null
+++ b/src/content-parser.h
@@ -0,0 +1,174 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _CONTENT_PARSER_H
+#define _CONTENT_PARSER_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "utils.h"
+
+/*
+ * Raw node
+ *
+ * +-----+
+ * | RAW | -> ...
+ * +-----+
+ *
+ */
+
+/*
+ * Header node
+ *
+ * +--------+
+ * | HEADER | -> ...
+ * +--------+
+ * |
+ * +-----+ +------+ +-----+
+ * | raw | -> | bold | -> | raw | -> ...
+ * +-----+ +------+ +-----+
+ *
+ */
+
+/*
+ * Blockquote node
+ *
+ * +------------+
+ * | BLOCKQUOTE | -> ...
+ * +------------+
+ * |
+ * +-----------+ +--------+ +-----------+
+ * | PARAGRAPH | -> | HEADER | -> | PARAGRAPH | -> ...
+ * +-----------+ +--------+ +-----------+
+ * |
+ * +-----+
+ * | raw | -> ...
+ * +-----+
+ *
+ */
+
+/*
+ * Code node
+ *
+ * +------+
+ * | CODE | -> ...
+ * +------+
+ *
+ */
+
+/*
+ * Horizontal rule node
+ *
+ * +-----------------+
+ * | HORIZONTAL_RULE | -> ...
+ * +-----------------+
+ *
+ */
+
+/*
+ * Unordered list node
+ *
+ * +----------------+
+ * | UNORDERED_LIST | -> ...
+ * +----------------+
+ * |
+ * +-----------+ +-----------+
+ * | LIST_ITEM | -> | LIST_ITEM | -> ...
+ * +-----------+ +-----------+
+ * |
+ * +-----+ +------+
+ * | raw | -> | bold | -> ...
+ * +-----+ +------+
+ *
+ */
+
+/*
+ * Ordered list node
+ *
+ * +--------------+
+ * | ORDERED_LIST | -> ...
+ * +--------------+
+ * |
+ * +-----------+ +-----------+
+ * | LIST_ITEM | -> | LIST_ITEM | -> ...
+ * +-----------+ +-----------+
+ * |
+ * +-----+ +------+
+ * | raw | -> | bold | -> ...
+ * +-----+ +------+
+ *
+ */
+
+/*
+ * Paragraph node
+ *
+ * +-----------+
+ * | PARAGRAPH | -> ...
+ * +-----------+
+ * |
+ * +-----+ +------+ +-----+
+ * | raw | -> | bold | -> | raw |
+ * +-----+ +------+ +-----+
+ *
+ */
+
+
+typedef enum {
+ BLOGC_CONTENT_BLOCK = 1,
+ BLOGC_CONTENT_INLINE,
+} blogc_content_node_type_t;
+
+typedef enum {
+ BLOGC_CONTENT_BLOCK_RAW = 1,
+ BLOGC_CONTENT_BLOCK_HEADER,
+ BLOGC_CONTENT_BLOCK_BLOCKQUOTE,
+ BLOGC_CONTENT_BLOCK_CODE,
+ BLOGC_CONTENT_BLOCK_HORIZONTAL_RULE,
+ BLOGC_CONTENT_BLOCK_UNORDERED_LIST,
+ BLOGC_CONTENT_BLOCK_ORDERED_LIST,
+ BLOGC_CONTENT_BLOCK_LIST_ITEM,
+ BLOGC_CONTENT_BLOCK_PARAGRAPH,
+ BLOGC_CONTENT_BLOCK_EXCERPT,
+} blogc_content_block_type_t;
+
+typedef enum {
+ BLOGC_CONTENT_INLINE_RAW = 1,
+ BLOGC_CONTENT_INLINE_LINK,
+ BLOGC_CONTENT_INLINE_IMAGE,
+ BLOGC_CONTENT_INLINE_BOLD,
+ BLOGC_CONTENT_INLINE_ITALIC,
+ BLOGC_CONTENT_INLINE_CODE,
+ BLOGC_CONTENT_INLINE_BREAK_LINE,
+} blogc_content_inline_type_t;
+
+typedef struct _blogc_content_node_t {
+ blogc_content_node_type_t node_type;
+ union {
+ blogc_content_block_type_t block_type;
+ blogc_content_inline_type_t inline_type;
+ } type;
+ char *content;
+ struct _blogc_content_node_t *child;
+ struct _blogc_content_node_t *next;
+ sb_trie_t *parameters;
+} blogc_content_node_t;
+
+char* blogc_slugify(const char *str);
+char* blogc_htmlentities(const char *str);
+char* blogc_fix_description(const char *paragraph);
+char* blogc_content_parse_inline(const char *src);
+bool blogc_is_ordered_list_item(const char *str, size_t prefix_len);
+blogc_content_node_t* blogc_content_parse_ast(const char *src, char **nl);
+void blogc_content_free_ast(blogc_content_node_t *ast);
+char* blogc_content_parse(const char *src, char **excerpt, char **description);
+char* blogc_content_render_html(blogc_content_node_t *ast, char *nl, char **excerpt,
+ char **description);
+void blogc_content_debug(blogc_content_node_t *ast);
+
+#endif /* _CONTENT_PARSER_H */
diff --git a/src/blogc/datetime-parser.c b/src/datetime-parser.c
index ddaf6ef..ad65ff7 100644
--- a/src/blogc/datetime-parser.c
+++ b/src/datetime-parser.c
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -16,9 +16,9 @@
#include <string.h>
+#include "error.h"
#include "datetime-parser.h"
-#include "../common/error.h"
-#include "../common/utils.h"
+#include "utils.h"
typedef enum {
@@ -47,14 +47,14 @@ typedef enum {
char*
blogc_convert_datetime(const char *orig, const char *format,
- bc_error_t **err)
+ blogc_error_t **err)
{
if (err == NULL || *err != NULL)
return NULL;
#ifndef HAVE_TIME_H
- *err = bc_error_new(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new(BLOGC_WARNING_DATETIME_PARSER,
"Your operating system does not supports the datetime functionalities "
"used by blogc. Sorry.");
return NULL;
@@ -69,7 +69,7 @@ blogc_convert_datetime(const char *orig, const char *format,
int tmp = 0;
int diff = '0';
- for (size_t i = 0; orig[i] != '\0'; i++) {
+ for (unsigned int i = 0; orig[i] != '\0'; i++) {
char c = orig[i];
switch (state) {
@@ -80,7 +80,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_YEAR;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of year. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -91,7 +91,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_THIRD_YEAR;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of year. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -102,7 +102,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FOURTH_YEAR;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid third digit of year. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -111,7 +111,7 @@ blogc_convert_datetime(const char *orig, const char *format,
if (c >= '0' && c <= '9') {
tmp += c - diff - 1900;
if (tmp < 0) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid year. Found %d, must be >= 1900.",
tmp + 1900);
break;
@@ -120,7 +120,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_HYPHEN;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid fourth digit of year. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -131,7 +131,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_MONTH;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid separator between year and month. "
"Found '%c', must be '-'.", c);
break;
@@ -142,7 +142,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_MONTH;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of month. "
"Found '%c', must be integer >= 0 and <= 1.", c);
break;
@@ -151,7 +151,7 @@ blogc_convert_datetime(const char *orig, const char *format,
if (c >= '0' && c <= '9') {
tmp += c - diff - 1;
if (tmp < 0 || tmp > 11) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid month. Found %d, must be >= 1 and <= 12.",
tmp + 1);
break;
@@ -160,7 +160,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_HYPHEN;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of month. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -171,7 +171,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_DAY;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid separator between month and day. "
"Found '%c', must be '-'.", c);
break;
@@ -182,7 +182,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_DAY;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of day. "
"Found '%c', must be integer >= 0 and <= 3.", c);
break;
@@ -191,7 +191,7 @@ blogc_convert_datetime(const char *orig, const char *format,
if (c >= '0' && c <= '9') {
tmp += c - diff;
if (tmp < 1 || tmp > 31) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid day. Found %d, must be >= 1 and <= 31.",
tmp);
break;
@@ -200,7 +200,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SPACE;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of day. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -211,7 +211,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_HOUR;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid separator between date and time. "
"Found '%c', must be ' ' (empty space).", c);
break;
@@ -222,7 +222,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_HOUR;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of hours. "
"Found '%c', must be integer >= 0 and <= 2.", c);
break;
@@ -231,7 +231,7 @@ blogc_convert_datetime(const char *orig, const char *format,
if (c >= '0' && c <= '9') {
tmp += c - diff;
if (tmp < 0 || tmp > 23) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid hours. Found %d, must be >= 0 and <= 23.",
tmp);
break;
@@ -240,7 +240,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_COLON;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of hours. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -251,7 +251,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_MINUTE;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid separator between hours and minutes. "
"Found '%c', must be ':'.", c);
break;
@@ -262,7 +262,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_MINUTE;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of minutes. "
"Found '%c', must be integer >= 0 and <= 5.", c);
break;
@@ -274,7 +274,7 @@ blogc_convert_datetime(const char *orig, const char *format,
// this won't happen because we are restricting the digits
// to 00-59 already, but lets keep the code here for
// reference.
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid minutes. Found %d, must be >= 0 and <= 59.",
tmp);
break;
@@ -283,7 +283,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_COLON;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of minutes. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -294,7 +294,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_FIRST_SECOND;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid separator between minutes and seconds. "
"Found '%c', must be ':'.", c);
break;
@@ -305,7 +305,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_SECOND_SECOND;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid first digit of seconds. "
"Found '%c', must be integer >= 0 and <= 6.", c);
break;
@@ -314,7 +314,7 @@ blogc_convert_datetime(const char *orig, const char *format,
if (c >= '0' && c <= '9') {
tmp += c - diff;
if (tmp < 0 || tmp > 60) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid seconds. Found %d, must be >= 0 and <= 60.",
tmp);
break;
@@ -323,7 +323,7 @@ blogc_convert_datetime(const char *orig, const char *format,
state = DATETIME_DONE;
break;
}
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid second digit of seconds. "
"Found '%c', must be integer >= 0 and <= 9.", c);
break;
@@ -355,7 +355,7 @@ blogc_convert_datetime(const char *orig, const char *format,
case DATETIME_SECOND_MINUTE:
case DATETIME_FIRST_SECOND:
case DATETIME_SECOND_SECOND:
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Invalid datetime string. "
"Found '%s', formats allowed are: 'yyyy-mm-dd hh:mm:ss', "
"'yyyy-mm-dd hh:ss', 'yyyy-mm-dd hh' and 'yyyy-mm-dd'.",
@@ -374,13 +374,13 @@ blogc_convert_datetime(const char *orig, const char *format,
char buf[1024];
if (0 == strftime(buf, sizeof(buf), format, &t)) {
- *err = bc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
+ *err = blogc_error_new_printf(BLOGC_WARNING_DATETIME_PARSER,
"Failed to format DATE variable, FORMAT is too long: %s",
format);
return NULL;
}
- return bc_strdup(buf);
+ return sb_strdup(buf);
#endif
}
diff --git a/src/blogc/datetime-parser.h b/src/datetime-parser.h
index 8617ad0..a5087b3 100644
--- a/src/blogc/datetime-parser.h
+++ b/src/datetime-parser.h
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -9,9 +9,9 @@
#ifndef _DATETIME_H
#define _DATETIME_H
-#include "../common/error.h"
+#include "error.h"
char* blogc_convert_datetime(const char *orig, const char *format,
- bc_error_t **err);
+ blogc_error_t **err);
#endif /* _DATETIME_H */
diff --git a/src/debug.c b/src/debug.c
new file mode 100644
index 0000000..9689028
--- /dev/null
+++ b/src/debug.c
@@ -0,0 +1,80 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-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 <stdio.h>
+
+#include "template-parser.h"
+#include "utils.h"
+#include "debug.h"
+
+
+static const char*
+get_operator(blogc_template_stmt_operator_t op)
+{
+ if (op & BLOGC_TEMPLATE_OP_NEQ)
+ return "!=";
+ if (op & BLOGC_TEMPLATE_OP_EQ) {
+ if (op & BLOGC_TEMPLATE_OP_LT)
+ return "<=";
+ else if (op & BLOGC_TEMPLATE_OP_GT)
+ return ">=";
+ return "==";
+ }
+ if (op & BLOGC_TEMPLATE_OP_LT)
+ return "<";
+ else if (op & BLOGC_TEMPLATE_OP_GT)
+ return ">";
+ return "";
+}
+
+
+void
+blogc_debug_template(sb_slist_t *stmts)
+{
+ for (sb_slist_t *tmp = stmts; tmp != NULL; tmp = tmp->next) {
+ blogc_template_stmt_t *data = tmp->data;
+ fprintf(stderr, "DEBUG: <TEMPLATE ");
+ switch (data->type) {
+ case BLOGC_TEMPLATE_IFDEF_STMT:
+ fprintf(stderr, "IFDEF: %s", data->value);
+ break;
+ case BLOGC_TEMPLATE_IFNDEF_STMT:
+ fprintf(stderr, "IFNDEF: %s", data->value);
+ break;
+ case BLOGC_TEMPLATE_IF_STMT:
+ fprintf(stderr, "IF: %s %s %s", data->value,
+ get_operator(data->op), data->value2);
+ break;
+ case BLOGC_TEMPLATE_ELSE_STMT:
+ fprintf(stderr, "ELSE");
+ break;
+ case BLOGC_TEMPLATE_ENDIF_STMT:
+ fprintf(stderr, "ENDIF");
+ break;
+ case BLOGC_TEMPLATE_FOREACH_STMT:
+ fprintf(stderr, "FOREACH: %s", data->value);
+ break;
+ case BLOGC_TEMPLATE_ENDFOREACH_STMT:
+ fprintf(stderr, "ENDFOREACH");
+ break;
+ case BLOGC_TEMPLATE_BLOCK_STMT:
+ fprintf(stderr, "BLOCK: %s", data->value);
+ break;
+ case BLOGC_TEMPLATE_ENDBLOCK_STMT:
+ fprintf(stderr, "ENDBLOCK");
+ break;
+ case BLOGC_TEMPLATE_VARIABLE_STMT:
+ fprintf(stderr, "VARIABLE: %s", data->value);
+ break;
+ case BLOGC_TEMPLATE_CONTENT_STMT:
+ fprintf(stderr, "CONTENT: `%s`", data->value);
+ break;
+ }
+ fprintf(stderr, ">\n");
+ }
+}
diff --git a/src/blogc/debug.h b/src/debug.h
index 6138a91..cd454d4 100644
--- a/src/blogc/debug.h
+++ b/src/debug.h
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -9,8 +9,8 @@
#ifndef ___DEBUG_H
#define ___DEBUG_H
-#include "../common/utils.h"
+#include "utils.h"
-void blogc_debug_template(bc_slist_t *ast);
+void blogc_debug_template(sb_slist_t *stmts);
#endif /* ___DEBUG_H */
diff --git a/src/common/error.c b/src/error.c
index caf47c3..c238f51 100644
--- a/src/common/error.c
+++ b/src/error.c
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -13,40 +13,36 @@
#include "utils.h"
-bc_error_t*
-bc_error_new(bc_error_type_t type, const char *msg)
+blogc_error_t*
+blogc_error_new(blogc_error_type_t type, const char *msg)
{
- bc_error_t *err = bc_malloc(sizeof(bc_error_t));
+ blogc_error_t *err = sb_malloc(sizeof(blogc_error_t));
err->type = type;
- err->msg = bc_strdup(msg);
+ err->msg = sb_strdup(msg);
return err;
}
-bc_error_t*
-bc_error_new_printf(bc_error_type_t type, const char *format, ...)
+blogc_error_t*
+blogc_error_new_printf(blogc_error_type_t type, const char *format, ...)
{
- if (format == NULL)
- return bc_error_new(type, "");
va_list ap;
va_start(ap, format);
- char *tmp = bc_strdup_vprintf(format, ap);
+ char *tmp = sb_strdup_vprintf(format, ap);
va_end(ap);
- bc_error_t *rv = bc_error_new(type, tmp);
+ blogc_error_t *rv = blogc_error_new(type, tmp);
free(tmp);
return rv;
}
-bc_error_t*
-bc_error_parser(bc_error_type_t type, const char *src, size_t src_len,
+blogc_error_t*
+blogc_error_parser(blogc_error_type_t type, const char *src, size_t src_len,
size_t current, const char *format, ...)
{
- if (format == NULL)
- return bc_error_new(type, "");
va_list ap;
va_start(ap, format);
- char *msg = bc_strdup_vprintf(format, ap);
+ char *msg = sb_strdup_vprintf(format, ap);
va_end(ap);
size_t lineno = 1;
@@ -87,14 +83,14 @@ bc_error_parser(bc_error_type_t type, const char *src, size_t src_len,
if (lineend <= linestart && src_len >= linestart)
lineend = src_len;
- char *line = bc_strndup(src + linestart, lineend - linestart);
+ char *line = sb_strndup(src + linestart, lineend - linestart);
- bc_error_t *rv = NULL;
+ blogc_error_t *rv = NULL;
if (line[0] == '\0') // "near" message isn't useful if line is empty
- rv = bc_error_new(type, msg);
+ rv = blogc_error_new(type, msg);
else
- rv = bc_error_new_printf(type,
+ rv = blogc_error_new_printf(type,
"%s\nError occurred near line %d, position %d: %s", msg, lineno,
pos, line);
@@ -105,55 +101,36 @@ bc_error_parser(bc_error_type_t type, const char *src, size_t src_len,
}
-// error handling is centralized here for the sake of simplicity :/
void
-bc_error_print(bc_error_t *err, const char *prefix)
+blogc_error_print(blogc_error_t *err)
{
if (err == NULL)
return;
- if (prefix != NULL)
- fprintf(stderr, "%s: ", prefix);
-
switch(err->type) {
- case BC_ERROR_CONFIG_PARSER:
- fprintf(stderr, "error: config-parser: %s\n", err->msg);
- break;
- case BC_ERROR_FILE:
- fprintf(stderr, "error: file: %s\n", err->msg);
- break;
case BLOGC_ERROR_SOURCE_PARSER:
- fprintf(stderr, "error: source: %s\n", err->msg);
+ fprintf(stderr, "blogc: error: source: %s\n", err->msg);
break;
case BLOGC_ERROR_TEMPLATE_PARSER:
- fprintf(stderr, "error: template: %s\n", err->msg);
+ fprintf(stderr, "blogc: error: template: %s\n", err->msg);
break;
case BLOGC_ERROR_LOADER:
- fprintf(stderr, "error: loader: %s\n", err->msg);
- break;
- case BLOGC_WARNING_DATETIME_PARSER:
- fprintf(stderr, "warning: datetime: %s\n", err->msg);
- break;
- case BLOGC_MAKE_ERROR_SETTINGS:
- fprintf(stderr, "error: settings: %s\n", err->msg);
+ fprintf(stderr, "blogc: error: loader: %s\n", err->msg);
break;
- case BLOGC_MAKE_ERROR_EXEC:
- fprintf(stderr, "error: exec: %s\n", err->msg);
+ case BLOGC_ERROR_FILE:
+ fprintf(stderr, "blogc: error: file: %s\n", err->msg);
break;
- case BLOGC_MAKE_ERROR_ATOM:
- fprintf(stderr, "error: atom: %s\n", err->msg);
- break;
- case BLOGC_MAKE_ERROR_UTILS:
- fprintf(stderr, "error: utils: %s\n", err->msg);
+ case BLOGC_WARNING_DATETIME_PARSER:
+ fprintf(stderr, "blogc: warning: datetime: %s\n", err->msg);
break;
default:
- fprintf(stderr, "error: %s\n", err->msg);
+ fprintf(stderr, "blogc: error: %s\n", err->msg);
}
}
void
-bc_error_free(bc_error_t *err)
+blogc_error_free(blogc_error_t *err)
{
if (err == NULL)
return;
diff --git a/src/error.h b/src/error.h
new file mode 100644
index 0000000..31fbaf2
--- /dev/null
+++ b/src/error.h
@@ -0,0 +1,34 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _ERROR_H
+#define _ERROR_H
+
+#include <stddef.h>
+
+typedef enum {
+ BLOGC_ERROR_SOURCE_PARSER = 1,
+ BLOGC_ERROR_TEMPLATE_PARSER,
+ BLOGC_ERROR_LOADER,
+ BLOGC_ERROR_FILE,
+ BLOGC_WARNING_DATETIME_PARSER,
+} blogc_error_type_t;
+
+typedef struct {
+ char *msg;
+ blogc_error_type_t type;
+} blogc_error_t;
+
+blogc_error_t* blogc_error_new(blogc_error_type_t type, const char *msg);
+blogc_error_t* blogc_error_new_printf(blogc_error_type_t type, const char *format, ...);
+blogc_error_t* blogc_error_parser(blogc_error_type_t type, const char *src,
+ size_t src_len, size_t current, const char *format, ...);
+void blogc_error_print(blogc_error_t *err);
+void blogc_error_free(blogc_error_t *err);
+
+#endif /* _ERROR_H */
diff --git a/src/file.c b/src/file.c
new file mode 100644
index 0000000..dc43056
--- /dev/null
+++ b/src/file.c
@@ -0,0 +1,81 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-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 <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "file.h"
+#include "error.h"
+#include "utf8.h"
+#include "utils.h"
+
+// this would belong to loader.c, but we need it in a separated file to be
+// able to mock it when unit testing the loader functions.
+
+
+char*
+blogc_file_get_contents(const char *path, size_t *len, blogc_error_t **err)
+{
+ if (path == NULL || err == NULL || *err != NULL)
+ return NULL;
+
+ *len = 0;
+ FILE *fp = fopen(path, "r");
+
+ if (fp == NULL) {
+ int tmp_errno = errno;
+ *err = blogc_error_new_printf(BLOGC_ERROR_FILE,
+ "Failed to open file (%s): %s", path, strerror(tmp_errno));
+ return NULL;
+ }
+
+ sb_string_t *str = sb_string_new();
+ char buffer[BLOGC_FILE_CHUNK_SIZE];
+ char *tmp;
+
+ while (!feof(fp)) {
+ size_t read_len = fread(buffer, sizeof(char), BLOGC_FILE_CHUNK_SIZE, fp);
+
+ tmp = buffer;
+
+ if (str->len == 0 && read_len > 0) {
+ // skipping BOM before validation, for performance. should be safe
+ // enough
+ size_t skip = blogc_utf8_skip_bom((uint8_t*) buffer, read_len);
+ read_len -= skip;
+ tmp += skip;
+ }
+
+ *len += read_len;
+ sb_string_append_len(str, tmp, read_len);
+ }
+ fclose(fp);
+
+ if (!blogc_utf8_validate_str(str)) {
+ *err = blogc_error_new_printf(BLOGC_ERROR_FILE,
+ "File content is not valid UTF-8: %s", path);
+ sb_string_free(str, true);
+ return NULL;
+ }
+
+ return sb_string_free(str, false);
+}
+
+
+int
+blogc_fprintf(FILE *stream, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int rv = vfprintf(stream, format, ap);
+ va_end(ap);
+ return rv;
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000..d2c4390
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,21 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _FILE_H
+#define _FILE_H
+
+#include <stddef.h>
+#include <stdio.h>
+#include "error.h"
+
+#define BLOGC_FILE_CHUNK_SIZE 1024
+
+char* blogc_file_get_contents(const char *path, size_t *len, blogc_error_t **err);
+int blogc_fprintf(FILE *stream, const char *format, ...);
+
+#endif /* _FILE_H */
diff --git a/src/loader.c b/src/loader.c
new file mode 100644
index 0000000..bd3251e
--- /dev/null
+++ b/src/loader.c
@@ -0,0 +1,212 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-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 <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "file.h"
+#include "source-parser.h"
+#include "template-parser.h"
+#include "loader.h"
+#include "error.h"
+#include "utils.h"
+
+
+char*
+blogc_get_filename(const char *f)
+{
+ if (f == NULL)
+ return NULL;
+
+ if (strlen(f) == 0)
+ return NULL;
+
+ char *filename = sb_strdup(f);
+
+ // keep a pointer to original string
+ char *tmp = filename;
+
+ bool removed_dot = false;
+ for (int i = strlen(tmp); i >= 0 ; i--) {
+
+ // remove last extension
+ if (!removed_dot && tmp[i] == '.') {
+ tmp[i] = '\0';
+ removed_dot = true;
+ continue;
+ }
+
+ if (tmp[i] == '/' || tmp[i] == '\\') {
+ tmp += i + 1;
+ break;
+ }
+ }
+
+ char *final_filename = sb_strdup(tmp);
+ free(filename);
+
+ return final_filename;
+}
+
+
+sb_slist_t*
+blogc_template_parse_from_file(const char *f, blogc_error_t **err)
+{
+ if (err == NULL || *err != NULL)
+ return NULL;
+ size_t len;
+ char *s = blogc_file_get_contents(f, &len, err);
+ if (s == NULL)
+ return NULL;
+ sb_slist_t *rv = blogc_template_parse(s, len, err);
+ free(s);
+ return rv;
+}
+
+
+sb_trie_t*
+blogc_source_parse_from_file(const char *f, blogc_error_t **err)
+{
+ if (err == NULL || *err != NULL)
+ return NULL;
+ size_t len;
+ char *s = blogc_file_get_contents(f, &len, err);
+ if (s == NULL)
+ return NULL;
+ sb_trie_t *rv = blogc_source_parse(s, len, err);
+
+ // set FILENAME variable
+ if (rv != NULL) {
+ char *filename = blogc_get_filename(f);
+ if (filename != NULL)
+ sb_trie_insert(rv, "FILENAME", filename);
+ }
+
+ free(s);
+ return rv;
+}
+
+
+sb_slist_t*
+blogc_source_parse_from_files(sb_trie_t *conf, sb_slist_t *l, blogc_error_t **err)
+{
+ blogc_error_t *tmp_err = NULL;
+ sb_slist_t *rv = NULL;
+ unsigned int with_date = 0;
+
+ const char *filter_tag = sb_trie_lookup(conf, "FILTER_TAG");
+ const char *filter_page = sb_trie_lookup(conf, "FILTER_PAGE");
+ const char *filter_per_page = sb_trie_lookup(conf, "FILTER_PER_PAGE");
+
+ long page = strtol(filter_page != NULL ? filter_page : "", NULL, 10);
+ if (page <= 0)
+ page = 1;
+ long per_page = strtol(filter_per_page != NULL ? filter_per_page : "10",
+ NULL, 10);
+ if (per_page <= 0)
+ per_page = 10;
+
+ // poor man's pagination
+ unsigned int start = (page - 1) * per_page;
+ unsigned int end = start + per_page;
+ unsigned int counter = 0;
+
+ for (sb_slist_t *tmp = l; tmp != NULL; tmp = tmp->next) {
+ char *f = tmp->data;
+ sb_trie_t *s = blogc_source_parse_from_file(f, &tmp_err);
+ if (s == NULL) {
+ *err = blogc_error_new_printf(BLOGC_ERROR_LOADER,
+ "An error occurred while parsing source file: %s\n\n%s",
+ f, tmp_err->msg);
+ blogc_error_free(tmp_err);
+ tmp_err = NULL;
+ sb_slist_free_full(rv, (sb_free_func_t) sb_trie_free);
+ rv = NULL;
+ break;
+ }
+ if (filter_tag != NULL) {
+ const char *tags_str = sb_trie_lookup(s, "TAGS");
+ // if user wants to filter by tag and no tag is provided, skip it
+ if (tags_str == NULL) {
+ sb_trie_free(s);
+ continue;
+ }
+ char **tags = sb_str_split(tags_str, ' ', 0);
+ bool found = false;
+ for (unsigned int i = 0; tags[i] != NULL; i++) {
+ if (tags[i][0] == '\0')
+ continue;
+ if (0 == strcmp(tags[i], filter_tag))
+ found = true;
+ }
+ sb_strv_free(tags);
+ if (!found) {
+ sb_trie_free(s);
+ continue;
+ }
+ }
+ if (filter_page != NULL) {
+ if (counter < start || counter >= end) {
+ counter++;
+ sb_trie_free(s);
+ continue;
+ }
+ counter++;
+ }
+ if (sb_trie_lookup(s, "DATE") != NULL)
+ with_date++;
+ rv = sb_slist_append(rv, s);
+ }
+
+ if (with_date > 0 && with_date < sb_slist_length(rv))
+ // fatal error, maybe?
+ blogc_fprintf(stderr,
+ "blogc: warning: 'DATE' variable provided for at least one source "
+ "file, but not for all source files. This means that you may get "
+ "wrong values for 'DATE_FIRST' and 'DATE_LAST' variables.\n");
+
+ bool first = true;
+ for (sb_slist_t *tmp = rv; tmp != NULL; tmp = tmp->next) {
+ sb_trie_t *s = tmp->data;
+ if (first) {
+ const char *val = sb_trie_lookup(s, "DATE");
+ if (val != NULL)
+ sb_trie_insert(conf, "DATE_FIRST", sb_strdup(val));
+ val = sb_trie_lookup(s, "FILENAME");
+ if (val != NULL)
+ sb_trie_insert(conf, "FILENAME_FIRST", sb_strdup(val));
+ first = false;
+ }
+ if (tmp->next == NULL) { // last
+ const char *val = sb_trie_lookup(s, "DATE");
+ if (val != NULL)
+ sb_trie_insert(conf, "DATE_LAST", sb_strdup(val));
+ val = sb_trie_lookup(s, "FILENAME");
+ if (val != NULL)
+ sb_trie_insert(conf, "FILENAME_LAST", sb_strdup(val));
+ }
+ }
+
+ if (filter_page != NULL) {
+ unsigned int last_page = ceilf(((float) counter) / per_page);
+ sb_trie_insert(conf, "CURRENT_PAGE", sb_strdup_printf("%ld", page));
+ if (page > 1)
+ sb_trie_insert(conf, "PREVIOUS_PAGE", sb_strdup_printf("%ld", page - 1));
+ if (page < last_page)
+ sb_trie_insert(conf, "NEXT_PAGE", sb_strdup_printf("%ld", page + 1));
+ if (sb_slist_length(rv) > 0)
+ sb_trie_insert(conf, "FIRST_PAGE", sb_strdup("1"));
+ if (last_page > 0)
+ sb_trie_insert(conf, "LAST_PAGE", sb_strdup_printf("%d", last_page));
+ }
+
+ return rv;
+}
diff --git a/src/loader.h b/src/loader.h
new file mode 100644
index 0000000..32b58be
--- /dev/null
+++ b/src/loader.h
@@ -0,0 +1,21 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _LOADER_H
+#define _LOADER_H
+
+#include "error.h"
+#include "utils.h"
+
+char* blogc_get_filename(const char *f);
+sb_slist_t* blogc_template_parse_from_file(const char *f, blogc_error_t **err);
+sb_trie_t* blogc_source_parse_from_file(const char *f, blogc_error_t **err);
+sb_slist_t* blogc_source_parse_from_files(sb_trie_t *conf, sb_slist_t *l,
+ blogc_error_t **err);
+
+#endif /* _LOADER_H */
diff --git a/src/blogc/renderer.c b/src/renderer.c
index a91d105..27e92bd 100644
--- a/src/blogc/renderer.c
+++ b/src/renderer.c
@@ -1,112 +1,77 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-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 <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "datetime-parser.h"
-#include "funcvars.h"
+#include "error.h"
#include "template-parser.h"
#include "renderer.h"
-#include "../common/error.h"
-#include "../common/utils.h"
+#include "utils.h"
const char*
-blogc_get_variable(const char *name, bc_trie_t *global, bc_trie_t *local)
+blogc_get_variable(const char *name, sb_trie_t *global, sb_trie_t *local)
{
const char *rv = NULL;
if (local != NULL) {
- rv = bc_trie_lookup(local, name);
+ rv = sb_trie_lookup(local, name);
if (rv != NULL)
return rv;
}
if (global != NULL)
- rv = bc_trie_lookup(global, name);
+ rv = sb_trie_lookup(global, name);
return rv;
}
char*
-blogc_format_date(const char *date, bc_trie_t *global, bc_trie_t *local)
+blogc_format_date(const char *date, sb_trie_t *global, sb_trie_t *local)
{
const char *date_format = blogc_get_variable("DATE_FORMAT", global, local);
if (date == NULL)
return NULL;
if (date_format == NULL)
- return bc_strdup(date);
+ return sb_strdup(date);
- bc_error_t *err = NULL;
+ blogc_error_t *err = NULL;
char *rv = blogc_convert_datetime(date, date_format, &err);
if (err != NULL) {
- bc_error_print(err, "blogc");
- bc_error_free(err);
- return bc_strdup(date);
+ blogc_error_print(err);
+ blogc_error_free(err);
+ return sb_strdup(date);
}
return rv;
}
-static char*
-foreach_value_variable(const char *name, const char *item)
-{
- if (name == NULL || item == NULL)
- return NULL;
-
- char *rv = bc_strdup_printf("%s__%s", name, item);
- int diff = 'a' - 'A'; // just to avoid magic numbers
- for (size_t i = 0; rv[i] != '\0'; i++) {
- if ((rv[i] >= '0' && rv[i] <= '9') ||
- (rv[i] >= 'A' && rv[i] <= 'Z')) {
- continue;
- }
- if (rv[i] >= 'a' && rv[i] <= 'z') {
- rv[i] -= diff;
- continue;
- }
- rv[i] = '_';
- }
-
- return rv;
-}
-
-
char*
-blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
- const char *foreach_name, bc_slist_t *foreach_var)
+blogc_format_variable(const char *name, sb_trie_t *global, sb_trie_t *local,
+ sb_slist_t *foreach_var)
{
// if used asked for a variable that exists, just return it right away
const char *value = blogc_get_variable(name, global, local);
if (value != NULL)
- return bc_strdup(value);
+ return sb_strdup(value);
- // do the same for special foreach variables
+ // do the same for special variable 'FOREACH_ITEM'
if (0 == strcmp(name, "FOREACH_ITEM")) {
if (foreach_var != NULL && foreach_var->data != NULL) {
- return bc_strdup(foreach_var->data);
- }
- return NULL;
- }
- if (0 == strcmp(name, "FOREACH_VALUE")) {
- if (foreach_name != NULL && foreach_var != NULL && foreach_var->data != NULL) {
- char *value_var = foreach_value_variable(foreach_name, foreach_var->data);
- if (value_var != NULL) {
- value = blogc_get_variable(value_var, global, local);
- free(value_var);
- return bc_strdup(value);
- }
+ return sb_strdup(foreach_var->data);
}
return NULL;
}
- char *var = bc_strdup(name);
+ char *var = sb_strdup(name);
size_t i;
size_t last = strlen(var);
@@ -117,11 +82,11 @@ blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
for (i = last - 1; i > 0 && var[i] >= '0' && var[i] <= '9'; i--);
if (var[i] == '_' && (i + 1) < last) { // var ends with '_[0-9]+'
- char *endptr;
- len = strtol(var + i + 1, &endptr, 10);
- if (*endptr != '\0') {
- fprintf(stderr, "warning: invalid variable size for '%s', "
- "ignoring.\n", var);
+ // passing NULL to endptr because our string was previously validated
+ len = strtol(var + i + 1, NULL, 10);
+ if (errno != 0) {
+ fprintf(stderr, "warning: invalid variable size for '%s' (%s), "
+ "ignoring.\n", var, strerror(errno));
len = -1;
}
else {
@@ -131,27 +96,16 @@ blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
bool must_format = false;
- if (bc_str_ends_with(var, "_FORMATTED")) {
+ if (sb_str_ends_with(var, "_FORMATTED")) {
var[strlen(var) - 10] = '\0';
must_format = true;
}
if ((0 == strcmp(var, "FOREACH_ITEM")) &&
- (foreach_var != NULL && foreach_var->data != NULL)) {
+ (foreach_var != NULL && foreach_var->data != NULL))
value = foreach_var->data;
- }
- else if ((0 == strcmp(var, "FOREACH_VALUE")) &&
- (foreach_name != NULL && foreach_var != NULL && foreach_var->data != NULL)) {
- char *value_var = foreach_value_variable(foreach_name, foreach_var->data);
- if (value_var != NULL) {
- value = blogc_get_variable(value_var, global, local);
- free(value_var);
- }
- }
- else {
- blogc_funcvars_eval(global, var);
+ else
value = blogc_get_variable(var, global, local);
- }
if (value == NULL) {
free(var);
@@ -161,23 +115,23 @@ blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
char *rv = NULL;
if (must_format) {
- if (bc_str_starts_with(name, "DATE_")) {
+ if (sb_str_starts_with(name, "DATE_")) {
rv = blogc_format_date(value, global, local);
}
else {
fprintf(stderr, "warning: no formatter found for '%s', "
"ignoring.\n", var);
- rv = bc_strdup(value);
+ rv = sb_strdup(value);
}
}
else {
- rv = bc_strdup(value);
+ rv = sb_strdup(value);
}
free(var);
if (len > 0) {
- char *tmp = bc_strndup(rv, len);
+ char *tmp = sb_strndup(rv, len);
free(rv);
rv = tmp;
}
@@ -186,19 +140,19 @@ blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
}
-bc_slist_t*
-blogc_split_list_variable(const char *name, bc_trie_t *global, bc_trie_t *local)
+sb_slist_t*
+blogc_split_list_variable(const char *name, sb_trie_t *global, sb_trie_t *local)
{
const char *value = blogc_get_variable(name, global, local);
if (value == NULL)
return NULL;
- bc_slist_t *rv = NULL;
+ sb_slist_t *rv = NULL;
- char **tmp = bc_str_split(value, ' ', 0);
- for (size_t i = 0; tmp[i] != NULL; i++) {
+ char **tmp = sb_str_split(value, ' ', 0);
+ for (unsigned int i = 0; tmp[i] != NULL; i++) {
if (tmp[i][0] != '\0') // ignore empty strings
- rv = bc_slist_append(rv, tmp[i]);
+ rv = sb_slist_append(rv, tmp[i]);
else
free(tmp[i]);
}
@@ -209,27 +163,25 @@ blogc_split_list_variable(const char *name, bc_trie_t *global, bc_trie_t *local)
char*
-blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
- bc_trie_t *config, bool listing)
+blogc_render(sb_slist_t *tmpl, sb_slist_t *sources, sb_trie_t *config, bool listing)
{
if (tmpl == NULL)
return NULL;
- bc_slist_t *current_source = NULL;
- bc_slist_t *listing_start = NULL;
+ sb_slist_t *current_source = NULL;
+ sb_slist_t *listing_start = NULL;
- bc_string_t *str = bc_string_new();
+ sb_string_t *str = sb_string_new();
- bc_trie_t *tmp_source = NULL;
+ sb_trie_t *tmp_source = NULL;
char *config_value = NULL;
char *defined = NULL;
- size_t if_count = 0;
+ unsigned int if_count = 0;
- char *foreach_name = NULL;
- bc_slist_t *foreach_var = NULL;
- bc_slist_t *foreach_var_start = NULL;
- bc_slist_t *foreach_start = NULL;
+ sb_slist_t *foreach_var = NULL;
+ sb_slist_t *foreach_var_start = NULL;
+ sb_slist_t *foreach_start = NULL;
bool if_not = false;
bool inside_block = false;
@@ -238,87 +190,55 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
int cmp = 0;
- bc_slist_t *tmp = tmpl;
- bc_slist_t *current_listing_entry = listing_entries;
+ sb_slist_t *tmp = tmpl;
while (tmp != NULL) {
- blogc_template_node_t *node = tmp->data;
+ blogc_template_stmt_t *stmt = tmp->data;
- switch (node->type) {
+ switch (stmt->type) {
- case BLOGC_TEMPLATE_NODE_CONTENT:
- if (node->data[0] != NULL)
- bc_string_append(str, node->data[0]);
+ case BLOGC_TEMPLATE_CONTENT_STMT:
+ if (stmt->value != NULL)
+ sb_string_append(str, stmt->value);
break;
- case BLOGC_TEMPLATE_NODE_BLOCK:
+ case BLOGC_TEMPLATE_BLOCK_STMT:
inside_block = true;
if_count = 0;
- if (0 == strcmp("entry", node->data[0])) {
+ if (0 == strcmp("entry", stmt->value)) {
if (listing) {
// we can just skip anything and walk until the next
// 'endblock'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
+ while (stmt->type != BLOGC_TEMPLATE_ENDBLOCK_STMT) {
tmp = tmp->next;
- node = tmp->data;
+ stmt = tmp->data;
}
break;
}
current_source = sources;
- tmp_source = current_source != NULL ? current_source->data : NULL;
- }
- if (0 == strcmp("listing_entry", node->data[0])) {
- bc_trie_t *listing_entry = NULL;
- if (current_listing_entry != NULL) {
- listing_entry = current_listing_entry->data;
- current_listing_entry = current_listing_entry->next;
- }
- if (listing_entry == NULL || !listing) {
- // we can just skip anything and walk until the next
- // 'endblock'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
- tmp = tmp->next;
- node = tmp->data;
- }
- break;
- }
- current_source = NULL;
- tmp_source = listing_entry;
+ tmp_source = current_source->data;
}
- else if ((0 == strcmp("listing", node->data[0])) ||
- (0 == strcmp("listing_empty", node->data[0])) ||
- (0 == strcmp("listing_once", node->data[0]))) {
+ else if ((0 == strcmp("listing", stmt->value)) ||
+ (0 == strcmp("listing_once", stmt->value))) {
if (!listing) {
// we can just skip anything and walk until the next
// 'endblock'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
+ while (stmt->type != BLOGC_TEMPLATE_ENDBLOCK_STMT) {
tmp = tmp->next;
- node = tmp->data;
+ stmt = tmp->data;
}
break;
}
}
- if (0 == strcmp("listing_empty", node->data[0])) {
- if (sources != NULL) {
-
- // we can just skip anything and walk until the next
- // 'endblock'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
- tmp = tmp->next;
- node = tmp->data;
- }
- break;
- }
- }
- if (0 == strcmp("listing", node->data[0])) {
+ if (0 == strcmp("listing", stmt->value)) {
if (sources == NULL) {
// we can just skip anything and walk until the next
// 'endblock'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
+ while (stmt->type != BLOGC_TEMPLATE_ENDBLOCK_STMT) {
tmp = tmp->next;
- node = tmp->data;
+ stmt = tmp->data;
}
break;
}
@@ -326,16 +246,16 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
listing_start = tmp;
current_source = sources;
}
- tmp_source = current_source != NULL ? current_source->data : NULL;
+ tmp_source = current_source->data;
}
break;
- case BLOGC_TEMPLATE_NODE_VARIABLE:
- if (node->data[0] != NULL) {
- config_value = blogc_format_variable(node->data[0],
- config, inside_block ? tmp_source : NULL, foreach_name, foreach_var);
+ case BLOGC_TEMPLATE_VARIABLE_STMT:
+ if (stmt->value != NULL) {
+ config_value = blogc_format_variable(stmt->value,
+ config, inside_block ? tmp_source : NULL, foreach_var);
if (config_value != NULL) {
- bc_string_append(str, config_value);
+ sb_string_append(str, config_value);
free(config_value);
config_value = NULL;
break;
@@ -343,7 +263,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
}
break;
- case BLOGC_TEMPLATE_NODE_ENDBLOCK:
+ case BLOGC_TEMPLATE_ENDBLOCK_STMT:
inside_block = false;
if (listing_start != NULL && current_source != NULL) {
current_source = current_source->next;
@@ -356,46 +276,46 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
}
break;
- case BLOGC_TEMPLATE_NODE_IFNDEF:
+ case BLOGC_TEMPLATE_IFNDEF_STMT:
if_not = true;
- case BLOGC_TEMPLATE_NODE_IF:
- case BLOGC_TEMPLATE_NODE_IFDEF:
+ case BLOGC_TEMPLATE_IF_STMT:
+ case BLOGC_TEMPLATE_IFDEF_STMT:
if_count = 0;
defined = NULL;
- if (node->data[0] != NULL)
- defined = blogc_format_variable(node->data[0], config,
- inside_block ? tmp_source : NULL, foreach_name, foreach_var);
+ if (stmt->value != NULL)
+ defined = blogc_format_variable(stmt->value, config,
+ inside_block ? tmp_source : NULL, foreach_var);
evaluate = false;
- if (node->op != 0) {
+ if (stmt->op != 0) {
// Strings that start with a '"' are actually strings, the
// others are meant to be looked up as a second variable
// check.
char *defined2 = NULL;
- if (node->data[1] != NULL) {
- if ((strlen(node->data[1]) >= 2) &&
- (node->data[1][0] == '"') &&
- (node->data[1][strlen(node->data[1]) - 1] == '"'))
+ if (stmt->value2 != NULL) {
+ if ((strlen(stmt->value2) >= 2) &&
+ (stmt->value2[0] == '"') &&
+ (stmt->value2[strlen(stmt->value2) - 1] == '"'))
{
- defined2 = bc_strndup(node->data[1] + 1,
- strlen(node->data[1]) - 2);
+ defined2 = sb_strndup(stmt->value2 + 1,
+ strlen(stmt->value2) - 2);
}
else {
- defined2 = blogc_format_variable(node->data[1],
+ defined2 = blogc_format_variable(stmt->value2,
config, inside_block ? tmp_source : NULL,
- foreach_name, foreach_var);
+ foreach_var);
}
}
if (defined != NULL && defined2 != NULL) {
cmp = strcmp(defined, defined2);
- if (cmp != 0 && node->op & BLOGC_TEMPLATE_OP_NEQ)
+ if (cmp != 0 && stmt->op & BLOGC_TEMPLATE_OP_NEQ)
evaluate = true;
- else if (cmp == 0 && node->op & BLOGC_TEMPLATE_OP_EQ)
+ else if (cmp == 0 && stmt->op & BLOGC_TEMPLATE_OP_EQ)
evaluate = true;
- else if (cmp < 0 && node->op & BLOGC_TEMPLATE_OP_LT)
+ else if (cmp < 0 && stmt->op & BLOGC_TEMPLATE_OP_LT)
evaluate = true;
- else if (cmp > 0 && node->op & BLOGC_TEMPLATE_OP_GT)
+ else if (cmp > 0 && stmt->op & BLOGC_TEMPLATE_OP_GT)
evaluate = true;
}
@@ -414,15 +334,15 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
// skip as well.
while (1) {
tmp = tmp->next;
- node = tmp->data;
- if ((node->type == BLOGC_TEMPLATE_NODE_IF) ||
- (node->type == BLOGC_TEMPLATE_NODE_IFDEF) ||
- (node->type == BLOGC_TEMPLATE_NODE_IFNDEF))
+ stmt = tmp->data;
+ if ((stmt->type == BLOGC_TEMPLATE_IF_STMT) ||
+ (stmt->type == BLOGC_TEMPLATE_IFDEF_STMT) ||
+ (stmt->type == BLOGC_TEMPLATE_IFNDEF_STMT))
{
if_count++;
continue;
}
- if ((node->type == BLOGC_TEMPLATE_NODE_ELSE) &&
+ if ((stmt->type == BLOGC_TEMPLATE_ELSE_STMT) &&
(if_count == 0))
{
// this is somewhat complex. only an else statement
@@ -433,7 +353,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
valid_else = true;
break;
}
- if (node->type == BLOGC_TEMPLATE_NODE_ENDIF) {
+ if (stmt->type == BLOGC_TEMPLATE_ENDIF_STMT) {
if (if_count > 0) {
if_count--;
continue;
@@ -442,15 +362,12 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
}
}
}
- else {
- valid_else = false;
- }
free(defined);
defined = NULL;
if_not = false;
break;
- case BLOGC_TEMPLATE_NODE_ELSE:
+ case BLOGC_TEMPLATE_ELSE_STMT:
if_count = 0;
if (!valid_else) {
@@ -459,17 +376,17 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
// skip as well.
while (1) {
tmp = tmp->next;
- node = tmp->data;
- if ((node->type == BLOGC_TEMPLATE_NODE_IF) ||
- (node->type == BLOGC_TEMPLATE_NODE_IFDEF) ||
- (node->type == BLOGC_TEMPLATE_NODE_IFNDEF))
+ stmt = tmp->data;
+ if ((stmt->type == BLOGC_TEMPLATE_IF_STMT) ||
+ (stmt->type == BLOGC_TEMPLATE_IFDEF_STMT) ||
+ (stmt->type == BLOGC_TEMPLATE_IFNDEF_STMT))
{
if_count++;
continue;
}
// no need to handle else statements here, because every
// if should have an endif.
- if (node->type == BLOGC_TEMPLATE_NODE_ENDIF) {
+ if (stmt->type == BLOGC_TEMPLATE_ENDIF_STMT) {
if (if_count > 0) {
if_count--;
continue;
@@ -481,7 +398,7 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
valid_else = false;
break;
- case BLOGC_TEMPLATE_NODE_ENDIF:
+ case BLOGC_TEMPLATE_ENDIF_STMT:
// any endif statement should invalidate valid_else, to avoid
// propagation to outter conditionals.
valid_else = false;
@@ -489,14 +406,13 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
if_count--;
break;
- case BLOGC_TEMPLATE_NODE_FOREACH:
+ case BLOGC_TEMPLATE_FOREACH_STMT:
if (foreach_var_start == NULL) {
- if (node->data[0] != NULL)
- foreach_var_start = blogc_split_list_variable(node->data[0],
+ if (stmt->value != NULL)
+ foreach_var_start = blogc_split_list_variable(stmt->value,
config, inside_block ? tmp_source : NULL);
if (foreach_var_start != NULL) {
- foreach_name = bc_strdup(node->data[0]);
foreach_var = foreach_var_start;
foreach_start = tmp;
}
@@ -504,9 +420,9 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
// we can just skip anything and walk until the next
// 'endforeach'
- while (node->type != BLOGC_TEMPLATE_NODE_ENDFOREACH) {
+ while (stmt->type != BLOGC_TEMPLATE_ENDFOREACH_STMT) {
tmp = tmp->next;
- node = tmp->data;
+ stmt = tmp->data;
}
break;
}
@@ -514,12 +430,11 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
if (foreach_var == NULL) {
foreach_start = tmp;
- foreach_name = bc_strdup(node->data[0]);
foreach_var = foreach_var_start;
}
break;
- case BLOGC_TEMPLATE_NODE_ENDFOREACH:
+ case BLOGC_TEMPLATE_ENDFOREACH_STMT:
if (foreach_start != NULL && foreach_var != NULL) {
foreach_var = foreach_var->next;
if (foreach_var != NULL) {
@@ -528,10 +443,8 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
}
}
foreach_start = NULL;
- bc_slist_free_full(foreach_var_start, free);
+ sb_slist_free_full(foreach_var_start, free);
foreach_var_start = NULL;
- free(foreach_name);
- foreach_name = NULL;
break;
}
tmp = tmp->next;
@@ -540,5 +453,5 @@ blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_slist_t *listing_entries,
// no need to free temporary variables here. the template parser makes sure
// that templates are sane and statements are closed.
- return bc_string_free(str, false);
+ return sb_string_free(str, false);
}
diff --git a/src/renderer.h b/src/renderer.h
new file mode 100644
index 0000000..2982ee8
--- /dev/null
+++ b/src/renderer.h
@@ -0,0 +1,24 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _RENDERER_H
+#define _RENDERER_H
+
+#include <stdbool.h>
+#include "utils.h"
+
+const char* blogc_get_variable(const char *name, sb_trie_t *global, sb_trie_t *local);
+char* blogc_format_date(const char *date, sb_trie_t *global, sb_trie_t *local);
+char* blogc_format_variable(const char *name, sb_trie_t *global, sb_trie_t *local,
+ sb_slist_t *foreach_var);
+sb_slist_t* blogc_split_list_variable(const char *name, sb_trie_t *global,
+ sb_trie_t *local);
+char* blogc_render(sb_slist_t *tmpl, sb_slist_t *sources, sb_trie_t *config,
+ bool listing);
+
+#endif /* _RENDERER_H */
diff --git a/src/blogc/source-parser.c b/src/source-parser.c
index 13df9e3..349d3f7 100644
--- a/src/blogc/source-parser.c
+++ b/src/source-parser.c
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -11,9 +11,8 @@
#include "content-parser.h"
#include "source-parser.h"
-#include "toctree.h"
-#include "../common/error.h"
-#include "../common/utils.h"
+#include "error.h"
+#include "utils.h"
typedef enum {
@@ -27,21 +26,19 @@ typedef enum {
} blogc_source_parser_state_t;
-bc_trie_t*
-blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
- bc_error_t **err)
+sb_trie_t*
+blogc_source_parse(const char *src, size_t src_len, blogc_error_t **err)
{
if (err == NULL || *err != NULL)
return NULL;
size_t current = 0;
size_t start = 0;
- size_t end_excerpt = 0;
char *key = NULL;
char *tmp = NULL;
char *content = NULL;
- bc_trie_t *rv = bc_trie_new(free);
+ sb_trie_t *rv = sb_trie_new(free);
blogc_source_parser_state_t state = SOURCE_START;
@@ -62,7 +59,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
state = SOURCE_SEPARATOR;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current,
"Can't find a configuration key or the content separator.");
break;
@@ -71,7 +68,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
if (c == ':') {
- key = bc_strndup(src + start, current - start);
+ key = sb_strndup(src + start, current - start);
if (((current - start == 8) &&
(0 == strncmp("FILENAME", src + start, 8))) ||
((current - start == 7) &&
@@ -95,7 +92,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
((current - start == 13) &&
(0 == strncmp("BLOGC_VERSION", src + start, 13))))
{
- *err = bc_error_new_printf(BLOGC_ERROR_SOURCE_PARSER,
+ *err = blogc_error_new_printf(BLOGC_ERROR_SOURCE_PARSER,
"'%s' variable is forbidden in source files. It will "
"be set for you by the compiler.", key);
break;
@@ -103,7 +100,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
state = SOURCE_CONFIG_VALUE_START;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current, "Invalid configuration key.");
break;
@@ -113,16 +110,15 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
start = current;
break;
}
- bc_trie_insert(rv, key, bc_strdup(""));
- free(key);
- key = NULL;
- state = SOURCE_START;
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ current, "Configuration value not provided for '%s'.",
+ key);
break;
case SOURCE_CONFIG_VALUE:
if (c == '\n' || c == '\r') {
- tmp = bc_strndup(src + start, current - start);
- bc_trie_insert(rv, key, bc_strdup(bc_str_strip(tmp)));
+ tmp = sb_strndup(src + start, current - start);
+ sb_trie_insert(rv, key, sb_strdup(sb_str_strip(tmp)));
free(tmp);
free(key);
key = NULL;
@@ -137,7 +133,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
state = SOURCE_CONTENT_START;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current,
"Invalid content separator. Must be more than one '-' characters.");
break;
@@ -151,65 +147,25 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
case SOURCE_CONTENT:
if (current == (src_len - 1)) {
- tmp = bc_strndup(src + start, src_len - start);
- bc_trie_insert(rv, "RAW_CONTENT", tmp);
- char *first_header = NULL;
+ tmp = sb_strndup(src + start, src_len - start);
+ sb_trie_insert(rv, "RAW_CONTENT", tmp);
+ char *excerpt = NULL;
char *description = NULL;
- char *endl = NULL;
- bc_slist_t *headers = NULL;
- bool read_headers = (NULL == bc_trie_lookup(rv, "TOCTREE"));
- content = blogc_content_parse(tmp, &end_excerpt,
- &first_header, &description, &endl, read_headers ? &headers : NULL);
- if (first_header != NULL) {
- // do not override source-provided first_header.
- if (NULL == bc_trie_lookup(rv, "FIRST_HEADER")) {
- // no need to free, because we are transfering memory
- // ownership to the trie.
- bc_trie_insert(rv, "FIRST_HEADER", first_header);
- }
- else {
- free(first_header);
- }
- }
+ content = blogc_content_parse(tmp, &excerpt, &description);
if (description != NULL) {
// do not override source-provided description.
- if (NULL == bc_trie_lookup(rv, "DESCRIPTION")) {
+ if (NULL == sb_trie_lookup(rv, "DESCRIPTION")) {
// no need to free, because we are transfering memory
// ownership to the trie.
- bc_trie_insert(rv, "DESCRIPTION", description);
+ sb_trie_insert(rv, "DESCRIPTION", description);
}
else {
free(description);
}
}
- if (headers != NULL) {
- // we already validated that the user do not defined TOCTREE
- // manually in source file.
- const char *maxdepth = bc_trie_lookup(rv, "TOCTREE_MAXDEPTH");
- if (maxdepth != NULL) {
- char *endptr;
- toctree_maxdepth = strtol(maxdepth, &endptr, 10);
- if (*maxdepth != '\0' && *endptr != '\0') {
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
- current,
- "Invalid value for 'TOCTREE_MAXDEPTH' variable: %s.",
- maxdepth);
- blogc_toctree_free(headers);
- free(endl);
- free(content);
- break;
- }
- }
- char *toctree = blogc_toctree_render(headers, toctree_maxdepth, endl);
- blogc_toctree_free(headers);
- if (toctree != NULL) {
- bc_trie_insert(rv, "TOCTREE", toctree);
- }
- }
- free(endl);
- bc_trie_insert(rv, "CONTENT", content);
- bc_trie_insert(rv, "EXCERPT", end_excerpt == 0 ?
- bc_strdup(content) : bc_strndup(content, end_excerpt));
+ sb_trie_insert(rv, "EXCERPT", excerpt == NULL ?
+ sb_strdup(content) : excerpt);
+ sb_trie_insert(rv, "CONTENT", content);
}
break;
}
@@ -220,28 +176,28 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
current++;
}
- if (*err == NULL && bc_trie_size(rv) == 0) {
+ if (*err == NULL && sb_trie_size(rv) == 0) {
// ok, nothing found in the config trie, but no error set either.
// let's try to be nice with the users and provide some reasonable
// output. :)
switch (state) {
case SOURCE_START:
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current, "Your source file is empty.");
break;
case SOURCE_CONFIG_KEY:
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current, "Your last configuration key is missing ':' and "
"the value");
break;
case SOURCE_CONFIG_VALUE_START:
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current, "Configuration value not provided for '%s'.",
key);
break;
case SOURCE_CONFIG_VALUE:
- *err = bc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_SOURCE_PARSER, src, src_len,
current, "No line ending after the configuration value for "
"'%s'.", key);
break;
@@ -254,7 +210,7 @@ blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
if (*err != NULL) {
free(key);
- bc_trie_free(rv);
+ sb_trie_free(rv);
return NULL;
}
diff --git a/src/blogc/source-parser.h b/src/source-parser.h
index 2acd753..0d54742 100644
--- a/src/blogc/source-parser.h
+++ b/src/source-parser.h
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -10,10 +10,10 @@
#define _SOURCE_PARSER_H
#include <stddef.h>
-#include "../common/error.h"
-#include "../common/utils.h"
+#include "error.h"
+#include "utils.h"
-bc_trie_t* blogc_source_parse(const char *src, size_t src_len, int toctree_maxdepth,
- bc_error_t **err);
+sb_trie_t* blogc_source_parse(const char *src, size_t src_len,
+ blogc_error_t **err);
#endif /* _SOURCE_PARSER_H */
diff --git a/src/blogc/template-parser.c b/src/template-parser.c
index 8e7ca84..6ca6eb7 100644
--- a/src/blogc/template-parser.c
+++ b/src/template-parser.c
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -11,8 +11,8 @@
#include <string.h>
#include "template-parser.h"
-#include "../common/error.h"
-#include "../common/utils.h"
+#include "error.h"
+#include "utils.h"
typedef enum {
@@ -41,8 +41,8 @@ typedef enum {
} blogc_template_parser_state_t;
-bc_slist_t*
-blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
+sb_slist_t*
+blogc_template_parse(const char *src, size_t src_len, blogc_error_t **err)
{
if (err == NULL || *err != NULL)
return NULL;
@@ -55,16 +55,16 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
size_t start2 = 0;
size_t end2 = 0;
- blogc_template_operator_t tmp_op = 0;
+ blogc_template_stmt_operator_t tmp_op = 0;
- size_t if_count = 0;
- size_t block_if_count = 0;
+ unsigned int if_count = 0;
+ unsigned int block_if_count = 0;
bool else_open = false;
bool foreach_open = false;
bool block_foreach_open = false;
- bc_slist_t *ast = NULL;
- blogc_template_node_t *node = NULL;
+ sb_slist_t *stmts = NULL;
+ blogc_template_stmt_t *stmt = NULL;
/*
* this is a reference to the content of previous node in the singly-linked
@@ -75,14 +75,14 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
* - template parser never walk backwards, then the list itself does not
* need to know its previous node.
*/
- blogc_template_node_t *previous = NULL;
+ blogc_template_stmt_t *previous = NULL;
bool lstrip_next = false;
char *tmp = NULL;
char *block_type = NULL;
blogc_template_parser_state_t state = TEMPLATE_START;
- blogc_template_node_type_t type = BLOGC_TEMPLATE_NODE_CONTENT;
+ blogc_template_stmt_type_t type = BLOGC_TEMPLATE_CONTENT_STMT;
bool block_open = false;
@@ -94,23 +94,23 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_START:
if (last) {
- node = bc_malloc(sizeof(blogc_template_node_t));
- node->type = type;
+ stmt = sb_malloc(sizeof(blogc_template_stmt_t));
+ stmt->type = type;
if (lstrip_next) {
- tmp = bc_strndup(src + start, src_len - start);
- node->data[0] = bc_strdup(bc_str_lstrip(tmp));
+ tmp = sb_strndup(src + start, src_len - start);
+ stmt->value = sb_strdup(sb_str_lstrip(tmp));
free(tmp);
tmp = NULL;
lstrip_next = false;
}
else {
- node->data[0] = bc_strndup(src + start, src_len - start);
+ stmt->value = sb_strndup(src + start, src_len - start);
}
- node->op = 0;
- node->data[1] = NULL;
- ast = bc_slist_append(ast, node);
- previous = node;
- node = NULL;
+ stmt->op = 0;
+ stmt->value2 = NULL;
+ stmts = sb_slist_append(stmts, stmt);
+ previous = stmt;
+ stmt = NULL;
}
if (c == '{') {
end = current;
@@ -125,23 +125,23 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
else
state = TEMPLATE_VARIABLE_START;
if (end > start) {
- node = bc_malloc(sizeof(blogc_template_node_t));
- node->type = type;
+ stmt = sb_malloc(sizeof(blogc_template_stmt_t));
+ stmt->type = type;
if (lstrip_next) {
- tmp = bc_strndup(src + start, end - start);
- node->data[0] = bc_strdup(bc_str_lstrip(tmp));
+ tmp = sb_strndup(src + start, end - start);
+ stmt->value = sb_strdup(sb_str_lstrip(tmp));
free(tmp);
tmp = NULL;
lstrip_next = false;
}
else {
- node->data[0] = bc_strndup(src + start, end - start);
+ stmt->value = sb_strndup(src + start, end - start);
}
- node->op = 0;
- node->data[1] = NULL;
- ast = bc_slist_append(ast, node);
- previous = node;
- node = NULL;
+ stmt->op = 0;
+ stmt->value2 = NULL;
+ stmts = sb_slist_append(stmts, stmt);
+ previous = stmt;
+ stmt = NULL;
}
break;
}
@@ -151,9 +151,9 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_START_WHITESPACE_CLEANER:
if (c == '-') {
if ((previous != NULL) &&
- (previous->type == BLOGC_TEMPLATE_NODE_CONTENT))
+ (previous->type == BLOGC_TEMPLATE_CONTENT_STMT))
{
- previous->data[0] = bc_str_rstrip(previous->data[0]); // does not need copy
+ previous->value = sb_str_rstrip(previous->value); // does not need copy
}
state = TEMPLATE_BLOCK_START;
break;
@@ -161,7 +161,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
state = TEMPLATE_BLOCK_START;
case TEMPLATE_BLOCK_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'a' && c <= 'z') {
state = TEMPLATE_BLOCK_TYPE;
@@ -169,13 +169,13 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
}
if (c == '-') {
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Duplicated whitespace "
"cleaner before statement.");
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Must begin with lowercase letter.");
break;
@@ -183,19 +183,19 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_TYPE:
if (c >= 'a' && c <= 'z')
break;
- if (bc_isspace(c)) {
+ if (c == ' ') {
if ((current - start == 5) &&
(0 == strncmp("block", src + start, 5)))
{
if (!block_open) {
state = TEMPLATE_BLOCK_BLOCK_TYPE_START;
- type = BLOGC_TEMPLATE_NODE_BLOCK;
+ type = BLOGC_TEMPLATE_BLOCK_STMT;
start = current;
block_if_count = if_count;
block_foreach_open = foreach_open;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current, "Blocks can't be nested.");
break;
}
@@ -204,24 +204,24 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
{
if (block_open) {
if (if_count != block_if_count) {
- *err = bc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
"%d open 'if', 'ifdef' and/or 'ifndef' statements "
"were not closed inside a '%s' block!",
if_count - block_if_count, block_type);
break;
}
if (!block_foreach_open && foreach_open) {
- *err = bc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
"An open 'foreach' statement was not closed "
"inside a '%s' block!", block_type);
break;
}
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- type = BLOGC_TEMPLATE_NODE_ENDBLOCK;
+ type = BLOGC_TEMPLATE_ENDBLOCK_STMT;
block_open = false;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current,
"'endblock' statement without an open 'block' statement.");
break;
@@ -230,7 +230,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
(0 == strncmp("ifdef", src + start, 5)))
{
state = TEMPLATE_BLOCK_IF_START;
- type = BLOGC_TEMPLATE_NODE_IFDEF;
+ type = BLOGC_TEMPLATE_IFDEF_STMT;
start = current;
if_count++;
else_open = false;
@@ -240,7 +240,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
(0 == strncmp("ifndef", src + start, 6)))
{
state = TEMPLATE_BLOCK_IF_START;
- type = BLOGC_TEMPLATE_NODE_IFNDEF;
+ type = BLOGC_TEMPLATE_IFNDEF_STMT;
start = current;
if_count++;
else_open = false;
@@ -250,7 +250,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
(0 == strncmp("if", src + start, 2)))
{
state = TEMPLATE_BLOCK_IF_START;
- type = BLOGC_TEMPLATE_NODE_IF;
+ type = BLOGC_TEMPLATE_IF_STMT;
start = current;
if_count++;
else_open = false;
@@ -264,17 +264,17 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
{
if (!else_open) {
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- type = BLOGC_TEMPLATE_NODE_ELSE;
+ type = BLOGC_TEMPLATE_ELSE_STMT;
else_open = true;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current,
"More than one 'else' statement for an open 'if', "
"'ifdef' or 'ifndef' statement.");
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current,
"'else' statement without an open 'if', 'ifdef' or "
"'ifndef' statement.");
@@ -287,12 +287,12 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
(!block_open && if_count > 0))
{
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- type = BLOGC_TEMPLATE_NODE_ENDIF;
+ type = BLOGC_TEMPLATE_ENDIF_STMT;
if_count--;
else_open = false;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current,
"'endif' statement without an open 'if', 'ifdef' or "
"'ifndef' statement.");
@@ -303,12 +303,12 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
{
if (!foreach_open) {
state = TEMPLATE_BLOCK_FOREACH_START;
- type = BLOGC_TEMPLATE_NODE_FOREACH;
+ type = BLOGC_TEMPLATE_FOREACH_STMT;
start = current;
foreach_open = true;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current, "'foreach' statements can't "
"be nested.");
break;
@@ -320,18 +320,18 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
(!block_open && foreach_open))
{
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- type = BLOGC_TEMPLATE_NODE_ENDFOREACH;
+ type = BLOGC_TEMPLATE_ENDFOREACH_STMT;
foreach_open = false;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, current,
"'endforeach' statement without an open 'foreach' "
"statement.");
break;
}
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement type: Allowed types are: 'block', "
"'endblock', 'if', 'ifdef', 'ifndef', 'else', 'endif', "
@@ -339,14 +339,14 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_BLOCK_TYPE_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'a' && c <= 'z') {
state = TEMPLATE_BLOCK_BLOCK_TYPE;
start = current;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid block syntax. Must begin with lowercase letter.");
break;
@@ -354,7 +354,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_BLOCK_TYPE:
if ((c >= 'a' && c <= 'z') || c == '_')
break;
- if (bc_isspace(c)) {
+ if (c == ' ') {
if ((current - start == 5) &&
(0 == strncmp("entry", src + start, 5)))
{
@@ -379,38 +379,22 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
break;
}
- else if ((current - start == 13) &&
- (0 == strncmp("listing_empty", src + start, 13)))
- {
- block_open = true;
- end = current;
- state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- break;
- }
- else if ((current - start == 13) &&
- (0 == strncmp("listing_entry", src + start, 13)))
- {
- block_open = true;
- end = current;
- state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
- break;
- }
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
- "Invalid block type. Allowed types are: 'entry', 'listing', "
- "'listing_once', 'listing_empty' and 'listing_entry'.");
+ "Invalid block type. Allowed types are: 'entry', 'listing' "
+ "and 'listing_once'.");
break;
case TEMPLATE_BLOCK_IF_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_IF_VARIABLE;
start = current;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid variable name. Must begin with uppercase letter.");
break;
@@ -418,36 +402,37 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_IF_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (bc_isspace(c)) {
+ if (c == ' ') {
end = current;
- if (type == BLOGC_TEMPLATE_NODE_IF)
+ if (type == BLOGC_TEMPLATE_IF_STMT)
state = TEMPLATE_BLOCK_IF_OPERATOR_START;
else
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid variable name. Must be uppercase letter, number "
"or '_'.");
break;
case TEMPLATE_BLOCK_IF_OPERATOR_START:
- if (bc_isspace(c))
+ if (c == ' ') {
break;
+ }
state = TEMPLATE_BLOCK_IF_OPERATOR;
op_start = current;
break;
case TEMPLATE_BLOCK_IF_OPERATOR:
- if (!bc_isspace(c))
+ if (c != ' ')
break;
state = TEMPLATE_BLOCK_IF_OPERAND_START;
op_end = current;
break;
case TEMPLATE_BLOCK_IF_OPERAND_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_IF_VARIABLE_OPERAND;
@@ -461,7 +446,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
}
op_start = 0;
op_end = 0;
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid 'if' operand. Must be double-quoted static "
"string or variable.");
@@ -484,14 +469,14 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
case TEMPLATE_BLOCK_FOREACH_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_BLOCK_FOREACH_VARIABLE;
start = current;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid foreach variable name. Must begin with uppercase "
"letter.");
@@ -500,19 +485,19 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_BLOCK_FOREACH_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (bc_isspace(c)) {
+ if (c == ' ') {
end = current;
state = TEMPLATE_BLOCK_END_WHITESPACE_CLEANER;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid foreach variable name. Must be uppercase letter, "
"number or '_'.");
break;
case TEMPLATE_BLOCK_END_WHITESPACE_CLEANER:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c == '-') {
lstrip_next = true;
@@ -527,27 +512,27 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
break;
}
if (c == '-') {
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Duplicated whitespace "
"cleaner after statement.");
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Must end with '%%}'.");
break;
case TEMPLATE_VARIABLE_START:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c >= 'A' && c <= 'Z') {
state = TEMPLATE_VARIABLE;
- type = BLOGC_TEMPLATE_NODE_VARIABLE;
+ type = BLOGC_TEMPLATE_VARIABLE_STMT;
start = current;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid variable name. Must begin with uppercase letter.");
break;
@@ -555,7 +540,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
case TEMPLATE_VARIABLE:
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
break;
- if (bc_isspace(c)) {
+ if (c == ' ') {
end = current;
state = TEMPLATE_VARIABLE_END;
break;
@@ -565,20 +550,20 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
state = TEMPLATE_CLOSE_BRACKET;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid variable name. Must be uppercase letter, number "
"or '_'.");
break;
case TEMPLATE_VARIABLE_END:
- if (bc_isspace(c))
+ if (c == ' ')
break;
if (c == '}') {
state = TEMPLATE_CLOSE_BRACKET;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Must end with '}}'.");
break;
@@ -604,7 +589,7 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
tmp_op = BLOGC_TEMPLATE_OP_NEQ;
}
if (tmp_op == 0) {
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER,
src, src_len, op_start,
"Invalid 'if' operator. Must be '<', '>', "
"'<=', '>=', '==' or '!='.");
@@ -615,29 +600,29 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
op_start = 0;
op_end = 0;
}
- node = bc_malloc(sizeof(blogc_template_node_t));
- node->type = type;
- node->op = tmp_op;
- node->data[0] = NULL;
- node->data[1] = NULL;
+ stmt = sb_malloc(sizeof(blogc_template_stmt_t));
+ stmt->type = type;
+ stmt->value = NULL;
+ stmt->op = tmp_op;
+ stmt->value2 = NULL;
if (end > start)
- node->data[0] = bc_strndup(src + start, end - start);
+ stmt->value = sb_strndup(src + start, end - start);
if (end2 > start2) {
- node->data[1] = bc_strndup(src + start2, end2 - start2);
+ stmt->value2 = sb_strndup(src + start2, end2 - start2);
start2 = 0;
end2 = 0;
}
- if (type == BLOGC_TEMPLATE_NODE_BLOCK)
- block_type = node->data[0];
- ast = bc_slist_append(ast, node);
- previous = node;
- node = NULL;
+ if (type == BLOGC_TEMPLATE_BLOCK_STMT)
+ block_type = stmt->value;
+ stmts = sb_slist_append(stmts, stmt);
+ previous = stmt;
+ stmt = NULL;
state = TEMPLATE_START;
- type = BLOGC_TEMPLATE_NODE_CONTENT;
+ type = BLOGC_TEMPLATE_CONTENT_STMT;
start = current + 1;
break;
}
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src,
src_len, current,
"Invalid statement syntax. Must end with '}'.");
break;
@@ -652,43 +637,43 @@ blogc_template_parse(const char *src, size_t src_len, bc_error_t **err)
if (*err == NULL) {
if (state == TEMPLATE_BLOCK_IF_STRING_OPERAND)
- *err = bc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len,
+ *err = blogc_error_parser(BLOGC_ERROR_TEMPLATE_PARSER, src, src_len,
start2, "Found an open double-quoted string.");
else if (if_count != 0)
- *err = bc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_new_printf(BLOGC_ERROR_TEMPLATE_PARSER,
"%d open 'if', 'ifdef' and/or 'ifndef' statements were not closed!",
if_count);
else if (block_open)
- *err = bc_error_new(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER,
"An open block was not closed!");
else if (foreach_open)
- *err = bc_error_new(BLOGC_ERROR_TEMPLATE_PARSER,
+ *err = blogc_error_new(BLOGC_ERROR_TEMPLATE_PARSER,
"An open 'foreach' statement was not closed!");
}
if (*err != NULL) {
- if (node != NULL) {
- free(node->data[0]);
- free(node);
+ if (stmt != NULL) {
+ free(stmt->value);
+ free(stmt);
}
- blogc_template_free_ast(ast);
+ blogc_template_free_stmts(stmts);
return NULL;
}
- return ast;
+ return stmts;
}
void
-blogc_template_free_ast(bc_slist_t *ast)
+blogc_template_free_stmts(sb_slist_t *stmts)
{
- for (bc_slist_t *tmp = ast; tmp != NULL; tmp = tmp->next) {
- blogc_template_node_t *data = tmp->data;
+ for (sb_slist_t *tmp = stmts; tmp != NULL; tmp = tmp->next) {
+ blogc_template_stmt_t *data = tmp->data;
if (data == NULL)
continue;
- free(data->data[0]);
- free(data->data[1]);
+ free(data->value);
+ free(data->value2);
free(data);
}
- bc_slist_free(ast);
+ sb_slist_free(stmts);
}
diff --git a/src/template-parser.h b/src/template-parser.h
new file mode 100644
index 0000000..19df6af
--- /dev/null
+++ b/src/template-parser.h
@@ -0,0 +1,53 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _TEMPLATE_PARSER_H
+#define _TEMPLATE_PARSER_H
+
+#include <stddef.h>
+#include "error.h"
+#include "utils.h"
+
+/*
+ * note: whitespace cleaners are NOT added to ast. we fix strings right during
+ * template parsing. renderer does not need to care about it, for the sake of
+ * simplicity.
+ */
+typedef enum {
+ BLOGC_TEMPLATE_IFDEF_STMT = 1,
+ BLOGC_TEMPLATE_IFNDEF_STMT,
+ BLOGC_TEMPLATE_IF_STMT,
+ BLOGC_TEMPLATE_ELSE_STMT,
+ BLOGC_TEMPLATE_ENDIF_STMT,
+ BLOGC_TEMPLATE_FOREACH_STMT,
+ BLOGC_TEMPLATE_ENDFOREACH_STMT,
+ BLOGC_TEMPLATE_BLOCK_STMT,
+ BLOGC_TEMPLATE_ENDBLOCK_STMT,
+ BLOGC_TEMPLATE_VARIABLE_STMT,
+ BLOGC_TEMPLATE_CONTENT_STMT,
+} blogc_template_stmt_type_t;
+
+typedef enum {
+ BLOGC_TEMPLATE_OP_NEQ = 1 << 0,
+ BLOGC_TEMPLATE_OP_EQ = 1 << 1,
+ BLOGC_TEMPLATE_OP_LT = 1 << 2,
+ BLOGC_TEMPLATE_OP_GT = 1 << 3,
+} blogc_template_stmt_operator_t;
+
+typedef struct {
+ blogc_template_stmt_type_t type;
+ char *value;
+ char *value2;
+ blogc_template_stmt_operator_t op;
+} blogc_template_stmt_t;
+
+sb_slist_t* blogc_template_parse(const char *src, size_t src_len,
+ blogc_error_t **err);
+void blogc_template_free_stmts(sb_slist_t *stmts);
+
+#endif /* _TEMPLATE_PARSER_H */
diff --git a/src/common/utf8.c b/src/utf8.c
index df5e2d2..a2f4fdd 100644
--- a/src/common/utf8.c
+++ b/src/utf8.c
@@ -1,7 +1,7 @@
/*
* blogc: A blog compiler.
* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
- * Copyright (c) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (c) 2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -56,27 +56,41 @@ static const uint8_t utf8d[] = {
};
+static uint32_t inline
+decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+
bool
-bc_utf8_validate(const uint8_t *str, size_t len)
+blogc_utf8_validate(const uint8_t *str, size_t len)
{
+ uint32_t codepoint;
uint32_t state = 0;
for (size_t i = 0; i < len; i++)
- state = utf8d[256 + state + utf8d[str[i]]];
+ decode(&state, &codepoint, str[i]);
return state == UTF8_ACCEPT;
}
bool
-bc_utf8_validate_str(bc_string_t *str)
+blogc_utf8_validate_str(sb_string_t *str)
{
- return bc_utf8_validate((uint8_t*) str->str, str->len);
+ return blogc_utf8_validate((uint8_t*) str->str, str->len);
}
size_t
-bc_utf8_skip_bom(const uint8_t *str, size_t len)
+blogc_utf8_skip_bom(const uint8_t *str, size_t len)
{
if (len < 3)
return 0;
diff --git a/src/common/utf8.h b/src/utf8.h
index bf0f1ae..06fe07e 100644
--- a/src/common/utf8.h
+++ b/src/utf8.h
@@ -1,6 +1,6 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2019 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
@@ -14,8 +14,8 @@
#include <stdint.h>
#include "utils.h"
-bool bc_utf8_validate(const uint8_t *str, size_t len);
-bool bc_utf8_validate_str(bc_string_t *str);
-size_t bc_utf8_skip_bom(const uint8_t *str, size_t len);
+bool blogc_utf8_validate(const uint8_t *str, size_t len);
+bool blogc_utf8_validate_str(sb_string_t *str);
+size_t blogc_utf8_skip_bom(const uint8_t *str, size_t len);
#endif /* _UTF_8_H */
diff --git a/src/common/utils.c b/src/utils.c
index ddd3911..b42ae4e 100644
--- a/src/common/utils.c
+++ b/src/utils.c
@@ -1,15 +1,14 @@
/*
* blogc: A blog compiler.
- * Copyright (C) 2014-2020 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
*
* This program can be distributed under the terms of the BSD License.
* See the file LICENSE.
*/
-#define BC_STRING_CHUNK_SIZE 128
+#define SB_STRING_CHUNK_SIZE 128
#include <string.h>
-#include <strings.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -19,7 +18,7 @@
void*
-bc_malloc(size_t size)
+sb_malloc(size_t size)
{
// simple things simple!
void *rv = malloc(size);
@@ -32,7 +31,7 @@ bc_malloc(size_t size)
void*
-bc_realloc(void *ptr, size_t size)
+sb_realloc(void *ptr, size_t size)
{
// simple things even simpler :P
void *rv = realloc(ptr, size);
@@ -45,17 +44,17 @@ bc_realloc(void *ptr, size_t size)
}
-bc_slist_t*
-bc_slist_append(bc_slist_t *l, void *data)
+sb_slist_t*
+sb_slist_append(sb_slist_t *l, void *data)
{
- bc_slist_t *node = bc_malloc(sizeof(bc_slist_t));
+ sb_slist_t *node = sb_malloc(sizeof(sb_slist_t));
node->data = data;
node->next = NULL;
if (l == NULL) {
l = node;
}
else {
- bc_slist_t *tmp;
+ sb_slist_t *tmp;
for (tmp = l; tmp->next != NULL; tmp = tmp->next);
tmp->next = node;
}
@@ -63,10 +62,10 @@ bc_slist_append(bc_slist_t *l, void *data)
}
-bc_slist_t*
-bc_slist_prepend(bc_slist_t *l, void *data)
+sb_slist_t*
+sb_slist_prepend(sb_slist_t *l, void *data)
{
- bc_slist_t *node = bc_malloc(sizeof(bc_slist_t));
+ sb_slist_t *node = sb_malloc(sizeof(sb_slist_t));
node->data = data;
node->next = l;
l = node;
@@ -74,27 +73,11 @@ bc_slist_prepend(bc_slist_t *l, void *data)
}
-bc_slist_t*
-bc_slist_append_list(bc_slist_t *l, bc_slist_t *n)
-{
- if (l == NULL) {
- return n;
- }
- if (n == NULL) {
- return l;
- }
- bc_slist_t *tmp;
- for (tmp = l; tmp->next != NULL; tmp = tmp->next);
- tmp->next = n;
- return l;
-}
-
-
void
-bc_slist_free_full(bc_slist_t *l, bc_free_func_t free_func)
+sb_slist_free_full(sb_slist_t *l, sb_free_func_t free_func)
{
while (l != NULL) {
- bc_slist_t *tmp = l->next;
+ sb_slist_t *tmp = l->next;
if ((free_func != NULL) && (l->data != NULL))
free_func(l->data);
free(l);
@@ -104,26 +87,26 @@ bc_slist_free_full(bc_slist_t *l, bc_free_func_t free_func)
void
-bc_slist_free(bc_slist_t *l)
+sb_slist_free(sb_slist_t *l)
{
- bc_slist_free_full(l, NULL);
+ sb_slist_free_full(l, NULL);
}
size_t
-bc_slist_length(bc_slist_t *l)
+sb_slist_length(sb_slist_t *l)
{
if (l == NULL)
return 0;
size_t i;
- bc_slist_t *tmp;
+ sb_slist_t *tmp;
for (tmp = l, i = 0; tmp != NULL; tmp = tmp->next, i++);
return i;
}
char*
-bc_strdup(const char *s)
+sb_strdup(const char *s)
{
if (s == NULL)
return NULL;
@@ -137,7 +120,7 @@ bc_strdup(const char *s)
char*
-bc_strndup(const char *s, size_t n)
+sb_strndup(const char *s, size_t n)
{
if (s == NULL)
return NULL;
@@ -152,10 +135,8 @@ bc_strndup(const char *s, size_t n)
char*
-bc_strdup_vprintf(const char *format, va_list ap)
+sb_strdup_vprintf(const char *format, va_list ap)
{
- if (format == NULL)
- return NULL;
va_list ap2;
va_copy(ap2, ap);
int l = vsnprintf(NULL, 0, format, ap2);
@@ -175,28 +156,18 @@ bc_strdup_vprintf(const char *format, va_list ap)
char*
-bc_strdup_printf(const char *format, ...)
+sb_strdup_printf(const char *format, ...)
{
- if (format == NULL)
- return NULL;
va_list ap;
va_start(ap, format);
- char *tmp = bc_strdup_vprintf(format, ap);
+ char *tmp = sb_strdup_vprintf(format, ap);
va_end(ap);
return tmp;
}
-// locale-independent implementation of isspace
bool
-bc_isspace(int c)
-{
- return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
-}
-
-
-bool
-bc_str_starts_with(const char *str, const char *prefix)
+sb_str_starts_with(const char *str, const char *prefix)
{
int str_l = strlen(str);
int str_lp = strlen(prefix);
@@ -207,7 +178,7 @@ bc_str_starts_with(const char *str, const char *prefix)
bool
-bc_str_ends_with(const char *str, const char *suffix)
+sb_str_ends_with(const char *str, const char *suffix)
{
int str_l = strlen(str);
int str_ls = strlen(suffix);
@@ -218,7 +189,7 @@ bc_str_ends_with(const char *str, const char *suffix)
char*
-bc_str_lstrip(char *str)
+sb_str_lstrip(char *str)
{
if (str == NULL)
return NULL;
@@ -242,7 +213,7 @@ bc_str_lstrip(char *str)
char*
-bc_str_rstrip(char *str)
+sb_str_rstrip(char *str)
{
if (str == NULL)
return NULL;
@@ -266,52 +237,52 @@ bc_str_rstrip(char *str)
char*
-bc_str_strip(char *str)
+sb_str_strip(char *str)
{
- return bc_str_lstrip(bc_str_rstrip(str));
+ return sb_str_lstrip(sb_str_rstrip(str));
}
char**
-bc_str_split(const char *str, char c, size_t max_pieces)
+sb_str_split(const char *str, char c, unsigned int max_pieces)
{
if (str == NULL)
return NULL;
- char **rv = bc_malloc(sizeof(char*));
- size_t i, start = 0, count = 0;
+ char **rv = sb_malloc(sizeof(char*));
+ unsigned int i, start = 0, count = 0;
for (i = 0; i < strlen(str) + 1; i++) {
if (str[0] == '\0')
break;
if ((str[i] == c && (!max_pieces || count + 1 < max_pieces)) || str[i] == '\0') {
- rv = bc_realloc(rv, (count + 1) * sizeof(char*));
- rv[count] = bc_malloc(i - start + 1);
+ rv = sb_realloc(rv, (count + 1) * sizeof(char*));
+ rv[count] = sb_malloc(i - start + 1);
memcpy(rv[count], str + start, i - start);
rv[count++][i - start] = '\0';
start = i + 1;
}
}
- rv = bc_realloc(rv, (count + 1) * sizeof(char*));
+ rv = sb_realloc(rv, (count + 1) * sizeof(char*));
rv[count] = NULL;
return rv;
}
char*
-bc_str_replace(const char *str, const char search, const char *replace)
+sb_str_replace(const char *str, const char search, const char *replace)
{
- char **pieces = bc_str_split(str, search, 0);
+ char **pieces = sb_str_split(str, search, 0);
if (pieces == NULL)
return NULL;
- char* rv = bc_strv_join(pieces, replace);
- bc_strv_free(pieces);
+ char* rv = sb_strv_join(pieces, replace);
+ sb_strv_free(pieces);
if (rv == NULL)
- return bc_strdup(str);
+ return sb_strdup(str);
return rv;
}
char*
-bc_str_find(const char *str, char c)
+sb_str_find(const char *str, char c)
{
// this is somewhat similar to strchr, but respects '\' escaping.
if (str == NULL)
@@ -331,33 +302,8 @@ bc_str_find(const char *str, char c)
}
-bool
-bc_str_to_bool(const char *str)
-{
- if (str == NULL)
- return false;
-
- if (0 == strcmp(str, "1"))
- return true;
-
- if (0 == strcasecmp(str, "y"))
- return true;
-
- if (0 == strcasecmp(str, "yes"))
- return true;
-
- if (0 == strcasecmp(str, "true"))
- return true;
-
- if (0 == strcasecmp(str, "on"))
- return true;
-
- return false;
-}
-
-
void
-bc_strv_free(char **strv)
+sb_strv_free(char **strv)
{
if (strv == NULL)
return;
@@ -368,22 +314,22 @@ bc_strv_free(char **strv)
char*
-bc_strv_join(char **strv, const char *separator)
+sb_strv_join(char **strv, const char *separator)
{
if (strv == NULL || separator == NULL)
return NULL;
- bc_string_t *str = bc_string_new();
+ sb_string_t *str = sb_string_new();
for (size_t i = 0; strv[i] != NULL; i++) {
- str = bc_string_append(str, strv[i]);
+ str = sb_string_append(str, strv[i]);
if (strv[i + 1] != NULL)
- str = bc_string_append(str, separator);
+ str = sb_string_append(str, separator);
}
- return bc_string_free(str, false);
+ return sb_string_free(str, false);
}
size_t
-bc_strv_length(char **strv)
+sb_strv_length(char **strv)
{
if (strv == NULL)
return 0;
@@ -393,23 +339,23 @@ bc_strv_length(char **strv)
}
-bc_string_t*
-bc_string_new(void)
+sb_string_t*
+sb_string_new(void)
{
- bc_string_t* rv = bc_malloc(sizeof(bc_string_t));
+ sb_string_t* rv = sb_malloc(sizeof(sb_string_t));
rv->str = NULL;
rv->len = 0;
rv->allocated_len = 0;
// initialize with empty string
- rv = bc_string_append(rv, "");
+ rv = sb_string_append(rv, "");
return rv;
}
char*
-bc_string_free(bc_string_t *str, bool free_str)
+sb_string_free(sb_string_t *str, bool free_str)
{
if (str == NULL)
return NULL;
@@ -423,18 +369,18 @@ bc_string_free(bc_string_t *str, bool free_str)
}
-bc_string_t*
-bc_string_dup(bc_string_t *str)
+sb_string_t*
+sb_string_dup(sb_string_t *str)
{
if (str == NULL)
return NULL;
- bc_string_t* new = bc_string_new();
- return bc_string_append_len(new, str->str, str->len);
+ sb_string_t* new = sb_string_new();
+ return sb_string_append_len(new, str->str, str->len);
}
-bc_string_t*
-bc_string_append_len(bc_string_t *str, const char *suffix, size_t len)
+sb_string_t*
+sb_string_append_len(sb_string_t *str, const char *suffix, size_t len)
{
if (str == NULL)
return NULL;
@@ -443,8 +389,8 @@ bc_string_append_len(bc_string_t *str, const char *suffix, size_t len)
size_t old_len = str->len;
str->len += len;
if (str->len + 1 > str->allocated_len) {
- str->allocated_len = (((str->len + 1) / BC_STRING_CHUNK_SIZE) + 1) * BC_STRING_CHUNK_SIZE;
- str->str = bc_realloc(str->str, str->allocated_len);
+ str->allocated_len = (((str->len + 1) / SB_STRING_CHUNK_SIZE) + 1) * SB_STRING_CHUNK_SIZE;
+ str->str = sb_realloc(str->str, str->allocated_len);
}
memcpy(str->str + old_len, suffix, len);
str->str[str->len] = '\0';
@@ -452,26 +398,26 @@ bc_string_append_len(bc_string_t *str, const char *suffix, size_t len)
}
-bc_string_t*
-bc_string_append(bc_string_t *str, const char *suffix)
+sb_string_t*
+sb_string_append(sb_string_t *str, const char *suffix)
{
if (str == NULL)
return NULL;
const char *my_suffix = suffix == NULL ? "" : suffix;
- return bc_string_append_len(str, my_suffix, strlen(my_suffix));
+ return sb_string_append_len(str, my_suffix, strlen(my_suffix));
}
-bc_string_t*
-bc_string_append_c(bc_string_t *str, char c)
+sb_string_t*
+sb_string_append_c(sb_string_t *str, char c)
{
if (str == NULL)
return NULL;
size_t old_len = str->len;
str->len += 1;
if (str->len + 1 > str->allocated_len) {
- str->allocated_len = (((str->len + 1) / BC_STRING_CHUNK_SIZE) + 1) * BC_STRING_CHUNK_SIZE;
- str->str = bc_realloc(str->str, str->allocated_len);
+ str->allocated_len = (((str->len + 1) / SB_STRING_CHUNK_SIZE) + 1) * SB_STRING_CHUNK_SIZE;
+ str->str = sb_realloc(str->str, str->allocated_len);
}
str->str[old_len] = c;
str->str[str->len] = '\0';
@@ -479,25 +425,23 @@ bc_string_append_c(bc_string_t *str, char c)
}
-bc_string_t*
-bc_string_append_printf(bc_string_t *str, const char *format, ...)
+sb_string_t*
+sb_string_append_printf(sb_string_t *str, const char *format, ...)
{
if (str == NULL)
return NULL;
- if (format == NULL)
- return str;
va_list ap;
va_start(ap, format);
- char *tmp = bc_strdup_vprintf(format, ap);
+ char *tmp = sb_strdup_vprintf(format, ap);
va_end(ap);
- str = bc_string_append(str, tmp);
+ str = sb_string_append(str, tmp);
free(tmp);
return str;
}
-bc_string_t*
-bc_string_append_escaped(bc_string_t *str, const char *suffix)
+sb_string_t*
+sb_string_append_escaped(sb_string_t *str, const char *suffix)
{
if (str == NULL)
return NULL;
@@ -510,16 +454,16 @@ bc_string_append_escaped(bc_string_t *str, const char *suffix)
continue;
}
escaped = false;
- str = bc_string_append_c(str, suffix[i]);
+ str = sb_string_append_c(str, suffix[i]);
}
return str;
}
-bc_trie_t*
-bc_trie_new(bc_free_func_t free_func)
+sb_trie_t*
+sb_trie_new(sb_free_func_t free_func)
{
- bc_trie_t *trie = bc_malloc(sizeof(bc_trie_t));
+ sb_trie_t *trie = sb_malloc(sizeof(sb_trie_t));
trie->root = NULL;
trie->free_func = free_func;
return trie;
@@ -527,43 +471,43 @@ bc_trie_new(bc_free_func_t free_func)
static void
-bc_trie_free_node(bc_trie_t *trie, bc_trie_node_t *node)
+sb_trie_free_node(sb_trie_t *trie, sb_trie_node_t *node)
{
if (trie == NULL || node == NULL)
return;
if (node->data != NULL && trie->free_func != NULL)
trie->free_func(node->data);
- bc_trie_free_node(trie, node->next);
- bc_trie_free_node(trie, node->child);
+ sb_trie_free_node(trie, node->next);
+ sb_trie_free_node(trie, node->child);
free(node);
}
void
-bc_trie_free(bc_trie_t *trie)
+sb_trie_free(sb_trie_t *trie)
{
if (trie == NULL)
return;
- bc_trie_free_node(trie, trie->root);
+ sb_trie_free_node(trie, trie->root);
free(trie);
}
void
-bc_trie_insert(bc_trie_t *trie, const char *key, void *data)
+sb_trie_insert(sb_trie_t *trie, const char *key, void *data)
{
if (trie == NULL || key == NULL || data == NULL)
return;
- bc_trie_node_t *parent = NULL;
- bc_trie_node_t *previous;
- bc_trie_node_t *current;
- bc_trie_node_t *tmp;
+ sb_trie_node_t *parent = NULL;
+ sb_trie_node_t *previous;
+ sb_trie_node_t *current;
+ sb_trie_node_t *tmp;
while (1) {
if (trie->root == NULL || (parent != NULL && parent->child == NULL)) {
- current = bc_malloc(sizeof(bc_trie_node_t));
+ current = sb_malloc(sizeof(sb_trie_node_t));
current->key = *key;
current->data = NULL;
current->next = NULL;
@@ -589,7 +533,7 @@ bc_trie_insert(bc_trie_t *trie, const char *key, void *data)
if (previous == NULL || parent != NULL)
goto clean;
- current = bc_malloc(sizeof(bc_trie_node_t));
+ current = sb_malloc(sizeof(sb_trie_node_t));
current->key = *key;
current->data = NULL;
current->next = NULL;
@@ -610,13 +554,13 @@ clean:
void*
-bc_trie_lookup(bc_trie_t *trie, const char *key)
+sb_trie_lookup(sb_trie_t *trie, const char *key)
{
if (trie == NULL || trie->root == NULL || key == NULL)
return NULL;
- bc_trie_node_t *parent = trie->root;
- bc_trie_node_t *tmp;
+ sb_trie_node_t *parent = trie->root;
+ sb_trie_node_t *tmp;
while (1) {
for (tmp = parent; tmp != NULL; tmp = tmp->next) {
@@ -639,7 +583,7 @@ bc_trie_lookup(bc_trie_t *trie, const char *key)
static void
-bc_trie_size_node(bc_trie_node_t *node, size_t *count)
+sb_trie_size_node(sb_trie_node_t *node, size_t *count)
{
if (node == NULL || count == NULL)
return;
@@ -647,77 +591,55 @@ bc_trie_size_node(bc_trie_node_t *node, size_t *count)
if (node->key == '\0')
(*count)++;
- bc_trie_size_node(node->next, count);
- bc_trie_size_node(node->child, count);
+ sb_trie_size_node(node->next, count);
+ sb_trie_size_node(node->child, count);
}
size_t
-bc_trie_size(bc_trie_t *trie)
+sb_trie_size(sb_trie_t *trie)
{
if (trie == NULL)
return 0;
size_t count = 0;
- bc_trie_size_node(trie->root, &count);
+ sb_trie_size_node(trie->root, &count);
return count;
}
static void
-bc_trie_foreach_node(bc_trie_node_t *node, bc_string_t *str,
- bc_trie_foreach_func_t func, void *user_data)
+sb_trie_foreach_node(sb_trie_node_t *node, sb_string_t *str,
+ sb_trie_foreach_func_t func, void *user_data)
{
if (node == NULL || str == NULL || func == NULL)
return;
- if (node->key == '\0')
+ if (node->key == '\0') {
func(str->str, node->data, user_data);
+ return;
+ }
if (node->child != NULL) {
- bc_string_t *child = bc_string_dup(str);
- child = bc_string_append_c(child, node->key);
- bc_trie_foreach_node(node->child, child, func, user_data);
- bc_string_free(child, true);
+ sb_string_t *child = sb_string_dup(str);
+ child = sb_string_append_c(child, node->key);
+ sb_trie_foreach_node(node->child, child, func, user_data);
+ sb_string_free(child, true);
}
if (node->next != NULL)
- bc_trie_foreach_node(node->next, str, func, user_data);
+ sb_trie_foreach_node(node->next, str, func, user_data);
}
void
-bc_trie_foreach(bc_trie_t *trie, bc_trie_foreach_func_t func,
+sb_trie_foreach(sb_trie_t *trie, sb_trie_foreach_func_t func,
void *user_data)
{
if (trie == NULL || trie->root == NULL || func == NULL)
return;
- bc_string_t *str = bc_string_new();
- bc_trie_foreach_node(trie->root, str, func, user_data);
- bc_string_free(str, true);
-}
-
-
-char*
-bc_shell_quote(const char *command)
-{
- bc_string_t *rv = bc_string_new();
- bc_string_append_c(rv, '\'');
- if (command != NULL) {
- for (size_t i = 0; i < strlen(command); i++) {
- switch (command[i]) {
- case '!':
- bc_string_append(rv, "'\\!'");
- break;
- case '\'':
- bc_string_append(rv, "'\\''");
- break;
- default:
- bc_string_append_c(rv, command[i]);
- }
- }
- }
- bc_string_append_c(rv, '\'');
- return bc_string_free(rv, false);
+ sb_string_t *str = sb_string_new();
+ sb_trie_foreach_node(trie->root, str, func, user_data);
+ sb_string_free(str, true);
}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..aca02c0
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,102 @@
+/*
+ * blogc: A blog compiler.
+ * Copyright (C) 2014-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
+ *
+ * This program can be distributed under the terms of the BSD License.
+ * See the file LICENSE.
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+
+// memory
+
+typedef void (*sb_free_func_t) (void *ptr);
+
+void* sb_malloc(size_t size);
+void* sb_realloc(void *ptr, size_t size);
+
+
+// slist
+
+typedef struct _sb_slist_t {
+ struct _sb_slist_t *next;
+ void *data;
+} sb_slist_t;
+
+sb_slist_t* sb_slist_append(sb_slist_t *l, void *data);
+sb_slist_t* sb_slist_prepend(sb_slist_t *l, void *data);
+void sb_slist_free(sb_slist_t *l);
+void sb_slist_free_full(sb_slist_t *l, sb_free_func_t free_func);
+size_t sb_slist_length(sb_slist_t *l);
+
+
+// strfuncs
+
+char* sb_strdup(const char *s);
+char* sb_strndup(const char *s, size_t n);
+char* sb_strdup_vprintf(const char *format, va_list ap);
+char* sb_strdup_printf(const char *format, ...);
+bool sb_str_starts_with(const char *str, const char *prefix);
+bool sb_str_ends_with(const char *str, const char *suffix);
+char* sb_str_lstrip(char *str);
+char* sb_str_rstrip(char *str);
+char* sb_str_strip(char *str);
+char** sb_str_split(const char *str, char c, unsigned int max_pieces);
+char* sb_str_replace(const char *str, const char search, const char *replace);
+char* sb_str_find(const char *str, char c);
+void sb_strv_free(char **strv);
+char* sb_strv_join(char **strv, const char *separator);
+size_t sb_strv_length(char **strv);
+
+
+// string
+
+typedef struct {
+ char *str;
+ size_t len;
+ size_t allocated_len;
+} sb_string_t;
+
+sb_string_t* sb_string_new(void);
+char* sb_string_free(sb_string_t *str, bool free_str);
+sb_string_t* sb_string_dup(sb_string_t *str);
+sb_string_t* sb_string_append_len(sb_string_t *str, const char *suffix, size_t len);
+sb_string_t* sb_string_append(sb_string_t *str, const char *suffix);
+sb_string_t* sb_string_append_c(sb_string_t *str, char c);
+sb_string_t* sb_string_append_printf(sb_string_t *str, const char *format, ...);
+sb_string_t* sb_string_append_escaped(sb_string_t *str, const char *suffix);
+
+
+// trie
+
+typedef struct _sb_trie_node_t {
+ char key;
+ void *data;
+ struct _sb_trie_node_t *next, *child;
+} sb_trie_node_t;
+
+struct _sb_trie_t {
+ sb_trie_node_t *root;
+ sb_free_func_t free_func;
+};
+
+typedef struct _sb_trie_t sb_trie_t;
+
+typedef void (*sb_trie_foreach_func_t)(const char *key, void *data,
+ void *user_data);
+
+sb_trie_t* sb_trie_new(sb_free_func_t free_func);
+void sb_trie_free(sb_trie_t *trie);
+void sb_trie_insert(sb_trie_t *trie, const char *key, void *data);
+void* sb_trie_lookup(sb_trie_t *trie, const char *key);
+size_t sb_trie_size(sb_trie_t *trie);
+void sb_trie_foreach(sb_trie_t *trie, sb_trie_foreach_func_t func,
+ void *user_data);
+
+#endif /* _UTILS_H */