<commit_before>#include "minilzo209/minilzo.h"

#include <node.h>
#include <v8.h>
#include <sstream>

#include <node_buffer.h>

using namespace v8;

int compress(const unsigned char *input, unsigned char *output, lzo_uint in_len, lzo_uint& out_len) {
    char* wrkmem = (char*) malloc(LZO1X_1_MEM_COMPRESS);

    int result = lzo1x_1_compress(input, in_len, output, &out_len, wrkmem);

    free(wrkmem);

    return result;
}

lzo_uint decompress(const unsigned char *input, unsigned char *output, lzo_uint in_len, lzo_uint& out_len) {
    int r = lzo1x_decompress_safe(input, in_len, output, &out_len, NULL);

    if (r == LZO_E_OK)
        return out_len;
    else
        return r;
}

void js_compress(const v8::FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    HandleScope scope(isolate);

    Handle<Object> inputBuffer = args[0]->ToObject();
    Handle<Object> outputBuffer = args[1]->ToObject();

    lzo_uint input_len = node::Buffer::Length(inputBuffer);
    lzo_uint output_len = node::Buffer::Length(outputBuffer);

    int result = compress(  (unsigned char *) node::Buffer::Data(inputBuffer),
                            (unsigned char *) node::Buffer::Data(outputBuffer),
                            input_len,
                            output_len );

    Local<Object> ret = Object::New(isolate);

    ret->Set(String::NewFromUtf8(isolate, "err"),
        Number::New(isolate, result));

    ret->Set(String::NewFromUtf8(isolate, "len"),
        Number::New(isolate, (int) output_len) );

    args.GetReturnValue().Set(ret);
}

void js_decompress(const v8::FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    HandleScope scope(isolate);

    Handle<Object> inputBuffer = args[0]->ToObject();
    Handle<Object> outputBuffer = args[1]->ToObject();

    lzo_uint input_len = node::Buffer::Length(inputBuffer);
    lzo_uint output_len = node::Buffer::Length(outputBuffer);

    lzo_uint len = decompress(  (unsigned char *) node::Buffer::Data(inputBuffer),
                                (unsigned char *) node::Buffer::Data(outputBuffer),
                                input_len,
                                output_len);

    int err = (int) len < 0 ? (int) len : 0;

    Local<Object> ret = Object::New(isolate);

    ret->Set(String::NewFromUtf8(isolate, "err"),
        Number::New(isolate, err));

    ret->Set(String::NewFromUtf8(isolate, "len"),
        Number::New(isolate, (int) len) );

    args.GetReturnValue().Set(ret);
}

void Init(Local<Object> exports, Local<Context> context) {
    Isolate* isolate = context->GetIsolate();

    int init_result = lzo_init();

    if(init_result != LZO_E_OK) {
        std::stringstream ss;

        ss << "lzo_init() failed and returned `" << init_result << "`. ";
        ss << "Please report this on GitHub: https://github.com/schroffl/node-lzo/issues";

        Local<String> err = String::NewFromUtf8(isolate, ss.str().c_str());

        isolate->ThrowException(Exception::Error(err));

        return;
    }

    // Compression
    exports->Set(String::NewFromUtf8(isolate, "compress"),
        FunctionTemplate::New(isolate, js_compress)->GetFunction());

    // Decompression
    exports->Set(String::NewFromUtf8(isolate, "decompress"),
        FunctionTemplate::New(isolate, js_decompress)->GetFunction());

    // Current lzo version
    exports->Set(String::NewFromUtf8(isolate, "version"),
        String::NewFromUtf8(isolate, lzo_version_string()));

    // Date for current lzo version
    exports->Set(String::NewFromUtf8(isolate, "versionDate"),
        String::NewFromUtf8(isolate, lzo_version_date()));
}

#if NODE_MAJOR_VERSION >= 10 && NODE_MINOR_VERSION >= 7
  // Initialize this addon to be context-aware. See Issue #11
  NODE_MODULE_INIT(/* exports, module, context */) {
      Init(exports, context);
  }
#else
  // For backwards compatibility. See Issue #13
  NODE_MODULE(node_lzo, Init)
#endif
<commit_msg>Fix #14 - Allows the library to be built with Node v12.x<commit_after>#include "minilzo209/minilzo.h"

#include <node.h>
#include <v8.h>
#include <sstream>

#include <node_buffer.h>

#define GET_FUNCTION(A, B, C) FunctionTemplate::New(A, B)->GetFunction()
#define MAKE_STRING(A, B) String::NewFromUtf8(A, B)

#if NODE_MAJOR_VERSION >= 12
#define NODE_12

#undef GET_FUNCTION
#define GET_FUNCTION(A, B, C) FunctionTemplate::New(A, B)->GetFunction(C).ToLocalChecked()

#undef MAKE_STRING
#define MAKE_STRING(A, B) String::NewFromUtf8(A, B, NewStringType::kNormal).ToLocalChecked()
#endif

#define NEW_API (NODE_MAJOR_VERSION >= 10)

using namespace v8;

int compress(const unsigned char *input, unsigned char *output, lzo_uint in_len, lzo_uint& out_len) {
    char* wrkmem = (char*) malloc(LZO1X_1_MEM_COMPRESS);

    int result = lzo1x_1_compress(input, in_len, output, &out_len, wrkmem);

    free(wrkmem);

    return result;
}

lzo_uint decompress(const unsigned char *input, unsigned char *output, lzo_uint in_len, lzo_uint& out_len) {
    int r = lzo1x_decompress_safe(input, in_len, output, &out_len, NULL);

    if (r == LZO_E_OK)
        return out_len;
    else
        return r;
}

void js_compress(const v8::FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    HandleScope scope(isolate);

#if NEW_API
    Local<Context> context = isolate->GetCurrentContext();
    Local<Object> inputBuffer = args[0]->ToObject(context).ToLocalChecked();
    Local<Object> outputBuffer = args[1]->ToObject(context).ToLocalChecked();
#else
    Handle<Object> inputBuffer = args[0]->ToObject();
    Handle<Object> outputBuffer = args[1]->ToObject();
#endif

    lzo_uint input_len = node::Buffer::Length(inputBuffer);
    lzo_uint output_len = node::Buffer::Length(outputBuffer);

    int result = compress(  (unsigned char *) node::Buffer::Data(inputBuffer),
                            (unsigned char *) node::Buffer::Data(outputBuffer),
                            input_len,
                            output_len );

    Local<Object> ret = Object::New(isolate);

#ifdef NODE_12
    (void) ret->Set(context, MAKE_STRING(isolate, "err"), Number::New(isolate, result));
    (void) ret->Set(context, MAKE_STRING(isolate, "len"), Number::New(isolate, (int) output_len));
#else
    ret->Set(MAKE_STRING(isolate, "err"), Number::New(isolate, result));
    ret->Set(MAKE_STRING(isolate, "len"), Number::New(isolate, (int) output_len));
#endif

    args.GetReturnValue().Set(ret);
}

void js_decompress(const v8::FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    HandleScope scope(isolate);

#if NEW_API
    Local<Context> context = isolate->GetCurrentContext();
    Local<Object> inputBuffer = args[0]->ToObject(context).ToLocalChecked();
    Local<Object> outputBuffer = args[1]->ToObject(context).ToLocalChecked();
#else
    Handle<Object> inputBuffer = args[0]->ToObject();
    Handle<Object> outputBuffer = args[1]->ToObject();
#endif

    lzo_uint input_len = node::Buffer::Length(inputBuffer);
    lzo_uint output_len = node::Buffer::Length(outputBuffer);

    lzo_uint len = decompress(  (unsigned char *) node::Buffer::Data(inputBuffer),
                                (unsigned char *) node::Buffer::Data(outputBuffer),
                                input_len,
                                output_len);

    int err = (int) len < 0 ? (int) len : 0;

    Local<Object> ret = Object::New(isolate);

#ifdef NODE_12
    (void) ret->Set(context, MAKE_STRING(isolate, "err"), Number::New(isolate, err));
    (void) ret->Set(context, MAKE_STRING(isolate, "len"), Number::New(isolate, (int) len) );
#else
    ret->Set(MAKE_STRING(isolate, "err"), Number::New(isolate, err));
    ret->Set(MAKE_STRING(isolate, "len"), Number::New(isolate, (int) len) );
#endif

    args.GetReturnValue().Set(ret);
}

void Init(Local<Object> exports, Local<Context> context) {
    Isolate* isolate = context->GetIsolate();

    int init_result = lzo_init();

    if(init_result != LZO_E_OK) {
        std::stringstream ss;

        ss << "lzo_init() failed and returned `" << init_result << "`. ";
        ss << "Please report this on GitHub: https://github.com/schroffl/node-lzo/issues";

        Local<String> err = MAKE_STRING(isolate, ss.str().c_str());

        isolate->ThrowException(Exception::Error(err));

        return;
    }

    // Compression
    (void) exports->Set(
        context,
        MAKE_STRING(isolate, "compress"),
        GET_FUNCTION(isolate, js_compress, context)
    );

    // Decompression
    (void) exports->Set(
        context,
        MAKE_STRING(isolate, "decompress"),
        GET_FUNCTION(isolate, js_decompress, context)
    );

    // Current lzo version
    const char *version = lzo_version_string();
    (void) exports->Set(
        context,
        MAKE_STRING(isolate, "version"),
        MAKE_STRING(isolate, version)
    );

    // Date for current lzo version
    const char *date = lzo_version_date();
    (void) exports->Set(
        context,
        MAKE_STRING(isolate, "versionDate"),
        MAKE_STRING(isolate, date)
    );
}

#if (NODE_MAJOR_VERSION >= 10 && NODE_MINOR_VERSION >= 7) || NODE_MAJOR_VERSION >= 11
  // Initialize this addon to be context-aware. See Issue #11
  NODE_MODULE_INIT(/* exports, module, context */) {
      Init(exports, context);
  }
#else
  // For backwards compatibility. See Issue #13
  NODE_MODULE(node_lzo, Init)
#endif
<|endoftext|>


<commit_before>#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdlib.h>
#include <string>

#include "number.h"

namespace rtl {

std::string os$getenv(const std::string &name)
{
    return getenv(name.c_str());
}

Number os$system(const std::string &command)
{
    return number_from_sint32(system(command.c_str()));
}

}
<commit_msg>Change slashes to backslashes in os.system() on Windows<commit_after>#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <ctype.h>
#include <iso646.h>
#include <stdlib.h>
#include <string>

#include "number.h"

namespace rtl {

std::string os$getenv(const std::string &name)
{
    return getenv(name.c_str());
}

Number os$system(const std::string &command)
{
    std::string cmd = command;
#ifdef _WIN32
    // Terrible hack to change slashes to backslashes so cmd.exe isn't confused.
    // Probably better handled by calling a lower level function than system().
    for (std::string::iterator i = cmd.begin(); not isspace(*i); ++i) {
        if (*i == '/') {
            *i = '\\';
        }
    }
#endif
    return number_from_sint32(system(cmd.c_str()));
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 midnightBITS
 *
 * 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 "pch.h"
#include <sstream>
#include <wiki.hpp>
#include <filesystem.hpp>
#include "wiki_parser.hpp"
#include "wiki_nodes.hpp"
#include <iomanip>
#include <fcntl.h>

#ifdef _WIN32
#include <process.h>
#include <io.h>
#else
#include <unistd.h>
#define _getpid getpid
#endif

namespace wiki
{
	struct compiler
	{
		using Token = parser::Token;
		using Tokens = parser::line::Tokens;
		using Block = parser::Parser::Block;
		using Blocks = parser::Parser::Text;

		struct by_token
		{
			TOKEN end_tok;
			by_token(TOKEN end_tok)
				: end_tok(end_tok)
			{}

			bool reached(Tokens::const_iterator cur)
			{
				return cur->type == end_tok;
			}
		};

		struct by_argument
		{
			TOKEN end_tok;
			std::string arg;
			by_argument(TOKEN end_tok, std::string&& arg)
				: end_tok(end_tok)
				, arg(std::move(arg))
			{}

			bool reached(Tokens::const_iterator cur)
			{
				return cur->type == end_tok && arg == cur->arg.str();
			}
		};

		template <typename Final, typename Right = by_token>
		struct scanner: Right
		{
			Tokens::const_iterator from, to;

			template <typename... Args>
			scanner(Tokens::const_iterator from, Tokens::const_iterator to, Args&&... args)
				: Right(std::forward<Args>(args)...)
				, from(from)
				, to(to)
			{}

			Tokens::const_iterator scan()
			{
				Nodes children;

				++from;
				from = compile(children, from, to, Right::end_tok, false);
				if (from != to && Right::reached(from))
					static_cast<Final*>(this)->visit(children);

				return from;
			}
		};

		template <typename Final>
		using arg_scanner = scanner<Final, by_argument>;

		struct items
		{
			Nodes& m_items;

			items(Nodes& _items) : m_items(_items) {}
		};

		struct variable : scanner<variable>, items
		{
			variable(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to) : scanner<variable>(from, to, TOKEN::VAR_E), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Variable>(m_items, text(children));
			}
		};

		struct link : scanner<link>, items
		{
			link(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to) : scanner<link>(from, to, TOKEN::HREF_E), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Link>(m_items, children)->normalize();
			}
		};

		struct element : arg_scanner<element>, items
		{
			element(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to, std::string&& arg) : arg_scanner<element>(from, to, TOKEN::TAG_E, std::move(arg)), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Element>(m_items, arg, children);
			}
		};

		static inline Nodes compile(const Tokens& tokens, bool pre);
		static inline Tokens::const_iterator compile(Nodes& children, Tokens::const_iterator begin, Tokens::const_iterator end, TOKEN endtok, bool pre);
		static inline NodePtr compile(const Block& block, bool pre);
		static inline void compile(Nodes& children, const Blocks& blocks, bool pre);
		static inline std::string text(const Nodes& nodes);
		template <typename Class, typename... Args>
		static inline std::shared_ptr<Class> make_node(Nodes& items, Args&&... args)
		{
			auto elem = std::make_shared<Class>(std::forward<Args>(args)...);
			items.push_back(elem);
			return elem;
		}
		template <typename Class, typename... Args>
		static inline std::shared_ptr<Class> make_block(Args&&... args)
		{
			return std::make_shared<Class>(std::forward<Args>(args)...);
		}
	};

	class Document : public document
	{
		Nodes m_children;
	public:
		Document(const Nodes& children) : m_children(children) {}
		void text(stream& o, const variables_t& vars, list_ctx& ctx) const override
		{
			for (auto& child : m_children)
				child->text(o, vars, ctx);
		}

		void markup(stream& o, const variables_t& vars, const styler_ptr& styler, list_ctx& ctx) const override
		{
			styler->begin_document(o);
			for (auto& child : m_children)
				child->markup(o, vars, styler, ctx);
			styler->end_document(o);
		}

		void debug(stream& o) const override
		{
			for (auto& child : m_children)
				child->debug(o);
		}

		void store(const filesystem::path& obj)
		{
			binary::Writer wr;
			if (!wr.open(obj))
				return;

			if (!wr.store(m_children) || !wr.close())
				filesystem::remove(obj);
		}
	};

	/* public: */

	void cstream::write(const char* buffer, size_t size)
	{
		ref.write(buffer, size);
	}

	document_ptr compile(const filesystem::path& file, const filesystem::path& obj)
	{
		filesystem::status st{ file };
		if (!st.exists())
			return nullptr;

		if (st.mtime() <= filesystem::status{ obj }.mtime())
		{
			binary::Reader r;
			if (r.open(obj))
			{
				Nodes children;
				if (r.load(children))
					return std::make_shared<Document>(children);
			}
		}

		size_t size = (size_t)st.file_size();

		std::string text;
		text.resize(size + 1);
		if (text.size() <= size)
			return nullptr;

		auto f = fopen(file.native().c_str(), "r");
		if (!f)
			return nullptr;
		fread(&text[0], 1, size, f);
		fclose(f);

		text[size] = 0;

		document_ptr out = compile(text);
		std::static_pointer_cast<Document>(out)->store(obj);
		return out;
	}

	document_ptr compile(const filesystem::path& file)
	{
		filesystem::status st{ file };
		if (!st.exists())
			return nullptr;

		size_t size = (size_t)st.file_size();

		std::string text;
		text.resize(size + 1);
		if (text.size() <= size)
			return nullptr;

		auto f = fopen(file.native().c_str(), "r");
		if (!f)
			return nullptr;
		fread(&text[0], 1, size, f);
		fclose(f);

		text[size] = 0;
		return compile(text);
	}

	document_ptr compile(const std::string& text)
	{
		auto blocks = parser::Parser{}.parse(text);

		Nodes children;
		compiler::compile(children, blocks, false);
		return std::make_shared<Document>(children);
	}

	/* private: */
	Nodes compiler::compile(const parser::line::Tokens& tokens, bool pre)
	{
		Nodes list;
		compile(list, tokens.begin(), tokens.end(), TOKEN::BAD, pre);
		return list;
	}

	compiler::Tokens::const_iterator compiler::compile(Nodes& items, Tokens::const_iterator begin, Tokens::const_iterator end, TOKEN endtok, bool pre)
	{
		std::shared_ptr<inline_elem::Text> text;
		std::shared_ptr<Node> elem;

		auto cur = begin;
		for (; cur != end; ++cur)
		{
			if (cur->type == endtok)
				return cur;

			if (cur->type != TOKEN::TEXT)
				text = nullptr;

			Nodes children;

			auto&& token = *cur;
			switch (token.type)
			{
			case TOKEN::TEXT:
				if (text)
					text->append(token.arg.begin(), token.arg.end());
				else
					text = make_node<inline_elem::Text>(items, token.arg.str());

				break;

			case TOKEN::BREAK:
				make_node<inline_elem::Break>(items);
				break;

			case TOKEN::LINE:
				make_node<inline_elem::Line>(items);
				break;

			case TOKEN::TAG_S:
				cur = element{ items, cur, end, token.arg.str() }.scan();
				break;

			case TOKEN::VAR_S:
				cur = variable{ items, cur, end }.scan();
				break;

			case TOKEN::HREF_S:
				cur = link{ items, cur, end }.scan();
				break;

			case TOKEN::HREF_NS:
			case TOKEN::HREF_SEG:
				make_node<inline_elem::Token>(items, token.type);
				break;
			}

			if (cur == end)
				break;
		}

		return cur;
	}

	void compiler::compile(Nodes& children, const parser::Parser::Text& blocks, bool pre)
	{
		for (auto&& block : blocks)
		{
			auto child = compile(block, pre);
			if (child)
				children.push_back(child);
		}
	}

	NodePtr compiler::compile(const parser::Parser::Block& block, bool pre)
	{
		using BLOCK = parser::Parser::BLOCK;

		if (!pre)
			pre = block.type == BLOCK::PRE;
		auto children = compile(block.tokens, pre);
		compile(children, block.items, pre);

		switch (block.type)
		{
		case BLOCK::HEADER:    return make_block<block_elem::Header>(block.iArg, children);
		case BLOCK::PARA:      return make_block<block_elem::Para>(children);
		case BLOCK::QUOTE:     return make_block<block_elem::Quote>(children);
		case BLOCK::PRE:       return make_block<block_elem::Pre>(children);
		case BLOCK::UL:        return make_block<block_elem::UList>(children);
		case BLOCK::OL:        return make_block<block_elem::OList>(children);
		case BLOCK::ITEM:      return make_block<block_elem::Item>(children);
		case BLOCK::HR:        return make_block<block_elem::HR>();
		case BLOCK::SIGNATURE: return make_block<block_elem::Signature>(children);
		default:
			break;
		}

		return nullptr;
	}

	std::string compiler::text(const Nodes& nodes)
	{
		std::ostringstream o;
		cstream co{ o };
		variables_t vars;
		list_ctx ctx;
		for (auto&& node : nodes)
			node->text(co, vars, ctx);
		return o.str();
	}
}
<commit_msg>COmpilation issue with GCC high optimalisation<commit_after>/*
 * Copyright (C) 2013 midnightBITS
 *
 * 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 "pch.h"
#include <sstream>
#include <wiki.hpp>
#include <filesystem.hpp>
#include "wiki_parser.hpp"
#include "wiki_nodes.hpp"
#include <iomanip>
#include <fcntl.h>

#ifdef _WIN32
#include <process.h>
#include <io.h>
#else
#include <unistd.h>
#define _getpid getpid
#endif

namespace wiki
{
	struct compiler
	{
		using Token = parser::Token;
		using Tokens = parser::line::Tokens;
		using Block = parser::Parser::Block;
		using Blocks = parser::Parser::Text;

		struct by_token
		{
			TOKEN end_tok;
			by_token(TOKEN end_tok)
				: end_tok(end_tok)
			{}

			bool reached(Tokens::const_iterator cur)
			{
				return cur->type == end_tok;
			}
		};

		struct by_argument
		{
			TOKEN end_tok;
			std::string arg;
			by_argument(TOKEN end_tok, std::string&& arg)
				: end_tok(end_tok)
				, arg(std::move(arg))
			{}

			bool reached(Tokens::const_iterator cur)
			{
				return cur->type == end_tok && arg == cur->arg.str();
			}
		};

		template <typename Final, typename Right = by_token>
		struct scanner: Right
		{
			Tokens::const_iterator from, to;

			template <typename... Args>
			scanner(Tokens::const_iterator from, Tokens::const_iterator to, Args&&... args)
				: Right(std::forward<Args>(args)...)
				, from(from)
				, to(to)
			{}

			Tokens::const_iterator scan()
			{
				Nodes children;

				++from;
				from = compile(children, from, to, Right::end_tok, false);
				if (from != to && Right::reached(from))
					static_cast<Final*>(this)->visit(children);

				return from;
			}
		};

		template <typename Final>
		using arg_scanner = scanner<Final, by_argument>;

		struct items
		{
			Nodes& m_items;

			items(Nodes& _items) : m_items(_items) {}
		};

		struct variable : scanner<variable>, items
		{
			variable(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to) : scanner<variable>(from, to, TOKEN::VAR_E), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Variable>(m_items, text(children));
			}
		};

		struct link : scanner<link>, items
		{
			link(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to) : scanner<link>(from, to, TOKEN::HREF_E), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Link>(m_items, children)->normalize();
			}
		};

		struct element : arg_scanner<element>, items
		{
			element(Nodes& _items, Tokens::const_iterator from, Tokens::const_iterator to, std::string&& arg) : arg_scanner<element>(from, to, TOKEN::TAG_E, std::move(arg)), items(_items) {}
			void visit(Nodes& children)
			{
				make_node<inline_elem::Element>(m_items, arg, children);
			}
		};

		static inline Nodes compile(const Tokens& tokens, bool pre);
		static inline Tokens::const_iterator compile(Nodes& children, Tokens::const_iterator begin, Tokens::const_iterator end, TOKEN endtok, bool pre);
		static inline NodePtr compile(const Block& block, bool pre);
		static inline void compile(Nodes& children, const Blocks& blocks, bool pre);
		static inline std::string text(const Nodes& nodes);
		template <typename Class, typename... Args>
		static inline std::shared_ptr<Class> make_node(Nodes& items, Args&&... args)
		{
			auto elem = std::make_shared<Class>(std::forward<Args>(args)...);
			items.push_back(elem);
			return elem;
		}
		template <typename Class, typename... Args>
		static inline std::shared_ptr<Class> make_block(Args&&... args)
		{
			return std::make_shared<Class>(std::forward<Args>(args)...);
		}
	};

	class Document : public document
	{
		Nodes m_children;
	public:
		Document(const Nodes& children) : m_children(children) {}
		void text(stream& o, const variables_t& vars, list_ctx& ctx) const override
		{
			for (auto& child : m_children)
				child->text(o, vars, ctx);
		}

		void markup(stream& o, const variables_t& vars, const styler_ptr& styler, list_ctx& ctx) const override
		{
			styler->begin_document(o);
			for (auto& child : m_children)
				child->markup(o, vars, styler, ctx);
			styler->end_document(o);
		}

		void debug(stream& o) const override
		{
			for (auto& child : m_children)
				child->debug(o);
		}

		void store(const filesystem::path& obj)
		{
			binary::Writer wr;
			if (!wr.open(obj))
				return;

			if (!wr.store(m_children) || !wr.close())
				filesystem::remove(obj);
		}
	};

	/* public: */

	void cstream::write(const char* buffer, size_t size)
	{
		ref.write(buffer, size);
	}

	document_ptr compile(const filesystem::path& file, const filesystem::path& obj)
	{
		filesystem::status st{ file };
		if (!st.exists())
			return nullptr;

		if (st.mtime() <= filesystem::status{ obj }.mtime())
		{
			binary::Reader r;
			if (r.open(obj))
			{
				Nodes children;
				if (r.load(children))
					return std::make_shared<Document>(children);
			}
		}

		size_t size = (size_t)st.file_size();

		std::string text;
		text.resize(size + 1);
		if (text.size() <= size)
			return nullptr;

		auto f = fopen(file.native().c_str(), "r");
		if (!f)
			return nullptr;
		if (fread(&text[0], 1, size, f) != size)
		{
			fclose(f);
			return nullptr;
		};
		fclose(f);

		text[size] = 0;

		document_ptr out = compile(text);
		std::static_pointer_cast<Document>(out)->store(obj);
		return out;
	}

	document_ptr compile(const filesystem::path& file)
	{
		filesystem::status st{ file };
		if (!st.exists())
			return nullptr;

		size_t size = (size_t)st.file_size();

		std::string text;
		text.resize(size + 1);
		if (text.size() <= size)
			return nullptr;

		auto f = fopen(file.native().c_str(), "r");
		if (!f)
			return nullptr;

		if (fread(&text[0], 1, size, f) != size)
		{
			fclose(f);
			return nullptr;
		};
		fclose(f);

		text[size] = 0;
		return compile(text);
	}

	document_ptr compile(const std::string& text)
	{
		auto blocks = parser::Parser{}.parse(text);

		Nodes children;
		compiler::compile(children, blocks, false);
		return std::make_shared<Document>(children);
	}

	/* private: */
	Nodes compiler::compile(const parser::line::Tokens& tokens, bool pre)
	{
		Nodes list;
		compile(list, tokens.begin(), tokens.end(), TOKEN::BAD, pre);
		return list;
	}

	compiler::Tokens::const_iterator compiler::compile(Nodes& items, Tokens::const_iterator begin, Tokens::const_iterator end, TOKEN endtok, bool pre)
	{
		std::shared_ptr<inline_elem::Text> text;
		std::shared_ptr<Node> elem;

		auto cur = begin;
		for (; cur != end; ++cur)
		{
			if (cur->type == endtok)
				return cur;

			if (cur->type != TOKEN::TEXT)
				text = nullptr;

			Nodes children;

			auto&& token = *cur;
			switch (token.type)
			{
			case TOKEN::TEXT:
				if (text)
					text->append(token.arg.begin(), token.arg.end());
				else
					text = make_node<inline_elem::Text>(items, token.arg.str());

				break;

			case TOKEN::BREAK:
				make_node<inline_elem::Break>(items);
				break;

			case TOKEN::LINE:
				make_node<inline_elem::Line>(items);
				break;

			case TOKEN::TAG_S:
				cur = element{ items, cur, end, token.arg.str() }.scan();
				break;

			case TOKEN::VAR_S:
				cur = variable{ items, cur, end }.scan();
				break;

			case TOKEN::HREF_S:
				cur = link{ items, cur, end }.scan();
				break;

			case TOKEN::HREF_NS:
			case TOKEN::HREF_SEG:
				make_node<inline_elem::Token>(items, token.type);
				break;
			}

			if (cur == end)
				break;
		}

		return cur;
	}

	void compiler::compile(Nodes& children, const parser::Parser::Text& blocks, bool pre)
	{
		for (auto&& block : blocks)
		{
			auto child = compile(block, pre);
			if (child)
				children.push_back(child);
		}
	}

	NodePtr compiler::compile(const parser::Parser::Block& block, bool pre)
	{
		using BLOCK = parser::Parser::BLOCK;

		if (!pre)
			pre = block.type == BLOCK::PRE;
		auto children = compile(block.tokens, pre);
		compile(children, block.items, pre);

		switch (block.type)
		{
		case BLOCK::HEADER:    return make_block<block_elem::Header>(block.iArg, children);
		case BLOCK::PARA:      return make_block<block_elem::Para>(children);
		case BLOCK::QUOTE:     return make_block<block_elem::Quote>(children);
		case BLOCK::PRE:       return make_block<block_elem::Pre>(children);
		case BLOCK::UL:        return make_block<block_elem::UList>(children);
		case BLOCK::OL:        return make_block<block_elem::OList>(children);
		case BLOCK::ITEM:      return make_block<block_elem::Item>(children);
		case BLOCK::HR:        return make_block<block_elem::HR>();
		case BLOCK::SIGNATURE: return make_block<block_elem::Signature>(children);
		default:
			break;
		}

		return nullptr;
	}

	std::string compiler::text(const Nodes& nodes)
	{
		std::ostringstream o;
		cstream co{ o };
		variables_t vars;
		list_ctx ctx;
		for (auto&& node : nodes)
			node->text(co, vars, ctx);
		return o.str();
	}
}
<|endoftext|>


<commit_before>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2012, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================

#include <QApplication>
#include <QX11Info>
#include <QProcess>
#include <QProcessEnvironment>
#include <QFile>
#include <QFileInfo>
#include <QString>
#include <QUrl>
#include <QDebug>
#include <QTranslator>
//#include <QLocale>
#include <QMessageBox>
#include <QSplashScreen>
#include <QDateTime>
#include <QPixmap>
#include <QColor>
#include <QFont>
//#include <QTextCodec>

#include "LFileDialog.h"

#include <LuminaXDG.h>
#include <LuminaUtils.h>
#include <LuminaOS.h>
#include <LuminaThemes.h>

void printUsageInfo(){
  qDebug() << "lumina-open: Application launcher for the Lumina Desktop Environment";
  qDebug() << "Description: Given a file (with absolute path) or URL, this utility will try to find the appropriate application with which to open the file. If the file is a *.desktop application shortcut, it will just start the application appropriately. It can also perform a few specific system operations if given special flags.";
  qDebug() << "Usage: lumina-open [-select] <absolute file path or URL>";
  qDebug() << "           lumina-open [-volumeup, -volumedown, -brightnessup, -brightnessdown]";
  qDebug() << "  [-select] (optional) flag to bypass any default application settings and show the application selector window";
  qDebug() << "Special Flags:";
  qDebug() << " \"-volume[up/down]\" Flag to increase/decrease audio volume by 5%";
  qDebug() << " \"-brightness[up/down]\" Flag to increase/decrease screen brightness by 5%";
  exit(1);
}

void ShowErrorDialog(int argc, char **argv, QString message){
    //Setup the application
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
	LUtils::LoadTranslation(&App,"lumina-open");
    QMessageBox dlg(QMessageBox::Critical, QObject::tr("File Error"), message );
    dlg.exec();
    exit(1);
}

void showOSD(int argc, char **argv, QString message){
  //Setup the application
  QApplication App(argc, argv);
    LUtils::LoadTranslation(&App,"lumina-open");

  //Display the OSD
  QPixmap pix(":/icons/OSD.png");
  QSplashScreen splash(pix, Qt::SplashScreen | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
     splash.setWindowTitle("");
     QFont myfont;
	myfont.setBold(true);
	myfont.setPixelSize(13);
	splash.setFont(myfont);
  qDebug() << "Display OSD";
  splash.show();
  //qDebug() << " - show message";
  splash.showMessage(message, Qt::AlignCenter, Qt::white);
  //qDebug() << " - loop";
  QDateTime end = QDateTime::currentDateTime().addMSecs(800);
  while(QDateTime::currentDateTime() < end){ App.processEvents(); }
  splash.hide();
}

QString cmdFromUser(int argc, char **argv, QString inFile, QString extension, QString& path, bool showDLG=false){
    //First check to see if there is a default for this extension
    QString defApp;
    if(extension=="mimetype"){
	//qDebug() << "inFile:" << inFile;
	QStringList matches = LXDG::findAppMimeForFile(inFile, true).split("::::"); //allow multiple matches
	//qDebug() << "Matches:" << matches;
	for(int i=0; i<matches.length(); i++){
	  defApp = LXDG::findDefaultAppForMime(matches[i]);
	  if(!defApp.isEmpty()){ extension = matches[i]; break; }
	  else if(i+1==matches.length()){ extension = matches[0]; }
	}
    }else{ defApp = LFileDialog::getDefaultApp(extension); }

    if( !defApp.isEmpty() && !showDLG ){
      bool ok = false;
      if(defApp.endsWith(".desktop")){
        XDGDesktop DF = LXDG::loadDesktopFile(defApp, ok);
        if(ok){
      	  QString exec = LXDG::getDesktopExec(DF);
      	  if(!exec.isEmpty()){
      	    qDebug() << "[lumina-open] Using default application:" << DF.name << "File:" << inFile;
            if(!DF.path.isEmpty()){ path = DF.path; }
            return exec;
          }
        }
      }else{
	//Only binary given
	if(LUtils::isValidBinary(defApp)){
	  qDebug() << "[lumina-open] Using default application:" << defApp << "File:" << inFile;
	  return defApp; //just use the binary
	}
      }
      //invalid default - reset it and continue on
      LFileDialog::setDefaultApp(extension, "");
    }
    //Final catch: directory given - no valid default found - use lumina-fm
    if(extension=="directory" && !showDLG){ return "lumina-fm"; }
    //No default set -- Start up the application selection dialog
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
      LUtils::LoadTranslation(&App,"lumina-open");

    LFileDialog w;
    if(extension=="email" || extension=="webbrowser"){
      //URL
      w.setFileInfo(inFile, extension, false);
    }else{
      //File
      if(inFile.endsWith("/")){ inFile.chop(1); }
      w.setFileInfo(inFile.section("/",-1), extension, true);
    }

    w.show();
    App.exec();
    if(!w.appSelected){ return ""; }
    //Return the run path if appropriate
    if(!w.appPath.isEmpty()){ path = w.appPath; }
    //Just do the default application registration here for now
    //  might move it to the runtime phase later after seeing that the app has successfully started
    if(w.setDefault){
      if(!w.appFile.isEmpty()){ LFileDialog::setDefaultApp(extension, w.appFile); }
      else{ LFileDialog::setDefaultApp(extension, w.appExec); }
    }else{ LFileDialog::setDefaultApp(extension, ""); }
    //Now return the resulting application command
    return w.appExec;
}

void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& path){
  //Get the input file
    //Make sure to load the proper system encoding first
    LUtils::LoadTranslation(0,""); //bypass application modification
  QString inFile;
  bool showDLG = false; //flag to bypass any default application setting
  if(argc > 1){
    for(int i=1; i<argc; i++){
      if(QString(argv[i]).simplified() == "-select"){
      	showDLG = true;
      }else if(QString(argv[i]).simplified() == "-volumeup"){
	int vol = LOS::audioVolume()+5; //increase 5%
	if(vol>100){ vol=100; }
	LOS::setAudioVolume(vol);
	showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
	return;
      }else if(QString(argv[i]).simplified() == "-volumedown"){
	int vol = LOS::audioVolume()-5; //decrease 5%
	if(vol<0){ vol=0; }
	LOS::setAudioVolume(vol);
	showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
	return;
      }else if(QString(argv[i]).simplified() == "-brightnessup"){
	int bright = LOS::ScreenBrightness();
	if(bright > 0){ //brightness control available
	  bright = bright+5; //increase 5%
	  if(bright>100){ bright = 100; }
	  LOS::setScreenBrightness(bright);
	  showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
	}
	return;
      }else if(QString(argv[i]).simplified() == "-brightnessdown"){
	int bright = LOS::ScreenBrightness();
	if(bright > 0){ //brightness control available
	  bright = bright-5; //decrease 5%
	  if(bright<0){ bright = 0; }
	  LOS::setScreenBrightness(bright);
	  showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
	}
	return;
      }else{
        inFile = QString::fromLocal8Bit(argv[i]);
        break;
      }
    }
  }else{
    printUsageInfo();
  }
  //Make sure that it is a valid file/URL
  bool isFile=false; bool isUrl=false;
  if(QFile::exists(inFile)){ isFile=true; }
  else if(QFile::exists(QDir::currentPath()+"/"+inFile)){isFile=true; inFile = QDir::currentPath()+"/"+inFile;} //account for relative paths
  else if(QUrl(inFile).isValid() && !inFile.startsWith("/") ){ isUrl=true; }
  if( !isFile && !isUrl ){ ShowErrorDialog( argc, argv, QString(QObject::tr("Invalid file or URL: %1")).arg(inFile) ); }
  //Determing the type of file (extension)
  QString extension;
  //qDebug() << "File Type:" << isFile << isUrl;
  if(isFile){
    QFileInfo info(inFile);
    extension=info.suffix();
    //qDebug() << " - Extension:" << extension;
    if(info.isDir()){ extension="directory"; }
    else if(info.isExecutable() && extension.isEmpty()){ extension="binary"; }
    else if(extension!="desktop"){ extension="mimetype"; } //flag to check for mimetype default based on file
  }
  else if(isUrl && inFile.startsWith("mailto:")){ extension = "email"; }
  else if(isUrl){ extension = "webbrowser"; }
  //qDebug() << "Input:" << inFile << isFile << isUrl << extension;
  //if not an application  - find the right application to open the file
  QString cmd;
  bool useInputFile = false;
  if(extension=="desktop" && !showDLG){
    bool ok = false;
    XDGDesktop DF = LXDG::loadDesktopFile(inFile, ok);
    if(!ok){
      ShowErrorDialog( argc, argv, QString(QObject::tr("File could not be opened: %1")).arg(inFile) );
    }
    switch(DF.type){
      case XDGDesktop::APP:
        if(!DF.exec.isEmpty()){
          cmd = LXDG::getDesktopExec(DF);
          if(!DF.path.isEmpty()){ path = DF.path; }
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("Application shortcut is missing the launching information (malformed shortcut): %1")).arg(inFile) );
        }
        break;
      case XDGDesktop::LINK:
        if(!DF.url.isEmpty()){
          //This is a URL - so adjust the input variables appropriately
          inFile = DF.url;
          cmd.clear();
          extension = inFile.section(":",0,0);
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("URL shortcut is missing the URL: %1")).arg(inFile) );
        }
        break;
      case XDGDesktop::DIR:
        if(!DF.path.isEmpty()){
          //This is a directory link - adjust inputs
          inFile = DF.path;
          cmd.clear();
          extension = "directory";
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("Directory shortcut is missing the path to the directory: %1")).arg(inFile) );
        }
        break;
      default:
	ShowErrorDialog( argc, argv, QString(QObject::tr("Unknown type of shortcut : %1")).arg(inFile) );
    }
  }
  if(cmd.isEmpty()){
    if(extension=="binary"){ cmd = inFile; }
    else{
    //Find out the proper application to use this file/directory
    useInputFile=true;
    cmd = cmdFromUser(argc, argv, inFile, extension, path, showDLG);
    if(cmd.isEmpty()){ return; }
    }
  }
  //Now assemble the exec string (replace file/url field codes as necessary)
  if(useInputFile){
    args = inFile; //just to keep them distinct internally
    // NOTE: lumina-open is only designed for a single input file,
    //    so no need to distinguish between the list codes (uppercase)
    //    and the single-file codes (lowercase)
    if(isFile && (cmd.contains("%f") || cmd.contains("%F") ) ){
      cmd.replace("%f","\""+inFile+"\"");
      cmd.replace("%F","\""+inFile+"\"");
    }else if(isUrl && (cmd.contains("%U") || cmd.contains("%u")) ){
      cmd.replace("%u","\""+inFile+"\"");
      cmd.replace("%U","\""+inFile+"\"");
    }else{
      //No field codes (or improper field codes given - which is quite common)
      // - Just tack the input file on the end and let the app handle it as necessary
      cmd.append(" \""+inFile+"\"");
    }
  }
  //qDebug() << "Found Command:" << cmd << "Extension:" << extension;
  //Clean up any leftover "Exec" field codes (should have already been replaced earlier)
  if(cmd.contains("%")){cmd = cmd.remove("%U").remove("%u").remove("%F").remove("%f").remove("%i").remove("%c").remove("%k").simplified(); }
  binary = cmd; //pass this string to the calling function

}

int main(int argc, char **argv){
  //Run all the actual code in a separate function to have as little memory usage
  //  as possible aside from the main application when running

  //Make sure the XDG environment variables exist first
  LXDG::setEnvironmentVars();
  //now get the command
  QString cmd, args, path;
  getCMD(argc, argv, cmd, args, path);
  //qDebug() << "Run CMD:" << cmd << args;
  //Now run the command (move to execvp() later?)
  if(cmd.isEmpty()){ return 0; } //no command to run (handled internally)
  //if(!args.isEmpty()){ cmd.append(" "+args+""); }
  //int retcode = system( cmd.toUtf8() );
  qDebug() << "[lumina-open] Running Cmd:" << cmd;
  QProcess *p = new QProcess();
  p->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
  if(!path.isEmpty() && QFile::exists(path)){ p->setWorkingDirectory(path); }
  p->start(cmd);
  //Check the startup procedure
  /*while(!p->waitForStarted(5000)){
    if(p->state() == QProcess::NotRunning){
     //bad/invalid start
     qDebug() << "[lumina-open] Application did not start properly:"<<cmd;
     return p->exitCode();
    }else if(p->state() == QProcess::Running){
     //This just missed the "started" signal - continue
     break;
    }
  }*/
  //Now check up on it once every minute until it is finished
  while(!p->waitForFinished(60000)){
    //qDebug() << "[lumina-open] process check:" << p->state();
    if(p->state() != QProcess::Running){ break; } //somehow missed the finished signal
  }
  int retcode = p->exitCode();
  //qDebug() << "[lumina-open] Finished Cmd:" << cmd << retcode << p->exitStatus();

  //if(retcode!=0 ){
  if(p->exitStatus() == QProcess::CrashExit){
    qDebug() << "[lumina-open] Application Error:" << retcode;
    QString err = QString(p->readAllStandardError());
    if(err.isEmpty()){ err = QString(p->readAllStandardOutput()); }
    //Setup the application
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
	LUtils::LoadTranslation(&App,"lumina-open");
    QMessageBox dlg(QMessageBox::Critical, QObject::tr("Application Error"), QObject::tr("The following application experienced an error and needed to close:")+"\n\n"+cmd );
    if(!err.isEmpty()){ dlg.setDetailedText(err); }
    dlg.exec();
  }
  return retcode;
}
<commit_msg>fix issue #55: inform user whan return code is not null<commit_after>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2012, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================

#include <QApplication>
#include <QX11Info>
#include <QProcess>
#include <QProcessEnvironment>
#include <QFile>
#include <QFileInfo>
#include <QString>
#include <QUrl>
#include <QDebug>
#include <QTranslator>
//#include <QLocale>
#include <QMessageBox>
#include <QSplashScreen>
#include <QDateTime>
#include <QPixmap>
#include <QColor>
#include <QFont>
//#include <QTextCodec>

#include "LFileDialog.h"

#include <LuminaXDG.h>
#include <LuminaUtils.h>
#include <LuminaOS.h>
#include <LuminaThemes.h>

void printUsageInfo(){
  qDebug() << "lumina-open: Application launcher for the Lumina Desktop Environment";
  qDebug() << "Description: Given a file (with absolute path) or URL, this utility will try to find the appropriate application with which to open the file. If the file is a *.desktop application shortcut, it will just start the application appropriately. It can also perform a few specific system operations if given special flags.";
  qDebug() << "Usage: lumina-open [-select] <absolute file path or URL>";
  qDebug() << "           lumina-open [-volumeup, -volumedown, -brightnessup, -brightnessdown]";
  qDebug() << "  [-select] (optional) flag to bypass any default application settings and show the application selector window";
  qDebug() << "Special Flags:";
  qDebug() << " \"-volume[up/down]\" Flag to increase/decrease audio volume by 5%";
  qDebug() << " \"-brightness[up/down]\" Flag to increase/decrease screen brightness by 5%";
  exit(1);
}

void ShowErrorDialog(int argc, char **argv, QString message){
    //Setup the application
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
	LUtils::LoadTranslation(&App,"lumina-open");
    QMessageBox dlg(QMessageBox::Critical, QObject::tr("File Error"), message );
    dlg.exec();
    exit(1);
}

void showOSD(int argc, char **argv, QString message){
  //Setup the application
  QApplication App(argc, argv);
    LUtils::LoadTranslation(&App,"lumina-open");

  //Display the OSD
  QPixmap pix(":/icons/OSD.png");
  QSplashScreen splash(pix, Qt::SplashScreen | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
     splash.setWindowTitle("");
     QFont myfont;
	myfont.setBold(true);
	myfont.setPixelSize(13);
	splash.setFont(myfont);
  qDebug() << "Display OSD";
  splash.show();
  //qDebug() << " - show message";
  splash.showMessage(message, Qt::AlignCenter, Qt::white);
  //qDebug() << " - loop";
  QDateTime end = QDateTime::currentDateTime().addMSecs(800);
  while(QDateTime::currentDateTime() < end){ App.processEvents(); }
  splash.hide();
}

QString cmdFromUser(int argc, char **argv, QString inFile, QString extension, QString& path, bool showDLG=false){
    //First check to see if there is a default for this extension
    QString defApp;
    if(extension=="mimetype"){
	//qDebug() << "inFile:" << inFile;
	QStringList matches = LXDG::findAppMimeForFile(inFile, true).split("::::"); //allow multiple matches
	//qDebug() << "Matches:" << matches;
	for(int i=0; i<matches.length(); i++){
	  defApp = LXDG::findDefaultAppForMime(matches[i]);
	  if(!defApp.isEmpty()){ extension = matches[i]; break; }
	  else if(i+1==matches.length()){ extension = matches[0]; }
	}
    }else{ defApp = LFileDialog::getDefaultApp(extension); }

    if( !defApp.isEmpty() && !showDLG ){
      bool ok = false;
      if(defApp.endsWith(".desktop")){
        XDGDesktop DF = LXDG::loadDesktopFile(defApp, ok);
        if(ok){
      	  QString exec = LXDG::getDesktopExec(DF);
      	  if(!exec.isEmpty()){
      	    qDebug() << "[lumina-open] Using default application:" << DF.name << "File:" << inFile;
            if(!DF.path.isEmpty()){ path = DF.path; }
            return exec;
          }
        }
      }else{
	//Only binary given
	if(LUtils::isValidBinary(defApp)){
	  qDebug() << "[lumina-open] Using default application:" << defApp << "File:" << inFile;
	  return defApp; //just use the binary
	}
      }
      //invalid default - reset it and continue on
      LFileDialog::setDefaultApp(extension, "");
    }
    //Final catch: directory given - no valid default found - use lumina-fm
    if(extension=="directory" && !showDLG){ return "lumina-fm"; }
    //No default set -- Start up the application selection dialog
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
      LUtils::LoadTranslation(&App,"lumina-open");

    LFileDialog w;
    if(extension=="email" || extension=="webbrowser"){
      //URL
      w.setFileInfo(inFile, extension, false);
    }else{
      //File
      if(inFile.endsWith("/")){ inFile.chop(1); }
      w.setFileInfo(inFile.section("/",-1), extension, true);
    }

    w.show();
    App.exec();
    if(!w.appSelected){ return ""; }
    //Return the run path if appropriate
    if(!w.appPath.isEmpty()){ path = w.appPath; }
    //Just do the default application registration here for now
    //  might move it to the runtime phase later after seeing that the app has successfully started
    if(w.setDefault){
      if(!w.appFile.isEmpty()){ LFileDialog::setDefaultApp(extension, w.appFile); }
      else{ LFileDialog::setDefaultApp(extension, w.appExec); }
    }else{ LFileDialog::setDefaultApp(extension, ""); }
    //Now return the resulting application command
    return w.appExec;
}

void getCMD(int argc, char ** argv, QString& binary, QString& args, QString& path){
  //Get the input file
    //Make sure to load the proper system encoding first
    LUtils::LoadTranslation(0,""); //bypass application modification
  QString inFile;
  bool showDLG = false; //flag to bypass any default application setting
  if(argc > 1){
    for(int i=1; i<argc; i++){
      if(QString(argv[i]).simplified() == "-select"){
      	showDLG = true;
      }else if(QString(argv[i]).simplified() == "-volumeup"){
	int vol = LOS::audioVolume()+5; //increase 5%
	if(vol>100){ vol=100; }
	LOS::setAudioVolume(vol);
	showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
	return;
      }else if(QString(argv[i]).simplified() == "-volumedown"){
	int vol = LOS::audioVolume()-5; //decrease 5%
	if(vol<0){ vol=0; }
	LOS::setAudioVolume(vol);
	showOSD(argc,argv, QString(QObject::tr("Audio Volume %1%")).arg(QString::number(vol)) );
	return;
      }else if(QString(argv[i]).simplified() == "-brightnessup"){
	int bright = LOS::ScreenBrightness();
	if(bright > 0){ //brightness control available
	  bright = bright+5; //increase 5%
	  if(bright>100){ bright = 100; }
	  LOS::setScreenBrightness(bright);
	  showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
	}
	return;
      }else if(QString(argv[i]).simplified() == "-brightnessdown"){
	int bright = LOS::ScreenBrightness();
	if(bright > 0){ //brightness control available
	  bright = bright-5; //decrease 5%
	  if(bright<0){ bright = 0; }
	  LOS::setScreenBrightness(bright);
	  showOSD(argc,argv, QString(QObject::tr("Screen Brightness %1%")).arg(QString::number(bright)) );
	}
	return;
      }else{
        inFile = QString::fromLocal8Bit(argv[i]);
        break;
      }
    }
  }else{
    printUsageInfo();
  }
  //Make sure that it is a valid file/URL
  bool isFile=false; bool isUrl=false;
  if(QFile::exists(inFile)){ isFile=true; }
  else if(QFile::exists(QDir::currentPath()+"/"+inFile)){isFile=true; inFile = QDir::currentPath()+"/"+inFile;} //account for relative paths
  else if(QUrl(inFile).isValid() && !inFile.startsWith("/") ){ isUrl=true; }
  if( !isFile && !isUrl ){ ShowErrorDialog( argc, argv, QString(QObject::tr("Invalid file or URL: %1")).arg(inFile) ); }
  //Determing the type of file (extension)
  QString extension;
  //qDebug() << "File Type:" << isFile << isUrl;
  if(isFile){
    QFileInfo info(inFile);
    extension=info.suffix();
    //qDebug() << " - Extension:" << extension;
    if(info.isDir()){ extension="directory"; }
    else if(info.isExecutable() && extension.isEmpty()){ extension="binary"; }
    else if(extension!="desktop"){ extension="mimetype"; } //flag to check for mimetype default based on file
  }
  else if(isUrl && inFile.startsWith("mailto:")){ extension = "email"; }
  else if(isUrl){ extension = "webbrowser"; }
  //qDebug() << "Input:" << inFile << isFile << isUrl << extension;
  //if not an application  - find the right application to open the file
  QString cmd;
  bool useInputFile = false;
  if(extension=="desktop" && !showDLG){
    bool ok = false;
    XDGDesktop DF = LXDG::loadDesktopFile(inFile, ok);
    if(!ok){
      ShowErrorDialog( argc, argv, QString(QObject::tr("File could not be opened: %1")).arg(inFile) );
    }
    switch(DF.type){
      case XDGDesktop::APP:
        if(!DF.exec.isEmpty()){
          cmd = LXDG::getDesktopExec(DF);
          if(!DF.path.isEmpty()){ path = DF.path; }
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("Application shortcut is missing the launching information (malformed shortcut): %1")).arg(inFile) );
        }
        break;
      case XDGDesktop::LINK:
        if(!DF.url.isEmpty()){
          //This is a URL - so adjust the input variables appropriately
          inFile = DF.url;
          cmd.clear();
          extension = inFile.section(":",0,0);
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("URL shortcut is missing the URL: %1")).arg(inFile) );
        }
        break;
      case XDGDesktop::DIR:
        if(!DF.path.isEmpty()){
          //This is a directory link - adjust inputs
          inFile = DF.path;
          cmd.clear();
          extension = "directory";
        }else{
	  ShowErrorDialog( argc, argv, QString(QObject::tr("Directory shortcut is missing the path to the directory: %1")).arg(inFile) );
        }
        break;
      default:
	ShowErrorDialog( argc, argv, QString(QObject::tr("Unknown type of shortcut : %1")).arg(inFile) );
    }
  }
  if(cmd.isEmpty()){
    if(extension=="binary"){ cmd = inFile; }
    else{
    //Find out the proper application to use this file/directory
    useInputFile=true;
    cmd = cmdFromUser(argc, argv, inFile, extension, path, showDLG);
    if(cmd.isEmpty()){ return; }
    }
  }
  //Now assemble the exec string (replace file/url field codes as necessary)
  if(useInputFile){
    args = inFile; //just to keep them distinct internally
    // NOTE: lumina-open is only designed for a single input file,
    //    so no need to distinguish between the list codes (uppercase)
    //    and the single-file codes (lowercase)
    if(isFile && (cmd.contains("%f") || cmd.contains("%F") ) ){
      cmd.replace("%f","\""+inFile+"\"");
      cmd.replace("%F","\""+inFile+"\"");
    }else if(isUrl && (cmd.contains("%U") || cmd.contains("%u")) ){
      cmd.replace("%u","\""+inFile+"\"");
      cmd.replace("%U","\""+inFile+"\"");
    }else{
      //No field codes (or improper field codes given - which is quite common)
      // - Just tack the input file on the end and let the app handle it as necessary
      cmd.append(" \""+inFile+"\"");
    }
  }
  //qDebug() << "Found Command:" << cmd << "Extension:" << extension;
  //Clean up any leftover "Exec" field codes (should have already been replaced earlier)
  if(cmd.contains("%")){cmd = cmd.remove("%U").remove("%u").remove("%F").remove("%f").remove("%i").remove("%c").remove("%k").simplified(); }
  binary = cmd; //pass this string to the calling function

}

int main(int argc, char **argv){
  //Run all the actual code in a separate function to have as little memory usage
  //  as possible aside from the main application when running

  //Make sure the XDG environment variables exist first
  LXDG::setEnvironmentVars();
  //now get the command
  QString cmd, args, path;
  getCMD(argc, argv, cmd, args, path);
  //qDebug() << "Run CMD:" << cmd << args;
  //Now run the command (move to execvp() later?)
  if(cmd.isEmpty()){ return 0; } //no command to run (handled internally)
  //if(!args.isEmpty()){ cmd.append(" "+args+""); }
  //int retcode = system( cmd.toUtf8() );
  qDebug() << "[lumina-open] Running Cmd:" << cmd;
  QProcess *p = new QProcess();
  p->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
  if(!path.isEmpty() && QFile::exists(path)){ p->setWorkingDirectory(path); }
  p->start(cmd);
  //Check the startup procedure
  /*while(!p->waitForStarted(5000)){
    if(p->state() == QProcess::NotRunning){
     //bad/invalid start
     qDebug() << "[lumina-open] Application did not start properly:"<<cmd;
     return p->exitCode();
    }else if(p->state() == QProcess::Running){
     //This just missed the "started" signal - continue
     break;
    }
  }*/
  //Now check up on it once every minute until it is finished
  while(!p->waitForFinished(60000)){
    //qDebug() << "[lumina-open] process check:" << p->state();
    if(p->state() != QProcess::Running){ break; } //somehow missed the finished signal
  }
  int retcode = p->exitCode();
  //qDebug() << "[lumina-open] Finished Cmd:" << cmd << retcode << p->exitStatus();

  //if(retcode!=0 ){
  if(p->exitStatus() == QProcess::CrashExit or retcode > 0){
    qDebug() << "[lumina-open] Application Error:" << retcode;
    QString err = QString(p->readAllStandardError());
    if(err.isEmpty()){ err = QString(p->readAllStandardOutput()); }
    //Setup the application
    QApplication App(argc, argv);
    LuminaThemeEngine theme(&App);
	LUtils::LoadTranslation(&App,"lumina-open");
    QMessageBox dlg(QMessageBox::Critical, QObject::tr("Application Error"), QObject::tr("The following application experienced an error and needed to close:")+"\n\n"+cmd );
    if(!err.isEmpty()){ dlg.setDetailedText(err); }
    dlg.exec();
  }
  return retcode;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/data.hpp"
#include "vast/json.hpp"

#include "vast/detail/assert.hpp"
#include "vast/detail/overload.hpp"

namespace vast {
namespace {

struct adder {
  template <class T>
  std::enable_if_t<
    !(std::is_same<T, caf::none_t>{}
      || std::is_same<T, vector>{}
      || std::is_same<T, set>{})
  >
  operator()(caf::none_t, const T& x) const {
    self = x;
  }

  void operator()(caf::none_t, caf::none_t) const {
    // nop
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
      && (std::is_same<T, boolean>{}
           || std::is_same<T, integer>{}
           || std::is_same<T, count>{}
           || std::is_same<T, real>{}
           || std::is_same<T, timespan>{}
           || std::is_same<T, std::string>{})
  >
  operator()(T& x, const T& y) const {
    x += y;
  }

  template <class T, class U>
  std::enable_if_t<
    !(std::is_same<T, U>{} || std::is_same<T, vector>{}
                           || std::is_same<T, set>{})
      && (std::is_same<T, boolean>{}
          || std::is_same<T, integer>{}
          || std::is_same<T, count>{}
          || std::is_same<T, real>{}
          || std::is_same<T, timespan>{}
          || std::is_same<T, std::string>{})
  >
  operator()(T&, const U&) const {
    // impossible
  }

  void operator()(timestamp&, timestamp) const {
    // impossible
  }

  void operator()(timestamp& ts, timespan x) const {
    ts += x;
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, timestamp>{}
      || std::is_same<T, timespan>{}
      || std::is_same<T, vector>{}
      || std::is_same<T, set>{})
  >
  operator()(timestamp&, const T&) const {
    // impossible
  }

  template <class T, class U>
  std::enable_if_t<
    !(std::is_same<U, vector>{} || std::is_same<U, set>{})
      && (std::is_same<T, pattern>{}
          || std::is_same<T, address>{}
          || std::is_same<T, subnet>{}
          || std::is_same<T, port>{}
          || std::is_same<T, enumeration>{}
          || std::is_same<T, map>{})
  >
  operator()(T&, const U&) const {
    // impossible
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
  >
  operator()(vector& lhs, const T& rhs) const {
    lhs.emplace_back(rhs);
  }

  void operator()(vector& lhs, const vector& rhs) const {
    std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
  >
  operator()(set& lhs, const T& rhs) const {
    lhs.emplace(rhs);
  }

  void operator()(set& lhs, const set& rhs) const {
    std::copy(rhs.begin(), rhs.end(), std::inserter(lhs, lhs.end()));
  }

  template <class T>
  std::enable_if_t<!std::is_same<T, vector>{}>
  operator()(T&, const vector& rhs) const {
    vector v;
    v.reserve(rhs.size() + 1);
    v.push_back(std::move(self));
    std::copy(rhs.begin(), rhs.end(), std::back_inserter(v));
    self = std::move(v);
  }

  template <class T>
  std::enable_if_t<!std::is_same<T, set>{}>
  operator()(T&, const set& rhs) const {
    set s;
    s.insert(std::move(self));
    std::copy(rhs.begin(), rhs.end(), std::inserter(s, s.end()));
    self = std::move(s);
  }

  data& self;
};

struct match_visitor {
  bool operator()(const std::string& lhs, const pattern& rhs) const {
    return rhs.match(lhs);
  }

  template <class T, class U>
  bool operator()(const T&, const U&) const {
    return false;
  }
};

struct in_visitor {
  bool operator()(const std::string& lhs, const std::string& rhs) const {
    return rhs.find(lhs) != std::string::npos;
  }

  bool operator()(const std::string& lhs, const pattern& rhs) const {
    return rhs.search(lhs);
  }

  bool operator()(const address& lhs, const subnet& rhs) const {
    return rhs.contains(lhs);
  }

  bool operator()(const subnet& lhs, const subnet& rhs) const {
    return rhs.contains(lhs);
  }

  template <class T>
  bool operator()(const T& lhs, const set& rhs) const {
    return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end();
  }

  template <class T>
  bool operator()(const T& lhs, const vector& rhs) const {
    return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end();
  }

  template <class T, class U>
  bool operator()(const T&, const U&) const {
    return false;
  }
};

} // namespace <anonymous>

data& data::operator+=(const data& rhs) {
  caf::visit(adder{*this}, *this, rhs);
  return *this;
}

bool operator==(const data& lhs, const data& rhs) {
  return lhs.data_ == rhs.data_;
}

bool operator<(const data& lhs, const data& rhs) {
  return lhs.data_ < rhs.data_;
}

bool evaluate(const data& lhs, relational_operator op, const data& rhs) {
  switch (op) {
    default:
      VAST_ASSERT(!"missing case");
      return false;
    case match:
      return caf::visit(match_visitor{}, lhs, rhs);
    case not_match:
      return !caf::visit(match_visitor{}, lhs, rhs);
    case in:
      return caf::visit(in_visitor{}, lhs, rhs);
    case not_in:
      return !caf::visit(in_visitor{}, lhs, rhs);
    case ni:
      return caf::visit(in_visitor{}, rhs, lhs);
    case not_ni:
      return !caf::visit(in_visitor{}, rhs, lhs);
    case equal:
      return lhs == rhs;
    case not_equal:
      return lhs != rhs;
    case less:
      return lhs < rhs;
    case less_equal:
      return lhs <= rhs;
    case greater:
      return lhs > rhs;
    case greater_equal:
      return lhs >= rhs;
  }
}

bool is_basic(const data& x) {
  return caf::visit(detail::overload(
    [](const auto&) { return true; },
    [](const enumeration&) { return false; },
    [](const vector&) { return false; },
    [](const set&) { return false; },
    [](const map&) { return false; }
  ), x);
}

bool is_complex(const data& x) {
  return !is_basic(x);
}

bool is_recursive(const data& x) {
  return caf::visit(detail::overload(
    [](const auto&) { return false; },
    [](const vector&) { return true; },
    [](const set&) { return true; },
    [](const map&) { return true; }
  ), x);
}

bool is_container(const data& x) {
  return is_recursive(x);
}

const data* get(const vector& v, const offset& o) {
  const vector* x = &v;
  for (size_t i = 0; i < o.size(); ++i) {
    auto& idx = o[i];
    if (idx >= x->size())
      return nullptr;
    auto d = &(*x)[idx];
    if (i + 1 == o.size())
      return d;
    x = caf::get_if<vector>(d);
    if (!x)
      return nullptr;
  }
  return nullptr;
}

const data* get(const data& d, const offset& o) {
  if (auto v = caf::get_if<vector>(&d))
    return get(*v, o);
  return nullptr;
}

namespace {

template <class Iterator>
vector flatten(Iterator& f, Iterator l) {
  vector xs;
  xs.reserve(l - f);
  for (; f != l; ++f) {
    auto&& x = *f;
    if (auto v = caf::get_if<vector>(&x)) {
      auto begin = Iterator{v->begin()};
      auto end = Iterator{v->end()};
      auto ys = flatten(begin, end);
      xs.insert(xs.end(),
                std::make_move_iterator(ys.begin()),
                std::make_move_iterator(ys.end()));
    } else {
      xs.push_back(*f);
    }
  }
  return xs;
}

template <class Iterator>
caf::optional<vector> unflatten(Iterator& f, Iterator l, const record_type& rec) {
  vector xs;
  xs.reserve(rec.fields.size());
  for (auto& field : rec.fields)
    if (f == l) {
      return {};
    } else if (auto rt = caf::get_if<record_type>(&field.type)) {
      auto ys = unflatten(f, l, *rt);
      if (!ys)
        return ys;
      xs.push_back(std::move(*ys));
    } else {
      xs.push_back(*f++);
    }
  return xs;
}

} // namespace <anonymous>

vector flatten(const vector& xs) {
  auto f = xs.begin();
  auto l = xs.end();
  return flatten(f, l);
}

vector flatten(vector&& xs) {
  auto f = std::make_move_iterator(xs.begin());
  auto l = std::make_move_iterator(xs.end());
  return flatten(f, l);
}

data flatten(const data& x) {
  auto xs = caf::get_if<vector>(&x);
  return xs ? flatten(*xs) : x;
}

data flatten(data&& x) {
  auto xs = caf::get_if<vector>(&x);
  return xs ? flatten(std::move(*xs)) : x;
}

caf::optional<vector> unflatten(const vector& xs, const record_type& rt) {
  auto first = xs.begin();
  auto last = xs.end();
  return unflatten(first, last, rt);
}

caf::optional<vector> unflatten(vector&& xs, const record_type& rt) {
  auto first = std::make_move_iterator(xs.begin());
  auto last = std::make_move_iterator(xs.end());
  return unflatten(first, last, rt);
}

caf::optional<vector> unflatten(const data& x, const type& t) {
  auto xs = caf::get_if<vector>(&x);
  auto rt = caf::get_if<record_type>(&t);
  return xs && rt ? unflatten(*xs, *rt) : caf::optional<vector>{};
}

caf::optional<vector> unflatten(data&& x, const type& t) {
  auto xs = caf::get_if<vector>(&x);
  auto rt = caf::get_if<record_type>(&t);
  return xs && rt ? unflatten(std::move(*xs), *rt) : caf::optional<vector>{};
}

namespace {

json jsonize(const data& x) {
  return caf::visit(detail::overload(
    [&](const auto& y) { return to_json(y); },
    [&](caf::none_t) { return json{}; },
    [&](const std::string& str) { return json{str}; }
  ), x);
}

} // namespace <anonymous>

bool convert(const vector& v, json& j) {
  json::array a(v.size());
  for (auto i = 0u; i < v.size(); ++i)
    a[i] = jsonize(v[i]);
  j = std::move(a);
  return true;
}

bool convert(const set& s, json& j) {
  json::array a(s.size());
  auto i = 0u;
  for (auto& x : s)
    a[i++] = jsonize(x);
  j = std::move(a);
  return true;
}

bool convert(const map& t, json& j) {
  json::array values;
  for (auto& p : t) {
    json::array a;
    a.emplace_back(jsonize(p.first));
    a.emplace_back(jsonize(p.second));
    values.emplace_back(std::move(a));
  };
  j = std::move(values);
  return true;
}

bool convert(const data& d, json& j) {
  j = jsonize(d);
  return true;
}

bool convert(const data& d, json& j, const type& t) {
  auto v = caf::get_if<vector>(&d);
  auto rt = caf::get_if<record_type>(&t);
  if (v && rt) {
    if (v->size() != rt->fields.size())
      return false;
    json::object o;
    for (auto i = 0u; i < v->size(); ++i) {
      auto& f = rt->fields[i];
      if (!convert((*v)[i], o[f.name], f.type))
        return false;
    }
    j = std::move(o);
    return true;
  }
  return convert(d, j);
}

} // namespace vast
<commit_msg>Refactor data evaluation<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/data.hpp"
#include "vast/json.hpp"

#include "vast/detail/assert.hpp"
#include "vast/detail/overload.hpp"

namespace vast {
namespace {

struct adder {
  template <class T>
  std::enable_if_t<
    !(std::is_same<T, caf::none_t>{}
      || std::is_same<T, vector>{}
      || std::is_same<T, set>{})
  >
  operator()(caf::none_t, const T& x) const {
    self = x;
  }

  void operator()(caf::none_t, caf::none_t) const {
    // nop
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
      && (std::is_same<T, boolean>{}
           || std::is_same<T, integer>{}
           || std::is_same<T, count>{}
           || std::is_same<T, real>{}
           || std::is_same<T, timespan>{}
           || std::is_same<T, std::string>{})
  >
  operator()(T& x, const T& y) const {
    x += y;
  }

  template <class T, class U>
  std::enable_if_t<
    !(std::is_same<T, U>{} || std::is_same<T, vector>{}
                           || std::is_same<T, set>{})
      && (std::is_same<T, boolean>{}
          || std::is_same<T, integer>{}
          || std::is_same<T, count>{}
          || std::is_same<T, real>{}
          || std::is_same<T, timespan>{}
          || std::is_same<T, std::string>{})
  >
  operator()(T&, const U&) const {
    // impossible
  }

  void operator()(timestamp&, timestamp) const {
    // impossible
  }

  void operator()(timestamp& ts, timespan x) const {
    ts += x;
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, timestamp>{}
      || std::is_same<T, timespan>{}
      || std::is_same<T, vector>{}
      || std::is_same<T, set>{})
  >
  operator()(timestamp&, const T&) const {
    // impossible
  }

  template <class T, class U>
  std::enable_if_t<
    !(std::is_same<U, vector>{} || std::is_same<U, set>{})
      && (std::is_same<T, pattern>{}
          || std::is_same<T, address>{}
          || std::is_same<T, subnet>{}
          || std::is_same<T, port>{}
          || std::is_same<T, enumeration>{}
          || std::is_same<T, map>{})
  >
  operator()(T&, const U&) const {
    // impossible
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
  >
  operator()(vector& lhs, const T& rhs) const {
    lhs.emplace_back(rhs);
  }

  void operator()(vector& lhs, const vector& rhs) const {
    std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
  }

  template <class T>
  std::enable_if_t<
    !(std::is_same<T, vector>{} || std::is_same<T, set>{})
  >
  operator()(set& lhs, const T& rhs) const {
    lhs.emplace(rhs);
  }

  void operator()(set& lhs, const set& rhs) const {
    std::copy(rhs.begin(), rhs.end(), std::inserter(lhs, lhs.end()));
  }

  template <class T>
  std::enable_if_t<!std::is_same<T, vector>{}>
  operator()(T&, const vector& rhs) const {
    vector v;
    v.reserve(rhs.size() + 1);
    v.push_back(std::move(self));
    std::copy(rhs.begin(), rhs.end(), std::back_inserter(v));
    self = std::move(v);
  }

  template <class T>
  std::enable_if_t<!std::is_same<T, set>{}>
  operator()(T&, const set& rhs) const {
    set s;
    s.insert(std::move(self));
    std::copy(rhs.begin(), rhs.end(), std::inserter(s, s.end()));
    self = std::move(s);
  }

  data& self;
};

} // namespace <anonymous>

data& data::operator+=(const data& rhs) {
  caf::visit(adder{*this}, *this, rhs);
  return *this;
}

bool operator==(const data& lhs, const data& rhs) {
  return lhs.data_ == rhs.data_;
}

bool operator<(const data& lhs, const data& rhs) {
  return lhs.data_ < rhs.data_;
}

bool evaluate(const data& lhs, relational_operator op, const data& rhs) {
  auto check_match = [](const auto& x, const auto& y) {
    return caf::visit(detail::overload(
      [](const auto&, const auto&) {
        return false;
      },
      [](const std::string& lhs, const pattern& rhs) {
        return rhs.match(lhs);
      }
    ), x, y);
  };
  auto check_in = [](const auto& x, const auto& y) {
    return caf::visit(detail::overload(
      [](const auto&, const auto&) {
        return false;
      },
      [](const std::string& lhs, const std::string& rhs) {
        return rhs.find(lhs) != std::string::npos;
      },
      [](const std::string& lhs, const pattern& rhs) {
        return rhs.search(lhs);
      },
      [](const address& lhs, const subnet& rhs) {
        return rhs.contains(lhs);
      },
      [](const subnet& lhs, const subnet& rhs) {
        return rhs.contains(lhs);
      },
      [](const auto& lhs, const vector& rhs) {
        return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end();
      },
      [](const auto& lhs, const set& rhs) {
        return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end();
      }
    ), x, y);
  };
  switch (op) {
    default:
      VAST_ASSERT(!"missing case");
      return false;
    case match:
      return check_match(lhs, rhs);
    case not_match:
      return !check_match(lhs, rhs);
    case in:
      return check_in(lhs, rhs);
    case not_in:
      return !check_in(lhs, rhs);
    case ni:
      return check_in(rhs, lhs);
    case not_ni:
      return !check_in(rhs, lhs);
    case equal:
      return lhs == rhs;
    case not_equal:
      return lhs != rhs;
    case less:
      return lhs < rhs;
    case less_equal:
      return lhs <= rhs;
    case greater:
      return lhs > rhs;
    case greater_equal:
      return lhs >= rhs;
  }
}

bool is_basic(const data& x) {
  return caf::visit(detail::overload(
    [](const auto&) { return true; },
    [](const enumeration&) { return false; },
    [](const vector&) { return false; },
    [](const set&) { return false; },
    [](const map&) { return false; }
  ), x);
}

bool is_complex(const data& x) {
  return !is_basic(x);
}

bool is_recursive(const data& x) {
  return caf::visit(detail::overload(
    [](const auto&) { return false; },
    [](const vector&) { return true; },
    [](const set&) { return true; },
    [](const map&) { return true; }
  ), x);
}

bool is_container(const data& x) {
  return is_recursive(x);
}

const data* get(const vector& v, const offset& o) {
  const vector* x = &v;
  for (size_t i = 0; i < o.size(); ++i) {
    auto& idx = o[i];
    if (idx >= x->size())
      return nullptr;
    auto d = &(*x)[idx];
    if (i + 1 == o.size())
      return d;
    x = caf::get_if<vector>(d);
    if (!x)
      return nullptr;
  }
  return nullptr;
}

const data* get(const data& d, const offset& o) {
  if (auto v = caf::get_if<vector>(&d))
    return get(*v, o);
  return nullptr;
}

namespace {

template <class Iterator>
vector flatten(Iterator& f, Iterator l) {
  vector xs;
  xs.reserve(l - f);
  for (; f != l; ++f) {
    auto&& x = *f;
    if (auto v = caf::get_if<vector>(&x)) {
      auto begin = Iterator{v->begin()};
      auto end = Iterator{v->end()};
      auto ys = flatten(begin, end);
      xs.insert(xs.end(),
                std::make_move_iterator(ys.begin()),
                std::make_move_iterator(ys.end()));
    } else {
      xs.push_back(*f);
    }
  }
  return xs;
}

template <class Iterator>
caf::optional<vector> unflatten(Iterator& f, Iterator l, const record_type& rec) {
  vector xs;
  xs.reserve(rec.fields.size());
  for (auto& field : rec.fields)
    if (f == l) {
      return {};
    } else if (auto rt = caf::get_if<record_type>(&field.type)) {
      auto ys = unflatten(f, l, *rt);
      if (!ys)
        return ys;
      xs.push_back(std::move(*ys));
    } else {
      xs.push_back(*f++);
    }
  return xs;
}

} // namespace <anonymous>

vector flatten(const vector& xs) {
  auto f = xs.begin();
  auto l = xs.end();
  return flatten(f, l);
}

vector flatten(vector&& xs) {
  auto f = std::make_move_iterator(xs.begin());
  auto l = std::make_move_iterator(xs.end());
  return flatten(f, l);
}

data flatten(const data& x) {
  auto xs = caf::get_if<vector>(&x);
  return xs ? flatten(*xs) : x;
}

data flatten(data&& x) {
  auto xs = caf::get_if<vector>(&x);
  return xs ? flatten(std::move(*xs)) : x;
}

caf::optional<vector> unflatten(const vector& xs, const record_type& rt) {
  auto first = xs.begin();
  auto last = xs.end();
  return unflatten(first, last, rt);
}

caf::optional<vector> unflatten(vector&& xs, const record_type& rt) {
  auto first = std::make_move_iterator(xs.begin());
  auto last = std::make_move_iterator(xs.end());
  return unflatten(first, last, rt);
}

caf::optional<vector> unflatten(const data& x, const type& t) {
  auto xs = caf::get_if<vector>(&x);
  auto rt = caf::get_if<record_type>(&t);
  return xs && rt ? unflatten(*xs, *rt) : caf::optional<vector>{};
}

caf::optional<vector> unflatten(data&& x, const type& t) {
  auto xs = caf::get_if<vector>(&x);
  auto rt = caf::get_if<record_type>(&t);
  return xs && rt ? unflatten(std::move(*xs), *rt) : caf::optional<vector>{};
}

namespace {

json jsonize(const data& x) {
  return caf::visit(detail::overload(
    [&](const auto& y) { return to_json(y); },
    [&](caf::none_t) { return json{}; },
    [&](const std::string& str) { return json{str}; }
  ), x);
}

} // namespace <anonymous>

bool convert(const vector& v, json& j) {
  json::array a(v.size());
  for (auto i = 0u; i < v.size(); ++i)
    a[i] = jsonize(v[i]);
  j = std::move(a);
  return true;
}

bool convert(const set& s, json& j) {
  json::array a(s.size());
  auto i = 0u;
  for (auto& x : s)
    a[i++] = jsonize(x);
  j = std::move(a);
  return true;
}

bool convert(const map& t, json& j) {
  json::array values;
  for (auto& p : t) {
    json::array a;
    a.emplace_back(jsonize(p.first));
    a.emplace_back(jsonize(p.second));
    values.emplace_back(std::move(a));
  };
  j = std::move(values);
  return true;
}

bool convert(const data& d, json& j) {
  j = jsonize(d);
  return true;
}

bool convert(const data& d, json& j, const type& t) {
  auto v = caf::get_if<vector>(&d);
  auto rt = caf::get_if<record_type>(&t);
  if (v && rt) {
    if (v->size() != rt->fields.size())
      return false;
    json::object o;
    for (auto i = 0u; i < v->size(); ++i) {
      auto& f = rt->fields[i];
      if (!convert((*v)[i], o[f.name], f.type))
        return false;
    }
    j = std::move(o);
    return true;
  }
  return convert(d, j);
}

} // namespace vast
<|endoftext|>