Minor code refactoring
Moved certain functions to proper locations and added new source files for minor refactoring.
This commit is contained in:
parent
647ad56349
commit
d97ef91db8
10
Makefile
10
Makefile
|
@ -24,8 +24,8 @@ CXXFLAGS=-Wall -Wextra -g -O0 -std=c++17
|
||||||
|
|
||||||
.PHONY: clean
|
.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
|
$(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
|
$(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
|
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
|
$(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
|
build/obj/Article.o: src/Article.hxx src/Article.cxx
|
||||||
$(CXXC) $(CXXFLAGS) -o build/obj/Article.o -c 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/:
|
build/:
|
||||||
mkdir -p build/
|
mkdir -p build/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
* <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Contact luca0N! by e-mail: <luca0n [at] luca0n [dot] com>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BlogBuilder.hxx"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#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<std::string> 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<Article::Metadata*> am;
|
||||||
|
//std::map<std::string, std::list<std::string>> 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 += "<h2>";
|
||||||
|
blog_html_catalog += blog->name;
|
||||||
|
blog_html_catalog += " - Catalog</h2>\n<ul>";
|
||||||
|
|
||||||
|
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<span><b>";
|
||||||
|
blog_html_catalog += hr_date;
|
||||||
|
blog_html_catalog += "</b></span>\n";
|
||||||
|
}
|
||||||
|
blog_html_catalog += "\t<li><a href=\"./";
|
||||||
|
blog_html_catalog += m->path;
|
||||||
|
blog_html_catalog += "\">";
|
||||||
|
blog_html_catalog += m->title;
|
||||||
|
blog_html_catalog += "</a></li>\n";
|
||||||
|
// Free memory as it's no longer needed.
|
||||||
|
free(m);
|
||||||
|
}
|
||||||
|
blog_html_catalog += "</ul>";
|
||||||
|
// 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("<!--\\[_SWG: \\$CONTENT\\]-->");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
* <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Contact luca0N! by e-mail: <luca0n [at] luca0n [dot] com>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#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<std::string> const &articles, Blog *blog);
|
|
@ -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
|
||||||
|
* <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Contact luca0N! by e-mail: <luca0n [at] luca0n [dot] com>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Common.hxx"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -35,3 +35,19 @@
|
||||||
#define RETURN_FAILED_WEBSITE_BUILD_EXISTS 4
|
#define RETURN_FAILED_WEBSITE_BUILD_EXISTS 4
|
||||||
|
|
||||||
#define RETURN_FAILED_UNKNOWN_ERROR 10
|
#define RETURN_FAILED_UNKNOWN_ERROR 10
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
|
@ -24,12 +24,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
struct Blog {
|
#include "Common.hxx"
|
||||||
char name[64],
|
|
||||||
path[64],
|
|
||||||
dir[64];
|
|
||||||
int test;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SwgContext {
|
struct SwgContext {
|
||||||
std::string websiteName,
|
std::string websiteName,
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -30,53 +29,11 @@
|
||||||
#include "SwgRuntime.hxx"
|
#include "SwgRuntime.hxx"
|
||||||
#include "SwgContext.hxx"
|
#include "SwgContext.hxx"
|
||||||
#include "ConfigUtils.hxx"
|
#include "ConfigUtils.hxx"
|
||||||
// #include "Blog.h"
|
#include "BlogBuilder.hxx"
|
||||||
#include "Common.hxx"
|
#include "Common.hxx"
|
||||||
#include "MarkdownParser.hxx"
|
#include "MarkdownParser.hxx"
|
||||||
#include "Article.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) {
|
void build_dir_structure(std::string const &path) {
|
||||||
// Create directory tree, which will be used for the website.
|
// Create directory tree, which will be used for the website.
|
||||||
std::filesystem::path rootDir = get_output_path(path);
|
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) {
|
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");
|
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') {
|
if (metadata->publish_str[0] == '\0') {
|
||||||
struct tm *publish_tm = gmtime(&(metadata->publish_ts));
|
struct tm *publish_tm = gmtime(&(metadata->publish_ts));
|
||||||
//article_html += ctime(&(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 += " ";
|
||||||
article_html += std::to_string(publish_tm->tm_mday);
|
article_html += std::to_string(publish_tm->tm_mday);
|
||||||
article_html += ", ";
|
article_html += ", ";
|
||||||
|
@ -165,118 +98,6 @@ void compile_markdown(std::string const &path, std::string const &md, std::strin
|
||||||
fclose(articleOutput);
|
fclose(articleOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void build_blog_structure(std::string const &path, std::string const &prefix, std::list<std::string> 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<Article::Metadata*> am;
|
|
||||||
//std::map<std::string, std::list<std::string>> 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 = "<ul>\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<span><b>";
|
|
||||||
blog_html_catalog += hr_date;
|
|
||||||
blog_html_catalog += "</b></span>\n";
|
|
||||||
}
|
|
||||||
blog_html_catalog += "\t<li><a href=\"./";
|
|
||||||
blog_html_catalog += m->path;
|
|
||||||
blog_html_catalog += "\">";
|
|
||||||
blog_html_catalog += m->title;
|
|
||||||
blog_html_catalog += "</a></li>\n";
|
|
||||||
// Free memory as it's no longer needed.
|
|
||||||
free(m);
|
|
||||||
}
|
|
||||||
blog_html_catalog += "</ul>";
|
|
||||||
// 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("<!--\\[_SWG: \\$CONTENT\\]-->");
|
|
||||||
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) {
|
void build_website(SwgContext &ctx, std::string const &path) {
|
||||||
build_dir_structure(path);
|
build_dir_structure(path);
|
||||||
|
|
||||||
|
@ -360,7 +181,7 @@ void build_website(SwgContext &ctx, std::string const &path) {
|
||||||
filename.find(".md") == filename.length() - 3) {
|
filename.find(".md") == filename.length() - 3) {
|
||||||
std::cout << "\t\tIs file: " << getFilename(dir_entry.path()) << "\n";
|
std::cout << "\t\tIs file: " << getFilename(dir_entry.path()) << "\n";
|
||||||
// Markdown files should be insite a YYYY/MM directory.
|
// 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());
|
failedArticles.insert(failedArticles.end(), dir_entry.path());
|
||||||
else parsedArticles.insert(parsedArticles.end(), dir_entry.path());
|
else parsedArticles.insert(parsedArticles.end(), dir_entry.path());
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,10 @@
|
||||||
|
|
||||||
#include "SwgContext.hxx"
|
#include "SwgContext.hxx"
|
||||||
|
|
||||||
void build_website(SwgContext &ctx, std::string const &path);
|
#include "Article.hxx"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue