aboutsummaryrefslogtreecommitdiffstats
path: root/src/blogc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/blogc.c')
-rw-r--r--src/blogc.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/blogc.c b/src/blogc.c
new file mode 100644
index 0000000..2338c9e
--- /dev/null
+++ b/src/blogc.c
@@ -0,0 +1,285 @@
+/*
+ * 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 */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+#include <errno.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "source-parser.h"
+#include "template-parser.h"
+#include "loader.h"
+#include "renderer.h"
+#include "error.h"
+#include "utils.h"
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "Unknown"
+#endif
+
+
+static void
+blogc_print_help(void)
+{
+ printf(
+ "usage:\n"
+ " blogc [-h] [-v] [-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"
+ " -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] [-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;
+#if defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_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 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 '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) {
+ 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;
+ }
+
+ sb_slist_t* l = blogc_template_parse_from_file(template, &err);
+ if (err != NULL) {
+ blogc_error_print(err);
+ rv = 2;
+ goto cleanup3;
+ }
+
+ 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 cleanup3;
+ }
+
+ if (template == NULL) {
+ blogc_print_usage();
+ fprintf(stderr, "blogc: error: argument -t is required when rendering content\n");
+ rv = 2;
+ goto cleanup3;
+ }
+
+ 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;
+}