swg/src/Article.cxx

140 lines
4.1 KiB
C++

/*
* 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 "Article.hxx"
#include <iostream>
#include <list>
#include <string.h>
#include "Common.hxx"
enum IterationStage {
CHECKING_TITLE,
READING_TITLE,
CHECKING
};
namespace Article {
/*
* FIXME: The article metadata declaration uses the same INI format as
* swg.cfg. Since they use the exact same format, we could unify the .cfg
* parsing into one function.
*/
void get_metadata(std::string const &path, Metadata *m) {
memset(m, 0, sizeof(Metadata));
FILE *a = fopen(path.c_str(), "r");
int buflen = 16;
char buf[buflen];
const char *det_article = "<!--SwgArticle";
size_t det_article_pos = 0;
bool reading_metadata = false,
done = false,
skip = false;
std::list<std::string> cfg_lines = { "" };
while (fgets(buf, buflen, a) != NULL) {
// Ignore blank lines or lines starting with a comment.
if (buf[0] == '\n' || buf[0] == '#') continue;
int eol = -1;
for (int c = 0; c < buflen; c++) {
// Check if this loop has already done what it's supposed to do by checking if the title has been set.
if (done || buf[c] == '\0') break;
if (!reading_metadata) {
// FIXME: This code will not work if buflen < strlen(det_article).
if (buf[c] == det_article[det_article_pos]) {
det_article_pos++;
} else {
// TODO: Instead of exiting with an
// error code, prompt the end user
// whether the build should continue
// (while ignoring this article) or
// stop.
std::cerr << "error: invalid article: " << path << "\n";
exit(RETURN_FAILED_UNKNOWN_ERROR);
}
if (det_article_pos == strlen(det_article)) {
reading_metadata = true;
skip = true;
}
}
// Check end of HTML comment
if (buf[c] == '-' &&
buf[c+1] == '-' &&
buf[c+2] == '>') {
reading_metadata = false;
done = true;
break;
}
if (buf[c] == '\n')
eol = c;
}
if (skip) { skip = false; continue; }
if (done) break;
if (reading_metadata) {
if (eol > -1) {
buf[eol] = '\0';
*(--cfg_lines.end()) += buf;
cfg_lines.insert(cfg_lines.end(), "");
} else
*(--cfg_lines.end()) += buf;
}
}
bool reading_article_metadata = false;
for (std::string const &line : cfg_lines) {
// Check namespace
if (reading_article_metadata) {
std::string k_title = "Title=",
k_authors = "Authors=",
k_published = "Published=";
if (line.find(k_title) == 0)
strncpy(m->title, line.substr(k_title.length()).c_str(), sizeof(m->title));
else if (line.find(k_authors) == 0)
strncpy(m->authors, line.substr(k_authors.length()).c_str(), sizeof(m->authors));
else if (line.find(k_published) == 0)
m->publish_ts = atol(line.substr(k_published.length()).c_str());
else
std::cerr << "warning: ignoring unknown key/value due to unknown key: " << line << std::endl;
} else if (line[0] == '[') {
if (line == "[ArticleMetadata]") {
reading_article_metadata = true;
} else {
std::cerr << "error: article properties has unknown namespace: " << line << std::endl;
exit(RETURN_FAILED_CONFIG_INVALID_SYNTAX);
}
}
}
fclose(a);
}
bool Comparator::comp(Metadata *a, Metadata *b) {
return a->publish_ts > b->publish_ts;
}
bool Comparator::equiv(Metadata *a, Metadata *b) {
return a->publish_ts == b->publish_ts;
}
};