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 }