stamdStatic Markdown Page Generator |
git clone git://git.dimitrijedobrota.com/stamd.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
main.cpp (5398B)
0 #include <filesystem>
1 #include <fstream>
2 #include <iostream>
3 #include <memory>
4 #include <sstream>
5 #include <string>
6 #include <unordered_map>
7 #include <vector>
9 #include <md4c-html.h>
10 #include <poafloc/poafloc.hpp>
12 #include "article.hpp"
13 #include "indexer.hpp"
14 #include "options.hpp"
15 #include "utility.hpp"
17 namespace
18 {
20 void preprocess(stamd::Article& article, std::istream& ist)
21 {
22 std::string line;
23 std::string key;
24 std::string value;
26 while (std::getline(ist, line)) {
27 if (line.empty() && line[0] != '@') {
28 break;
29 }
31 {
32 std::istringstream iss(line.substr(1));
33 std::getline(iss, key, ':');
34 std::getline(iss, value);
36 trim(key);
37 trim(value);
38 }
40 if (key == "hidden") {
41 article.set_hidden();
42 } else if (key == "nonav") {
43 article.set_nonav();
44 } else if (key != "categories") {
45 article.insert(key, value);
46 } else {
47 std::istringstream iss(value);
48 while (std::getline(iss, value, ',')) {
49 article.insert(trim(value));
50 }
51 }
52 }
53 }
55 struct arguments_t
56 {
57 std::filesystem::path output_dir = ".";
58 std::vector<std::filesystem::path> files;
59 bool index = false;
61 stamd::options_t options;
62 };
64 int parse_opt(int key, const char* arg, poafloc::Parser* parser)
65 {
66 auto* args = static_cast<arguments_t*>(parser->input());
67 switch (key) {
68 case 'o':
69 args->output_dir = arg;
70 break;
71 case 'i':
72 args->index = true;
73 break;
74 case 'b':
75 args->options.base_url = arg;
76 break;
77 case 'a':
78 args->options.author = arg;
79 break;
80 case 'e':
81 args->options.email = arg;
82 break;
83 case 'd':
84 args->options.description = arg;
85 break;
86 case 's':
87 args->options.summary = arg;
88 break;
89 case poafloc::ARG:
90 args->files.emplace_back(arg);
91 break;
92 case poafloc::END:
93 if (args->options.base_url.empty()
94 || args->options.base_url.back() != '/')
95 {
96 args->options.base_url += '/';
97 }
98 break;
99 default:
100 break;
101 }
102 return 0;
103 }
105 // NOLINTBEGIN
106 // clang-format off
107 static const poafloc::option_t options[] = {
108 {0, 0, 0, 0, "Output mode", 1},
109 {"output", 'o', "DIR", 0, "Output directory"},
110 {"index", 'i', 0, 0, "Generate all of the indices"},
111 {0, 0, 0, 0, "General information", 2},
112 {"base", 'b', "URL", 0, "Base URL for the content"},
113 {"author", 'a', "NAME", 0, "Name of the author, if not specified in article"},
114 {"email", 'e', "EMAIL", 0, "Email of the author, if not specified in article"},
115 {"summary", 's', "SMRY", 0, "A summary, if not specified in article"},
116 {"description", 'd', "DESC", 0, "Description of RSS feed"},
117 {0, 0, 0, 0, "Informational Options", -1},
118 {0},
119 };
120 // clang-format on
122 static const poafloc::arg_t arg {
123 options,
124 parse_opt,
125 "config_file",
126 "",
127 };
128 // NOLINTEND
130 } // namespace
132 int main(int argc, char* argv[])
133 {
134 using namespace stamd; // NOLINT
136 arguments_t args;
138 if (poafloc::parse(&arg, argc, argv, 0, &args) != 0) {
139 std::cerr << "There was an error while parsing arguments";
140 return 1;
141 }
143 using category_map_t = std::unordered_map<std::string, Indexer>;
145 category_map_t category_map;
146 Indexer index(args.options);
148 for (const auto& path : args.files) {
149 const std::string filename = path.stem().string() + ".html";
151 const auto article = make_shared<stamd::Article>(filename, args.options);
152 index.add(article);
154 std::ifstream ifs(path.string());
155 preprocess(*article, ifs);
157 std::stringstream sst;
158 sst << ifs.rdbuf();
160 // filename can change in preprocessing phase
161 std::ofstream ofs(args.output_dir / article->get_filename());
163 ofs << article->write(
164 [&]() -> hemplate::element
165 {
166 std::string html;
168 static auto process_output =
169 [](const MD_CHAR* str, MD_SIZE size, void* data)
170 {
171 std::string& buffer = *static_cast<std::string*>(data);
172 buffer += std::string(str, size);
173 };
175 md_html(
176 sst.str().c_str(),
177 static_cast<MD_SIZE>(sst.str().size()),
178 process_output,
179 &html,
180 MD_DIALECT_GITHUB,
181 0
182 );
183 return html;
184 }
185 );
187 if (article->is_hidden()) {
188 continue;
189 }
191 index.add(article->get_categories());
192 for (const auto& category : article->get_categories()) {
193 auto [it, _] = category_map.emplace(category, args.options);
194 it->second.add(article);
195 }
196 }
198 if (!args.index) {
199 return 0;
200 }
202 index.sort();
204 std::ofstream ofs_rss(args.output_dir / "rss.xml");
205 index.create_rss(ofs_rss, "index");
207 std::ofstream ofs_atom(args.output_dir / "atom.xml");
208 index.create_atom(ofs_atom, "index");
210 std::ofstream ofs_index(args.output_dir / "index.html");
211 index.create_index(ofs_index, "blog");
213 for (auto& [category_name, category_index] : category_map) {
214 auto ctgry = category_name;
215 std::ofstream ost(args.output_dir / (normalize(ctgry) + ".html"));
217 category_index.sort();
218 category_index.create_index(ost, category_name);
219 }
221 std::ofstream ofs_robots(args.output_dir / "robots.txt");
222 index.create_robots(ofs_robots);
224 std::ofstream ofs_sitemap(args.output_dir / "sitemap.xml");
225 index.create_sitemap(ofs_sitemap);
227 return 0;
228 }