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