stamd

Static Markdown Page Generator
git clone git://git.dimitrijedobrota.com/stamd.git
Log | Files | Refs | README | LICENSE

main.cpp (4782B)


      1 #include <filesystem>
      2 #include <fstream>
      3 #include <iostream>
      4 #include <sstream>
      5 
      6 #include <md4c-html.h>
      7 #include <poafloc/poafloc.hpp>
      8 
      9 #include "article.hpp"
     10 #include "indexer.hpp"
     11 #include "utility.hpp"
     12 
     13 void preprocess(stamd::article& article, std::istream& ist)
     14 {
     15   std::string line;
     16   std::string key;
     17   std::string value;
     18 
     19   while (std::getline(ist, line))
     20   {
     21     if (line.empty()) break;
     22     if (line[0] != '@') break;
     23 
     24     {
     25       std::istringstream iss(line.substr(1));
     26       std::getline(iss, key, ':');
     27       std::getline(iss, value);
     28 
     29       trim(key);
     30       trim(value);
     31     }
     32 
     33     if (key == "hidden") article.set_hidden(true);
     34     else if (key == "nonav") article.set_nonav(true);
     35     else if (key != "categories") article.insert(key, value);
     36     else
     37     {
     38       std::istringstream iss(value);
     39       while (std::getline(iss, value, ',')) article.insert(trim(value));
     40     }
     41   }
     42 }
     43 
     44 struct arguments_t
     45 {
     46   std::filesystem::path output_dir = ".";
     47   std::vector<std::filesystem::path> files;
     48   bool index = false;
     49 
     50   stamd::indexer::options_t options;
     51 };
     52 
     53 int parse_opt(int key, const char* arg, poafloc::Parser* parser)
     54 {
     55   auto* args = static_cast<arguments_t*>(parser->input());
     56   switch (key)
     57   {
     58     case 'o':
     59       args->output_dir = arg;
     60       break;
     61     case 'i':
     62       args->index = true;
     63       break;
     64     case 'b':
     65       args->options.base_url = arg;
     66       break;
     67     case 'a':
     68       args->options.author = arg;
     69       break;
     70     case 'e':
     71       args->options.email = arg;
     72       break;
     73     case 'd':
     74       args->options.description = arg;
     75       break;
     76     case 's':
     77       args->options.summary = arg;
     78       break;
     79     case poafloc::ARG:
     80       args->files.emplace_back(arg);
     81       break;
     82     default:
     83       break;
     84   }
     85   return 0;
     86 }
     87 
     88 // NOLINTBEGIN
     89 // clang-format off
     90 static const poafloc::option_t options[] = {
     91     {0, 0, 0, 0, "Output mode", 1},
     92     {"output", 'o', "DIR", 0, "Output directory"},
     93     {"index", 'i', 0, 0, "Generate all of the indices"},
     94     {0, 0, 0, 0, "General information", 2},
     95     {"base", 'b', "URL", 0, "Base URL for the content"},
     96     {"author", 'a', "NAME", 0, "Name of the author, if not specified in article"},
     97     {"email", 'e', "EMAIL", 0, "Email of the author, if not specified in article"},
     98     {"summary", 's', "SMRY", 0, "A summary, if not specified in article"},
     99     {"description", 'd', "DESC", 0, "Description of RSS feed"},
    100     {0, 0, 0, 0, "Informational Options", -1},
    101     {0},
    102 };
    103 // clang-format on
    104 
    105 static const poafloc::arg_t arg {
    106     options,
    107     parse_opt,
    108     "config_file",
    109     "",
    110 };
    111 // NOLINTEND
    112 
    113 void process_output(const MD_CHAR* str, MD_SIZE size, void* data)
    114 {
    115   std::ofstream& ofs = *static_cast<std::ofstream*>(data);
    116   ofs << std::string(str, size);
    117 }
    118 
    119 int main(int argc, char* argv[])
    120 {
    121   using namespace stamd;  // NOLINT
    122 
    123   arguments_t args;
    124 
    125   if (poafloc::parse(&arg, argc, argv, 0, &args) != 0)
    126   {
    127     std::cerr << "There was an error while parsing arguments";
    128     return 1;
    129   }
    130 
    131   using category_map_t =
    132       std::unordered_map<std::string, indexer::article_list>;
    133 
    134   stamd::indexer::categories_t categories;
    135   category_map_t category_map;
    136   indexer indexer(args.options);
    137 
    138   for (const auto& path : args.files)
    139   {
    140     const std::string filename = path.stem().string() + ".html";
    141 
    142     std::ifstream ifs(path.string());
    143     auto& article = indexer.add(make_shared<stamd::article>(filename));
    144 
    145     preprocess(*article, ifs);
    146 
    147     // filename can change in preprocessing phase
    148     std::ofstream ofs(args.output_dir / article->get_filename());
    149     std::stringstream sst;
    150 
    151     sst << ifs.rdbuf();
    152 
    153     article->write_header(ofs);
    154     md_html(sst.str().c_str(),
    155             static_cast<MD_SIZE>(sst.str().size()),
    156             process_output,
    157             &ofs,
    158             MD_DIALECT_GITHUB,
    159             0);
    160     article->write_footer(ofs);
    161 
    162     if (!article->is_hidden())
    163     {
    164       categories.merge(article->get_categories());
    165       for (const auto& category : article->get_categories())
    166         category_map[category].emplace_back(article);
    167     }
    168   }
    169 
    170   if (!args.index) return 0;
    171 
    172   indexer.sort();
    173 
    174   std::ofstream rss(args.output_dir / "rss.xml");
    175   indexer.create_rss(rss, "index");
    176 
    177   std::ofstream atom(args.output_dir / "atom.xml");
    178   indexer.create_atom(atom, "index");
    179 
    180   std::ofstream index(args.output_dir / "index.html");
    181   indexer.create_index(index, "blog index", categories);
    182 
    183   for (const auto& [category, articles] : category_map)
    184   {
    185     auto ctgry = category;
    186     std::ofstream ost(args.output_dir / (normalize(ctgry) + ".html"));
    187     indexer.create_index(ost, category, {});
    188   }
    189 
    190   std::ofstream robots(args.output_dir / "robots.txt");
    191   indexer.create_robots(robots);
    192 
    193   std::ofstream sitemap(args.output_dir / "sitemap.xml");
    194   indexer.create_sitemap(sitemap);
    195 
    196   return 0;
    197 }