diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..81ee390
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 luca0N!
+#
+# This file is part of Static Website Generator (swg).
+#
+# Static Website Generator (swg) is free software: you can redistribute it
+# and/or modify it under the terms of the version 3 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# Static Website Generator (swg) is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Static Website Generator (swg). If not, see
+# .
+#
+# Contact luca0N! by e-mail: .
+
+PROGRAM_NAME=swg
+
+CXXC=g++
+CXXFLAGS=-Wall -Wextra -g -O0 -std=c++17
+
+.PHONY: clean
+
+$(PROGRAM_NAME): build/ build/obj/ build/obj/main.o build/obj/ConfigUtils.o build/obj/WebsiteBuilder.o build/obj/SwgRuntime.o
+ $(CXXC) -o $(PROGRAM_NAME) build/obj/main.o build/obj/ConfigUtils.o build/obj/WebsiteBuilder.o build/obj/SwgRuntime.o
+
+build/obj/main.o: src/main.cxx src/ConfigUtils.hxx src/SwgContext.hxx src/WebsiteBuilder.hxx src/Common.hxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/main.o -c src/main.cxx
+
+build/obj/ConfigUtils.o: src/ConfigUtils.cxx src/SwgContext.hxx src/Common.hxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/ConfigUtils.o -c src/ConfigUtils.cxx
+
+build/obj/WebsiteBuilder.o: src/WebsiteBuilder.hxx src/WebsiteBuilder.cxx src/SwgRuntime.hxx src/Common.hxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/WebsiteBuilder.o -c src/WebsiteBuilder.cxx
+
+build/obj/SwgRuntime.o: src/SwgRuntime.hxx src/SwgRuntime.cxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/SwgRuntime.o -c src/SwgRuntime.cxx
+
+build/:
+ mkdir -p build/
+
+build/obj/:
+ mkdir -p build/obj/
+
+clean:
+ rm -rfv build/ $(PROGRAM_NAME)
diff --git a/src/Common.hxx b/src/Common.hxx
new file mode 100644
index 0000000..3651593
--- /dev/null
+++ b/src/Common.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#pragma once
+
+#define PROGRAM_NAME "SWG"
+#define PROGRAM_VERSION 1
+#define PROGRAM_VERSION_NAME "0.1-dev"
+#define PROGRAM_COPYRIGHT "luca0N!"
+#define PROGRAM_COPYRIGHT_YEARS "2022"
+
+#define RETURN_SUCCESSFUL 0
+
+#define RETURN_FAILED_INVALID_SYNTAX 1
+#define RETURN_FAILED_CONFIG_INVALID_SYNTAX 2
+#define RETURN_FAILED_INVALID_DIRECTORY 3
+#define RETURN_FAILED_WEBSITE_BUILD_EXISTS 4
+
+#define RETURN_FAILED_UNKNOWN_ERROR 10
+
diff --git a/src/ConfigUtils.cxx b/src/ConfigUtils.cxx
new file mode 100644
index 0000000..c0d2436
--- /dev/null
+++ b/src/ConfigUtils.cxx
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#include "ConfigUtils.hxx"
+
+#include
+#include
+#include
+
+#include "Common.hxx"
+
+void parse_config(SwgContext *ctx, std::string const &path) {
+ std::string ctxCfgPath = path;
+ ctxCfgPath += "/swg.cfg";
+
+ FILE *ctxCfgFile = fopen(ctxCfgPath.c_str(), "r");
+
+ if (ctxCfgFile == NULL) {
+ std::cerr << "error: cannot open swg.cfg file, are you sure "
+ << path << " is a valid SWG directory?" << std::endl;
+ perror(ctxCfgPath.c_str());
+ exit(RETURN_FAILED_INVALID_DIRECTORY);
+ }
+
+ int buflen = 16;
+ char cbuf[buflen];
+
+ // Yes, this looks ugly, but it works for now. Will probably optimize
+ // this in the future :)
+ //
+ // TODO: Optimize config file interpretation
+
+ std::list lines = { "" };
+ while (fgets(cbuf, buflen, ctxCfgFile) != NULL) {
+ int eol = -1;
+ for (int c = 0; c < buflen; c++) {
+ if (cbuf[c] == '\0') {
+ break;
+ } else if (cbuf[c] == '\n') {
+ eol = c;
+ break;
+ }
+ }
+ if (eol > -1) {
+ cbuf[eol] = '\0';
+ *(--lines.end()) += cbuf;
+ lines.insert(lines.end(), "");
+ } else
+ *(--lines.end()) += cbuf;
+ }
+
+ fclose(ctxCfgFile);
+
+ std::cout << "Done loading configuration file in memory" << std::endl;
+
+ // Parse configuration file
+
+ enum ConfigNamespace { General, Blogs };
+ ConfigNamespace ns;
+
+ int blogCount = 0;
+ Blog *currentBlog = NULL; // (Blog*) malloc(sizeof(Blog));
+
+ // currentBlog->name = "asd";
+
+ for (std::string line : lines) {
+ // Skip comment lines
+ if (line.c_str()[0] == '#')
+ continue;
+
+ if (line.c_str()[0] == '[') {
+ if (line == "[General]") {
+ ns = General;
+ continue;
+ } else if (line == "[Blogs]") {
+ ns = Blogs;
+ continue;
+ } else {
+ std::cerr << "error: unknown namespace in config file: " << line << std::endl;
+ exit(RETURN_FAILED_CONFIG_INVALID_SYNTAX);
+ }
+ }
+
+ // Skip empty lines
+ if (line == "") continue;
+
+ int end = line.find("=");
+ std::string k = line.substr(0, end),
+ v = line.substr(end + 2,
+ (line.length() - 1) - (end + 2));
+
+ switch (ns) {
+ case General:
+ if (k == "WebsiteName")
+ ctx->websiteName = v;
+ else if (k == "WebsitePublisher")
+ ctx->websitePublisher = v;
+ else if (k == "WebsiteCopyrightHolder")
+ ctx->websiteCopyrightHolder = v;
+ else if (k == "WebsiteCopyrightYears")
+ ctx->websiteCopyrightYears = v;
+ else if (k == "websiteContentLicense")
+ ctx->websiteContentLicense = v;
+ else
+ std::cerr << "warning: unknown key in namespace \"General\": " << k << std::endl;
+ continue;
+ case Blogs:
+ // FIXME: This code only accepts up to 9 blogs.
+ if (line.substr(0, 4) == "Blog") {
+ // Check No.
+ int blogNo = atoi(line.substr(4, 1).c_str());
+ if (blogNo != blogCount &&
+ blogNo != blogCount +1){
+ std::cerr << "error: blog declaration must be incremental (received "
+ << blogNo << " instead of " << blogCount << " or " << blogCount + 1
+ << ")" << std::endl;
+ exit(RETURN_FAILED_CONFIG_INVALID_SYNTAX);
+ } else if (blogNo == blogCount + 1) {
+ blogCount++;
+ if (currentBlog != NULL) ctx->blogs.push_back(currentBlog);
+ currentBlog = (Blog*) malloc(sizeof(Blog));
+ // Clear allocated memory
+ memset(currentBlog, 0, sizeof(Blog));
+ }
+
+ assert(currentBlog != NULL);
+ //std::cout << "Attempting to set K/V" << std::endl;
+
+ if (k.substr(5) == "Path") {
+ //std::cout << "Setting path (" << v << ") for " << blogCount << std::endl;
+ strncpy(currentBlog->path, v.c_str(), sizeof(currentBlog->path));
+ } else if (k.substr(5) == "Name") {
+ //std::cout << "Setting name (" << v << ") for " << blogCount << std::endl;
+ strncpy(currentBlog->name, v.c_str(), sizeof(currentBlog->name));
+ } else if (k.substr(5) == "Dir") {
+ strncpy(currentBlog->dir, v.c_str(), sizeof(currentBlog->dir));
+ } else {
+ std::cerr << "warning: unknown key in namespace \"Blogs\": " << k << "\n";
+ }
+ }
+ continue;
+ }
+ }
+ if (currentBlog != NULL) ctx->blogs.push_back(currentBlog);
+ /*std::cout << "Loaded " << blogCount << " blogs into memory" << std::endl;
+ for (Blog *b : ctx->blogs) {
+ std::cout << "\t" << b->name << ": " << b->path << std::endl;
+ }*/
+}
diff --git a/src/ConfigUtils.hxx b/src/ConfigUtils.hxx
new file mode 100644
index 0000000..7a63a73
--- /dev/null
+++ b/src/ConfigUtils.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "SwgContext.hxx"
+
+void parse_config(SwgContext *ctx, std::string const &path);
+
diff --git a/src/SwgContext.hxx b/src/SwgContext.hxx
new file mode 100644
index 0000000..df0d3de
--- /dev/null
+++ b/src/SwgContext.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#pragma once
+
+#include
+#include
+
+struct Blog {
+ char name[64],
+ path[64],
+ dir[64];
+ int test;
+};
+
+struct SwgContext {
+ std::string websiteName,
+ websitePublisher,
+ websiteCopyrightHolder,
+ websiteCopyrightYears,
+ websiteContentLicense;
+
+ // TODO: Use smart pointers
+ std::list blogs;
+};
+
+/*class SwgContext {
+ public:
+ SwgContext();
+ SwgContext(std::string websiteName, std::string websitePublisher, std::string copyrightHolder, std::string copyrightYears, std::string contentLicense);
+
+
+ private:
+ std::string websiteName, websitePublisher;
+ std::string copyrightHolder, copyrightYears, contentLicense;
+};
+
+class BlogEntry {
+ public:
+ BlogEntry(std::string name, std::string path);
+ std::string getBlogName();
+ std::string getBlogPath();
+ private:
+ std::string path, name;
+};*/
diff --git a/src/SwgRuntime.cxx b/src/SwgRuntime.cxx
new file mode 100644
index 0000000..3b08809
--- /dev/null
+++ b/src/SwgRuntime.cxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#include "SwgRuntime.hxx"
+
+bool swg_rt_global_bool(const char *name, bool ifne) {
+ // TODO: Check for runtime option and return it (if it's set, return ifne otherwise).
+ return ifne;
+}
diff --git a/src/SwgRuntime.hxx b/src/SwgRuntime.hxx
new file mode 100644
index 0000000..18deba8
--- /dev/null
+++ b/src/SwgRuntime.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#pragma once
+
+// Utilities for options set during runtime by the end-user (i.e. through command line options).
+
+bool swg_rt_global_bool(const char *name, bool ifne);
+
diff --git a/src/WebsiteBuilder.cxx b/src/WebsiteBuilder.cxx
new file mode 100644
index 0000000..38be321
--- /dev/null
+++ b/src/WebsiteBuilder.cxx
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "SwgRuntime.hxx"
+#include "SwgContext.hxx"
+#include "ConfigUtils.hxx"
+// #include "Blog.h"
+#include "Common.hxx"
+
+std::string blog_relative_path(std::string const &pathPrefix, std::string const &path) {
+ return path.substr(pathPrefix.length() - 1);
+}
+
+std::string getFilename(std::string const &path) {
+ // Backwards search for directory separator.
+ for (int x = path.length(); x > 0; --x) {
+ if (path[x] == '/')
+ return path.substr(x + 1);
+ }
+ return path;
+}
+
+bool isValidArticle(std::string const &pathPrefix, std::string const &path) {
+ return std::regex_search(blog_relative_path(pathPrefix, path), std::regex("^/\\d{4}/\\d{2}/.*\\.md"));
+}
+
+std::filesystem::path get_output_path(std::string const &path) {
+ std::filesystem::path output = path;
+ return output /= "output";
+}
+
+void build_dir_structure(std::string const &path) {
+ // Create directory tree, which will be used for the website.
+ std::filesystem::path rootDir = get_output_path(path);
+
+ // Create output directory if it doesn't exist.
+ try {
+ if(!std::filesystem::exists(rootDir)) std::filesystem::create_directory(rootDir);
+ } catch (std::filesystem::filesystem_error const &e) {
+ std::cerr << "error: fs error while generating output website directory structure on \""
+ << path << "\": " << e.what() << std::endl;
+ exit(RETURN_FAILED_UNKNOWN_ERROR);
+ }
+
+ // Check if a website has already been built.
+ //
+ // This is a safe measure to prevent overwriting an existing website
+ // output, but it can be skipped if explicitly asked by the end-user.
+ if (!swg_rt_global_bool("overwrite_existing", false)) {
+ std::filesystem::path websiteLock = rootDir /= ".swg_built";
+ if (std::filesystem::exists(websiteLock)) {
+ std::cout << "error: existing website build detected, exiting...\n";
+ exit(RETURN_FAILED_WEBSITE_BUILD_EXISTS);
+ }
+ }
+}
+
+std::string get_template(std::string const &path) {
+ // Template lookup
+ std::string stPath = path;
+ stPath += "/__swg_template.html";
+ FILE *swgTemplate = fopen(stPath.c_str(), "r");
+ if (swgTemplate == NULL) {
+ std::cerr << "error: couldn't open the SWG HTML template file; does it exist?\n";
+ perror(stPath.c_str());
+ exit(RETURN_FAILED_INVALID_DIRECTORY);
+ }
+
+ // Check for content placeholder
+ int buflen = 8;
+ char buf[buflen];
+ std::string htmlTemplate;
+ while (fgets(buf, buflen, swgTemplate) != NULL) {
+ htmlTemplate += buf;
+ }
+
+ fclose(swgTemplate);
+ std::cout << "Loaded HTML template into memory.\n";
+ return htmlTemplate;
+}
+
+void build_blog_structure(std::string const &path, std::string const &prefix, std::list const &articles, Blog *blog) {
+ std::filesystem::path obp = get_output_path(path) /= blog->dir; // Output Blog Path
+ try {
+ // Create blog directory
+ if (!std::filesystem::exists(obp)) std::filesystem::create_directory(obp);
+ } catch (std::filesystem::filesystem_error const &e) {
+ std::cerr << "error: failed to create directory for blog \"" << blog->name << "\": " << e.what() << std::endl;
+ exit(RETURN_FAILED_UNKNOWN_ERROR);
+ }
+
+ for (std::string const &a : articles) {
+ std::string articlePath = blog_relative_path(prefix, a);
+ std::regex yearMonth("(\\d+)");
+ auto match = std::sregex_iterator(articlePath.begin(), articlePath.end(), yearMonth);
+
+ std::string year = match->str(),
+ month = (++match)->str();
+
+ /*std::cout << "Year for \"" << articlePath << "\": " << year << "\n";
+ std::cout << "Month for \"" << articlePath << "\": " << month << "\n";*/
+
+ // TODO: This code could be optimized by removing directory
+ // checks for every single article. Instead, add
+ // directory-checks for years and months to a queue (skipping
+ // existing ones) and then checking and creating the
+ // directories later as needed.
+ try {
+ // Create directory for the year of this article if it doesn't exist.
+ std::filesystem::path oad = obp /= year; // Output Article Directory
+ if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
+
+ // Do the same for the article month.
+ oad /= month;
+ if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
+
+ // Now create the article file.
+ oad /= getFilename(a);
+ FILE *articleOutput = fopen(oad.string().c_str(), "w");
+ FILE *articleFile = fopen(a.c_str(), "r");
+
+ std::string articleContents;
+
+ int buflen = 512;
+ char buf[buflen];
+
+ while (fgets(buf, buflen, articleFile) != NULL) {
+ articleContents += buf;
+ }
+
+ fclose(articleFile);
+
+ std::string htmlTemplate = get_template(path);
+ // NOTE: std::regex requires the C++11 standard.
+ std::regex contentPlaceholder("");
+ articleContents = std::regex_replace(htmlTemplate, contentPlaceholder, articleContents);
+
+ fputs(articleContents.c_str(), articleOutput);
+
+ fclose(articleOutput);
+ } catch (std::filesystem::filesystem_error const &e) {
+ std::cerr << "error: failed to create directory for an article from blog \""
+ << blog->name << "\": " << e.what() << std::endl;
+ exit(RETURN_FAILED_UNKNOWN_ERROR);
+ }
+ }
+}
+
+void build_website(SwgContext &ctx, std::string const &path) {
+ build_dir_structure(path);
+
+ // Blog lookup
+
+ bool failed = false;
+
+ for (Blog *b : ctx.blogs) {
+ std::list parsedArticles = { },
+ failedArticles = { };
+
+ // std::filesystem requires the C++17 standard.
+ std::string relativePath = path;
+ relativePath += "/";
+ relativePath += b->path;
+ const std::filesystem::path blogPath(relativePath);
+ try {
+ for (auto const &dir_entry : std::filesystem::recursive_directory_iterator(blogPath)) {
+ // Directory item iteration
+ std::cout << "\t" << dir_entry << std::endl;
+
+ // Check if the item is a Markdown file.
+ std::string filename = getFilename(dir_entry.path());
+ if (dir_entry.is_regular_file() &&
+ filename.find(".md") == filename.length() - 3) {
+ std::cout << "\t\tIs file: " << getFilename(dir_entry.path()) << "\n";
+ // Markdown files should be insite a YYYY/MM directory.
+ if (!isValidArticle(relativePath, dir_entry.path()))
+ failedArticles.insert(failedArticles.end(), dir_entry.path());
+ else parsedArticles.insert(parsedArticles.end(), dir_entry.path());
+ }
+ }
+ } catch (std::filesystem::filesystem_error const &e) {
+ std::cerr << "error: fs error while attempting to read path for "
+ << b->name << ": " << e.what() << std::endl;
+ failed = true;
+ }
+
+ /*std::cout << "Parsed " << parsedArticles << " articles;\n" <<
+ "Failed to parse " << failedArticles << " articles\n";*/
+
+ for (std::string const &a : parsedArticles)
+ std::cout << "Parsed: " << a << "\n";
+ for (std::string const &a : failedArticles)
+ std::cout << "Unable to parse: " << a << "\n";
+
+ build_blog_structure(path, relativePath, parsedArticles, b);
+
+ free(b);
+ }
+
+ if (failed) {
+ std::cerr << "Refusing to proceed due to previous errors\n";
+ exit(RETURN_FAILED_CONFIG_INVALID_SYNTAX);
+ }
+
+ // Website blog
+
+ // Append blog to website
+ //ctx.appendBlog(blog1);
+
+ // Generate website
+ //ctx.generateWebsite();
+}
diff --git a/src/WebsiteBuilder.hxx b/src/WebsiteBuilder.hxx
new file mode 100644
index 0000000..68b10e8
--- /dev/null
+++ b/src/WebsiteBuilder.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#pragma once
+
+#include "SwgContext.hxx"
+
+void build_website(SwgContext &ctx, std::string const &path);
+
diff --git a/src/main.cxx b/src/main.cxx
new file mode 100644
index 0000000..5882f46
--- /dev/null
+++ b/src/main.cxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 luca0N!
+ *
+ * This file is part of Static Website Generator (swg).
+ *
+ * Static Website Generator (swg) is free software: you can redistribute it
+ * and/or modify it under the terms of the version 3 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * Static Website Generator (swg) is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Static Website Generator (swg). If not, see
+ * .
+ *
+ * Contact luca0N! by e-mail: .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "SwgContext.hxx"
+#include "ConfigUtils.hxx"
+// #include "Blog.h"
+#include "Common.hxx"
+#include "WebsiteBuilder.hxx"
+
+void printUsage(const char *programName) {
+ std::cerr << programName << ": usage: " << programName << " [path]"
+ << std::endl;
+}
+
+void printIntro() {
+ printf("%s v%s (%d)\nCopyright (C) %s %s\n\n",
+ PROGRAM_NAME, PROGRAM_VERSION_NAME, PROGRAM_VERSION,
+ PROGRAM_COPYRIGHT_YEARS, PROGRAM_COPYRIGHT);
+}
+
+int main(int argc, char *argv[]) {
+ printIntro();
+
+ std::string path;
+
+ // Parse cmdline options
+ switch (argc) {
+ case 1:
+ path = ".";
+ std::cout << "Assuming working directory is \".\""
+ << std::endl;
+ break;
+ case 2:
+ path = argv[1];
+ std::cout << "Using specified directory: "
+ << path << std::endl;
+ break;
+ default:
+ printUsage(argv[0]);
+ exit(RETURN_FAILED_INVALID_SYNTAX);
+ break;
+ }
+
+ SwgContext ctx;
+ parse_config(&ctx, path);
+
+ build_website(ctx, path);
+
+ // Website blog
+
+ // Append blog to website
+ //ctx.appendBlog(blog1);
+
+ // Generate website
+ //ctx.generateWebsite();
+
+ return 0;
+}