diff --git a/Makefile b/Makefile
index 3a8a9c4..e45b2df 100644
--- a/Makefile
+++ b/Makefile
@@ -24,8 +24,8 @@ 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 build/obj/MarkdownParser.o build/obj/Article.o
- $(CXXC) -o $(PROGRAM_NAME) build/obj/main.o build/obj/ConfigUtils.o build/obj/WebsiteBuilder.o build/obj/SwgRuntime.o build/obj/MarkdownParser.o build/obj/Article.o
+$(PROGRAM_NAME): build/ build/obj/ build/obj/main.o build/obj/ConfigUtils.o build/obj/WebsiteBuilder.o build/obj/SwgRuntime.o build/obj/MarkdownParser.o build/obj/Article.o build/obj/Common.o build/obj/BlogBuilder.o
+ $(CXXC) -o $(PROGRAM_NAME) build/obj/main.o build/obj/ConfigUtils.o build/obj/WebsiteBuilder.o build/obj/SwgRuntime.o build/obj/MarkdownParser.o build/obj/Article.o build/obj/Common.o build/obj/BlogBuilder.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
@@ -45,6 +45,12 @@ build/obj/MarkdownParser.o: src/MarkdownParser.hxx src/MarkdownParser.cxx
build/obj/Article.o: src/Article.hxx src/Article.cxx
$(CXXC) $(CXXFLAGS) -o build/obj/Article.o -c src/Article.cxx
+build/obj/Common.o: src/Common.hxx src/Common.cxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/Common.o -c src/Common.cxx
+
+build/obj/BlogBuilder.o: src/BlogBuilder.hxx src/BlogBuilder.cxx
+ $(CXXC) $(CXXFLAGS) -o build/obj/BlogBuilder.o -c src/BlogBuilder.cxx
+
build/:
mkdir -p build/
diff --git a/src/BlogBuilder.cxx b/src/BlogBuilder.cxx
new file mode 100644
index 0000000..d653b42
--- /dev/null
+++ b/src/BlogBuilder.cxx
@@ -0,0 +1,141 @@
+/*
+ * 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 "BlogBuilder.hxx"
+
+#include
+#include
+
+#include "Article.hxx"
+#include "WebsiteBuilder.hxx"
+
+std::string blog_relative_path(std::string const &pathPrefix, std::string const &path) {
+ return path.substr(pathPrefix.length() - 1);
+}
+
+bool is_valid_article(std::string const &pathPrefix, std::string const &path) {
+ return std::regex_search(blog_relative_path(pathPrefix, path), std::regex("^/\\d{4}/\\d{2}/.*\\.md"));
+}
+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); // Output Blog Path
+ obp /= "blog";
+ obp /= blog->dir;
+ try {
+ // Create blog directory
+ if (!std::filesystem::exists(obp)) std::filesystem::create_directories(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);
+ }
+
+ std::list am;
+ //std::map> sorted_articles;
+
+ 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();
+
+ // Go ahead and parse article metadata.
+ Article::Metadata *articleMetadata = (Article::Metadata*) malloc(sizeof(Article::Metadata));
+ Article::get_metadata(a, articleMetadata);
+ std::cout << "Parsed metadata for article \"" << articleMetadata->title << "\"\n\tPublished on "
+ << ctime(&(articleMetadata->publish_ts)) << "\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, // Output Article Directory
+ rap; // Relative Article Path
+ oad /= year;
+ rap /= year;
+ if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
+
+ // Do the same for the article month.
+ oad /= month;
+ rap /= month;
+ if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
+
+ // Now create the article file.
+ std::string new_article_filename = getFilename(a, false);
+ new_article_filename += ".html";
+ oad /= new_article_filename;
+ rap /= new_article_filename;
+ compile_markdown(path, a, oad, articleMetadata);
+ strncpy(articleMetadata->path, rap.c_str(), sizeof(articleMetadata->path));
+ am.push_back(articleMetadata);
+ } 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);
+ }
+ }
+ // Sort am list.
+ am.sort(Article::Comparator::comp);
+ // Generate blog catalog.
+ std::string blog_html_catalog,
+ last_date = "";
+ blog_html_catalog += "";
+ blog_html_catalog += blog->name;
+ blog_html_catalog += " - Catalog
\n";
+
+ for (Article::Metadata *m : am) {
+ struct tm *article_time = gmtime(&(m->publish_ts));
+ std::string hr_date = get_hr_month(article_time->tm_mon);
+ hr_date += " ";
+ hr_date += std::to_string(article_time->tm_year + 1900);
+ // Check if this article belongs to the same "month + year" group as
+ // the last article. If it doesn't, add a new group to the catalog.
+ if (hr_date != last_date) {
+ last_date = hr_date;
+ blog_html_catalog += "\n\t";
+ blog_html_catalog += hr_date;
+ blog_html_catalog += "\n";
+ }
+ blog_html_catalog += "\t- path;
+ blog_html_catalog += "\">";
+ blog_html_catalog += m->title;
+ blog_html_catalog += "
\n";
+ // Free memory as it's no longer needed.
+ free(m);
+ }
+ blog_html_catalog += "
";
+ // Put catalog HTML into template and save it to the blog directory.
+ std::filesystem::path catalog_output_path = obp;
+ catalog_output_path /= "index.html";
+
+ std::string html_template = get_template(path);
+ std::regex content_placeholder("");
+ std::string catalog_html_contents = std::regex_replace(html_template, content_placeholder, blog_html_catalog);
+
+ FILE *catalog_output_file = fopen(catalog_output_path.c_str(), "w");
+ fputs(catalog_html_contents.c_str(), catalog_output_file);
+ fclose(catalog_output_file);
+}
+
diff --git a/src/BlogBuilder.hxx b/src/BlogBuilder.hxx
new file mode 100644
index 0000000..91a369f
--- /dev/null
+++ b/src/BlogBuilder.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 "Common.hxx"
+
+bool is_valid_article(std::string const &pathPrefix, std::string const &path);
+void build_blog_structure(std::string const &path, std::string const &prefix, std::list const &articles, Blog *blog);
diff --git a/src/Common.cxx b/src/Common.cxx
new file mode 100644
index 0000000..170cd45
--- /dev/null
+++ b/src/Common.cxx
@@ -0,0 +1,97 @@
+/*
+ * 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 "Common.hxx"
+
+#include
+
+const char* HR_MONTH[] = {
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December"
+};
+
+const char* get_hr_month(unsigned short month) {
+ return HR_MONTH[month];
+}
+
+std::string getFilename(std::string const &path, bool const ext) {
+ int extSeparator = -1;
+ // Backwards search for directory separator.
+ for (int x = path.length(); x > 0; --x) {
+ if (path[x] == '/') return ext ? path.substr(x + 1) : path.substr(x + 1, extSeparator - x - 1);
+ // If this function was called with "ext" set to false,
+ // generate a substring of the path containing the filename
+ // only.
+ else if (!ext && path[x] == '.' && extSeparator == -1)
+ extSeparator = x;
+ }
+ return path;
+}
+
+std::filesystem::path get_output_path(std::string const &path) {
+ std::filesystem::path output = path;
+ return output /= "output";
+}
+
+/**
+ * Used to determine whether a file should be copied onto the output directory
+ * or not. Certain files, like the config file shouldn't be copied.
+ */
+bool is_special_file(std::string const &filename) {
+ if (filename == "swg.cfg" || filename.find("__swg_") == 0)
+ return true;
+ return false;
+}
+
+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;
+}
+
diff --git a/src/Common.hxx b/src/Common.hxx
index 449f707..806f4a8 100644
--- a/src/Common.hxx
+++ b/src/Common.hxx
@@ -21,17 +21,33 @@
#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 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_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
+#define RETURN_FAILED_UNKNOWN_ERROR 10
+
+#include
+#include
+
+struct Blog {
+ char name[64],
+ path[64],
+ dir[64];
+ int test;
+};
+
+const char* get_hr_month(unsigned short month);
+std::string getFilename(std::string const &path, bool const ext = true);
+std::filesystem::path get_output_path(std::string const &path);
+bool is_special_file(std::string const &filename);
+std::string get_template(std::string const &path);
diff --git a/src/SwgContext.hxx b/src/SwgContext.hxx
index df0d3de..61e92ea 100644
--- a/src/SwgContext.hxx
+++ b/src/SwgContext.hxx
@@ -24,12 +24,7 @@
#include
#include
-struct Blog {
- char name[64],
- path[64],
- dir[64];
- int test;
-};
+#include "Common.hxx"
struct SwgContext {
std::string websiteName,
diff --git a/src/WebsiteBuilder.cxx b/src/WebsiteBuilder.cxx
index 36d1635..15a1de3 100644
--- a/src/WebsiteBuilder.cxx
+++ b/src/WebsiteBuilder.cxx
@@ -21,7 +21,6 @@
#include
#include
-#include
#include
#include
#include
@@ -30,53 +29,11 @@
#include "SwgRuntime.hxx"
#include "SwgContext.hxx"
#include "ConfigUtils.hxx"
-// #include "Blog.h"
+#include "BlogBuilder.hxx"
#include "Common.hxx"
#include "MarkdownParser.hxx"
#include "Article.hxx"
-static std::string HR_MONTH[] = {
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
-};
-
-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, bool const ext = true) {
- int extSeparator = -1;
- // Backwards search for directory separator.
- for (int x = path.length(); x > 0; --x) {
- if (path[x] == '/') return ext ? path.substr(x + 1) : path.substr(x + 1, extSeparator - x - 1);
- // If this function was called with "ext" set to false,
- // generate a substring of the path containing the filename
- // only.
- else if (!ext && path[x] == '.' && extSeparator == -1)
- extSeparator = x;
- }
- 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);
@@ -103,30 +60,6 @@ void build_dir_structure(std::string const &path) {
}
}
-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 compile_markdown(std::string const &path, std::string const &md, std::string const &to, Article::Metadata *metadata = NULL) {
FILE *articleOutput = fopen(to.c_str(), "w");
@@ -144,7 +77,7 @@ void compile_markdown(std::string const &path, std::string const &md, std::strin
if (metadata->publish_str[0] == '\0') {
struct tm *publish_tm = gmtime(&(metadata->publish_ts));
//article_html += ctime(&(metadata->publish_ts));
- article_html += HR_MONTH[publish_tm->tm_mon];
+ article_html += get_hr_month(publish_tm->tm_mon);
article_html += " ";
article_html += std::to_string(publish_tm->tm_mday);
article_html += ", ";
@@ -165,118 +98,6 @@ void compile_markdown(std::string const &path, std::string const &md, std::strin
fclose(articleOutput);
}
-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); // Output Blog Path
- obp /= "blog";
- obp /= blog->dir;
- try {
- // Create blog directory
- if (!std::filesystem::exists(obp)) std::filesystem::create_directories(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);
- }
-
- std::list am;
- //std::map> sorted_articles;
-
- 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();
-
- // Go ahead and parse article metadata.
- Article::Metadata *articleMetadata = (Article::Metadata*) malloc(sizeof(Article::Metadata));
- Article::get_metadata(a, articleMetadata);
- std::cout << "Parsed metadata for article \"" << articleMetadata->title << "\"\n\tPublished on "
- << ctime(&(articleMetadata->publish_ts)) << "\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, // Output Article Directory
- rap; // Relative Article Path
- oad /= year;
- rap /= year;
- if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
-
- // Do the same for the article month.
- oad /= month;
- rap /= month;
- if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
-
- // Now create the article file.
- std::string new_article_filename = getFilename(a, false);
- new_article_filename += ".html";
- oad /= new_article_filename;
- rap /= new_article_filename;
- compile_markdown(path, a, oad, articleMetadata);
- strncpy(articleMetadata->path, rap.c_str(), sizeof(articleMetadata->path));
- am.push_back(articleMetadata);
- } 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);
- }
- }
- // Sort am list.
- am.sort(Article::Comparator::comp);
- // Generate blog catalog.
- std::string blog_html_catalog = "\n",
- last_date = "";
-
- for (Article::Metadata *m : am) {
- struct tm *article_time = gmtime(&(m->publish_ts));
- std::string hr_date = HR_MONTH[article_time->tm_mon];
- hr_date += " ";
- hr_date += std::to_string(article_time->tm_year + 1900);
- // Check if this article belongs to the same "month + year" group as
- // the last article. If it doesn't, add a new group to the catalog.
- if (hr_date != last_date) {
- last_date = hr_date;
- blog_html_catalog += "\n\t";
- blog_html_catalog += hr_date;
- blog_html_catalog += "\n";
- }
- blog_html_catalog += "\t- path;
- blog_html_catalog += "\">";
- blog_html_catalog += m->title;
- blog_html_catalog += "
\n";
- // Free memory as it's no longer needed.
- free(m);
- }
- blog_html_catalog += "
";
- // Put catalog HTML into template and save it to the blog directory.
- std::filesystem::path catalog_output_path = obp;
- catalog_output_path /= "index.html";
-
- std::string html_template = get_template(path);
- std::regex content_placeholder("");
- std::string catalog_html_contents = std::regex_replace(html_template, content_placeholder, blog_html_catalog);
-
- FILE *catalog_output_file = fopen(catalog_output_path.c_str(), "w");
- fputs(catalog_html_contents.c_str(), catalog_output_file);
- fclose(catalog_output_file);
-}
-
-/**
- * Used to determine whether a file should be copied onto the output directory
- * or not. Certain files, like the config file shouldn't be copied.
- */
-bool is_special_file(std::string const &filename) {
- if (filename == "swg.cfg" || filename.find("__swg_") == 0)
- return true;
- return false;
-}
-
void build_website(SwgContext &ctx, std::string const &path) {
build_dir_structure(path);
@@ -360,7 +181,7 @@ void build_website(SwgContext &ctx, std::string const &path) {
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()))
+ if (!is_valid_article(relativePath, dir_entry.path()))
failedArticles.insert(failedArticles.end(), dir_entry.path());
else parsedArticles.insert(parsedArticles.end(), dir_entry.path());
}
diff --git a/src/WebsiteBuilder.hxx b/src/WebsiteBuilder.hxx
index 68b10e8..96cd556 100644
--- a/src/WebsiteBuilder.hxx
+++ b/src/WebsiteBuilder.hxx
@@ -23,5 +23,10 @@
#include "SwgContext.hxx"
-void build_website(SwgContext &ctx, std::string const &path);
+#include "Article.hxx"
+
+#include
+
+void build_website(SwgContext &ctx, std::string const &path);
+void compile_markdown(std::string const &path, std::string const &md, std::string const &to, Article::Metadata *metadata = NULL);