246 sor
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			246 sor
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (c) 2018 Albert S.
 | |
| 
 | |
| Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| of this software and associated documentation files (the "Software"), to deal
 | |
| in the Software without restriction, including without limitation the rights
 | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| copies of the Software, and to permit persons to whom the Software is
 | |
| furnished to do so, subject to the following conditions:
 | |
| 
 | |
| The above copyright notice and this permission notice shall be included in all
 | |
| copies or substantial portions of the Software.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
| SOFTWARE.
 | |
| */
 | |
| #include <regex>
 | |
| #include <iostream>
 | |
| #include <regex>
 | |
| #include <vector>
 | |
| #include <algorithm>
 | |
| #include <iterator>
 | |
| #include "parser.h"
 | |
| #include "utils.h"
 | |
| #include "htmllink.h"
 | |
| std::vector<Headline> Parser::extractHeadlines(const std::string &content) const
 | |
| {
 | |
| 	std::vector<Headline> result;
 | |
| 
 | |
| 	std::string reg = R"(\[h(1|2|3)\](\[.*?\])*(.*?)(\[.*?\])*\[\/h\1\])";
 | |
| 	std::regex headerfinder(reg);
 | |
| 	auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder);
 | |
| 	auto end = std::sregex_iterator();
 | |
| 
 | |
| 	for(auto it = begin; it != end; it++)
 | |
| 	{
 | |
| 		auto smatch = *it;
 | |
| 		Headline h;
 | |
| 		h.level = utils::toUInt(smatch.str(1));
 | |
| 		h.title = smatch.str(3);
 | |
| 		result.push_back(h);
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| std::vector<std::string> Parser::extractCategories(const std::string &content) const
 | |
| {
 | |
| 	std::vector<std::string> result;
 | |
| 	std::string reg = R"(\[category\](.*?)\[/category\])";
 | |
| 	std::regex headerfinder(reg);
 | |
| 	auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder);
 | |
| 	auto end = std::sregex_iterator();
 | |
| 
 | |
| 	for(auto it = begin; it != end; it++)
 | |
| 	{
 | |
| 		auto smatch = *it;
 | |
| 		result.emplace_back(smatch.str(1));
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| std::string Parser::extractFirstTag(std::string tagname, const std::string &content) const
 | |
| {
 | |
| 	std::string cmd = "[" + tagname + "]";
 | |
| 	std::string cmdend = "[/" + tagname + "]";
 | |
| 	std::string_view view = content;
 | |
| 	size_t pos = 0;
 | |
| 	if((pos = view.find(cmd)) != std::string::npos)
 | |
| 	{
 | |
| 		view.remove_prefix(pos);
 | |
| 		view.remove_prefix(cmd.size());
 | |
| 		if((pos = view.find(cmdend)) != std::string::npos)
 | |
| 		{
 | |
| 			auto result = view.substr(0, pos);
 | |
| 			return std::string{result};
 | |
| 		}
 | |
| 	}
 | |
| 	return "";
 | |
| }
 | |
| 
 | |
| std::string Parser::extractCommand(std::string cmdname, const std::string &content) const
 | |
| {
 | |
| 
 | |
| 	return extractFirstTag("cmd:" + cmdname, content);
 | |
| }
 | |
| 
 | |
| std::vector<std::string> Parser::extractCommands(std::string cmdname, const std::string &content) const
 | |
| {
 | |
| 	std::vector<std::string> result;
 | |
| 
 | |
| 	std::string cmd = "[cmd:" + cmdname + "]";
 | |
| 	std::string cmdend = "[/cmd:" + cmdname + "]";
 | |
| 
 | |
| 	std::string_view view = content;
 | |
| 	size_t pos = 0;
 | |
| 	while((pos = view.find(cmd)) != std::string::npos)
 | |
| 	{
 | |
| 		view.remove_prefix(pos);
 | |
| 		view.remove_prefix(cmd.size());
 | |
| 		if((pos = view.find(cmdend)) != std::string::npos)
 | |
| 		{
 | |
| 			result.emplace_back(view.substr(0, pos));
 | |
| 		}
 | |
| 	}
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| std::string Parser::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
 | |
| {
 | |
| 	std::string linktag = match.str(1);
 | |
| 	std::string inside = match.str(2);
 | |
| 
 | |
| 	std::vector<std::string> splitted = utils::split(inside, '|');
 | |
| 	HtmlLink htmllink;
 | |
| 	if(splitted.size() == 2)
 | |
| 	{
 | |
| 		htmllink.innervalue = splitted[1];
 | |
| 		htmllink.href = splitted[0];
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		htmllink.innervalue = inside;
 | |
| 		htmllink.href = inside;
 | |
| 	}
 | |
| 
 | |
| 	if(linktag == "wikilink")
 | |
| 	{
 | |
| 		if(pageDao.exists(htmllink.href))
 | |
| 		{
 | |
| 			htmllink.cssclass = "exists";
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			htmllink.cssclass = "notexists";
 | |
| 		}
 | |
| 
 | |
| 		htmllink.href = urlProvider.page(htmllink.href);
 | |
| 	}
 | |
| 
 | |
| 	return htmllink.render();
 | |
| }
 | |
| 
 | |
| std::string Parser::processImage(std::smatch &match) const
 | |
| {
 | |
| 	std::string tag = match.str(1);
 | |
| 	std::string inside = match.str(2);
 | |
| 	std::vector<std::string> splitted = utils::split(inside, '|');
 | |
| 	std::string width;
 | |
| 	std::string height;
 | |
| 	std::string src;
 | |
| 	if(splitted.size() == 3)
 | |
| 	{
 | |
| 		width = splitted[0];
 | |
| 		height = splitted[1];
 | |
| 		src = splitted[2];
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		src = splitted[0];
 | |
| 	}
 | |
| 	if(!width.empty() && !height.empty())
 | |
| 	{
 | |
| 		return "<img src=\"" + src + "\" width=\"" + width + "\" height=\"" + height + "\"/>";
 | |
| 	}
 | |
| 	return "<img src=\"" + src + "\"/>";
 | |
| }
 | |
| 
 | |
| std::string Parser::parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content,
 | |
| 						  const std::function<std::string(std::string_view, std::string_view)> &callback) const
 | |
| {
 | |
| 	std::string result;
 | |
| 	// we don't care about commands, but we nevertheless replace them with empty strings
 | |
| 	std::regex tagfinder(
 | |
| 		R"(\[(b|i|u|s|li|p|br|ul|ol|code|blockquote|img|link|wikilink|h\d|cmd:visible|cmd:listed|cmd:feedlisted|cmd:rename|cmd:redirect|cmd:pagetitle|cmd:allowinclude|cmd:permissions|cmd:parentpage|content|category|dynamic:postlist|dynamic:includepage|dynamic:getvar|dynamic:setvar)*?\]((\s|\S)*?)\[/\1](\r\n)*)");
 | |
| 
 | |
| 	const std::string justreplace[] = {"b", "i", "u", "p", "br", "ul", "li", "ol"};
 | |
| 
 | |
| 	result = utils::regex_callback_replacer(
 | |
| 		tagfinder, content,
 | |
| 		[&](std::smatch &match)
 | |
| 		{
 | |
| 			std::string tag = match.str(1);
 | |
| 			std::string content = match.str(2);
 | |
| 
 | |
| 			std::string newlines = match.str(4);
 | |
| 			if(newlines == "\r\n")
 | |
| 			{
 | |
| 				newlines = "<br>";
 | |
| 			}
 | |
| 			if(tag == "br")
 | |
| 			{
 | |
| 				return std::string("<br>");
 | |
| 			}
 | |
| 			if(tag != "code" && tag != "blockquote")
 | |
| 			{
 | |
| 				content = parse(pagedao, provider, content, callback);
 | |
| 			}
 | |
| 			/* [content] just helps extracting the actual content of a page, pretty much noop otherwise */
 | |
| 			if(tag == "content")
 | |
| 			{
 | |
| 				return parse(pagedao, provider, content, callback);
 | |
| 			}
 | |
| 			if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace))
 | |
| 			{
 | |
| 				if(tag == "p" || tag == "br")
 | |
| 				{
 | |
| 					newlines = "";
 | |
| 				}
 | |
| 				return "<" + tag + ">" + content + "</" + tag + ">" + newlines;
 | |
| 			}
 | |
| 			if(tag == "link" || tag == "wikilink")
 | |
| 			{
 | |
| 				return this->processLink(pagedao, provider,
 | |
| 										 match) +
 | |
| 					   newlines; // TODO: recreate this so we don't check inside the function stuff again
 | |
| 			}
 | |
| 			if(tag == "img")
 | |
| 			{
 | |
| 				return this->processImage(match);
 | |
| 			}
 | |
| 			if(tag[0] == 'h')
 | |
| 			{
 | |
| 				return "<" + tag + " id='" + utils::strreplace(content, " ", "") + "'>" + content + "</" + tag + ">";
 | |
| 			}
 | |
| 			if(tag == "code" || tag == "blockquote")
 | |
| 			{
 | |
| 				return "<pre><" + tag + ">" + utils::strreplace(content, "\r\n", "\n") + "</" + tag + "></pre>";
 | |
| 			}
 | |
| 			return callback(tag, content);
 | |
| 		});
 | |
| 	result = utils::strreplace(result, "\r\n", "<br>");
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| std::string Parser::parseDynamics(const std::string &content,
 | |
| 								  const std::function<std::string(std::string_view, std::string_view)> &callback) const
 | |
| {
 | |
| 	std::regex tagfinder(R"(\[(dynamic:\w+)*?\]((\s|\S)*?)\[/\1])");
 | |
| 	return utils::regex_callback_replacer(tagfinder, content,
 | |
| 										  [&](std::smatch &match) { return callback(match.str(1), match.str(2)); });
 | |
| }
 |