Mercurial > irccd
changeset 292:671612cbc721
Irccd: split lib into libirccd-js, #564
line wrap: on
line diff
--- a/CMakeLists.txt Wed Oct 05 13:27:15 2016 +0200 +++ b/CMakeLists.txt Wed Oct 05 20:32:27 2016 +0200 @@ -80,7 +80,11 @@ add_subdirectory(doc) add_subdirectory(libcommon) add_subdirectory(libirccd) -#add_subdirectory(lib) + +if (WITH_JS) + add_subdirectory(libirccd-js) +endif () + #add_subdirectory(irccd) #add_subdirectory(irccdctl) #add_subdirectory(contrib)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/CMakeLists.txt Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,48 @@ +project(libirccd-js) + +set( + HEADERS + ${libirccd-js_SOURCE_DIR}/irccd/duktape.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-directory.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-elapsed-timer.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-file.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-irccd.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-logger.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-plugin.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-server.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-system.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-timer.hpp + ${libirccd-js_SOURCE_DIR}/irccd/module.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-unicode.hpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-util.hpp + ${libirccd-js_SOURCE_DIR}/irccd/plugin-js.hpp + ${libirccd-js_SOURCE_DIR}/irccd/timer.hpp + ${libirccd-js_SOURCE_DIR}/irccd/unicode.hpp +) + +set( + SOURCES + ${libirccd-js_SOURCE_DIR}/irccd/mod-directory.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-elapsed-timer.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-file.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-irccd.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-logger.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-plugin.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-server.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-system.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-timer.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-unicode.cpp + ${libirccd-js_SOURCE_DIR}/irccd/mod-util.cpp + ${libirccd-js_SOURCE_DIR}/irccd/plugin-js.cpp + ${libirccd-js_SOURCE_DIR}/irccd/timer.cpp + ${libirccd-js_SOURCE_DIR}/irccd/unicode.cpp +) + +irccd_define_library( + TARGET libirccd-js + SOURCES + ${libirccd-js_SOURCE_DIR}/CMakeLists.txt + ${HEADERS} + ${SOURCES} + LIBRARIES extern-duktape libirccd +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/duktape.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,484 @@ +/* + * duktape.hpp -- Duktape extras + * + * Copyright (c) 2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_DUKTAPE_HPP +#define IRCCD_DUKTAPE_HPP + +/** + * \file duktape.hpp + * \brief Bring some extras to Duktape C library. + * \author David Demelier <markand@malikania.fr> + */ + +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <duktape.h> + +namespace irccd { + +/** + * \class StackAssert + * \brief Stack sanity checker. + * + * Instanciate this class where you need to manipulate the Duktape stack outside a Duktape/C function, its destructor + * will examinate if the stack size matches the user expected size. + * + * When compiled with NDEBUG, this class does nothing. + * + * To use it, just declare an lvalue at the beginning of your function. + */ +class StackAssert { +#if !defined(NDEBUG) +private: + duk_context *m_context; + unsigned m_expected; + unsigned m_begin; +#endif + +public: + /** + * Create the stack checker. + * + * No-op if NDEBUG is set. + * + * \param ctx the context + * \param expected the size expected relative to the already existing values + */ + inline StackAssert(duk_context *ctx, unsigned expected = 0) noexcept +#if !defined(NDEBUG) + : m_context(ctx) + , m_expected(expected) + , m_begin(static_cast<unsigned>(duk_get_top(ctx))) +#endif + { +#if defined(NDEBUG) + (void)ctx; + (void)expected; +#endif + } + + /** + * Verify the expected size. + * + * No-op if NDEBUG is set. + */ + inline ~StackAssert() noexcept + { +#if !defined(NDEBUG) + if (static_cast<unsigned>(duk_get_top(m_context)) - m_begin != m_expected) { + std::fprintf(stderr, "Corrupt stack detection in StackAssert:\n"); + std::fprintf(stderr, " Size at start: %u\n", m_begin); + std::fprintf(stderr, " Size at end: %d\n", duk_get_top(m_context)); + std::fprintf(stderr, " Expected (user): %u\n", m_expected); + std::fprintf(stderr, " Expected (adjusted): %u\n", m_expected + m_begin); + std::fprintf(stderr, " Number of stale values: %u\n", duk_get_top(m_context) - m_begin - m_expected); + std::abort(); + } +#endif + } +}; + +/** + * \class Exception + * \brief Error description. + * + * This class fills the fields got in an Error object. + */ +class Exception : public std::exception { +public: + std::string name; //!< name of error + std::string message; //!< error message + std::string stack; //!< stack if available + std::string fileName; //!< filename if applicable + int lineNumber{0}; //!< line number if applicable + + /** + * Get the error message. This effectively returns message field. + * + * \return the message + */ + const char *what() const noexcept override + { + return message.c_str(); + } +}; + +/** + * \brief RAII based Duktape handler. + * + * This class is implicitly convertible to duk_context for convenience. + */ +class UniqueContext { +private: + using Deleter = void (*)(duk_context *); + using Handle = std::unique_ptr<duk_context, Deleter>; + + Handle m_handle; + + UniqueContext(const UniqueContext &) = delete; + UniqueContext &operator=(const UniqueContext &) = delete; + +public: + /** + * Create default context. + */ + inline UniqueContext() + : m_handle(duk_create_heap_default(), duk_destroy_heap) + { + } + + /** + * Default move constructor. + */ + UniqueContext(UniqueContext &&) noexcept = default; + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + inline operator duk_context *() noexcept + { + return m_handle.get(); + } + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + inline operator duk_context *() const noexcept + { + return m_handle.get(); + } + + /** + * Default move assignment operator. + * + * \return this + */ + UniqueContext &operator=(UniqueContext &&) noexcept = delete; +}; + +/** + * \class Error + * \brief Base ECMAScript error class. + * \warning Override the function create for your own exceptions + */ +class Error { +private: + int m_type{DUK_ERR_ERROR}; + std::string m_message; + +protected: + /** + * Constructor with a type of error specified, specially designed for derived errors. + * + * \param type of error (e.g. DUK_ERR_ERROR) + * \param message the message + */ + inline Error(int type, std::string message) noexcept + : m_type(type) + , m_message(std::move(message)) + { + } + +public: + /** + * Constructor with a message. + * + * \param message the message + */ + inline Error(std::string message) noexcept + : m_message(std::move(message)) + { + } + + /** + * Create the exception on the stack. + * + * \note the default implementation search for the global variables + * \param ctx the context + */ + virtual void raise(duk_context *ctx) const + { + duk_error(ctx, m_type, "%s", m_message.c_str()); + } +}; + +/** + * \class EvalError + * \brief Error in eval() function. + */ +class EvalError : public Error { +public: + /** + * Construct an EvalError. + * + * \param message the message + */ + inline EvalError(std::string message) noexcept + : Error(DUK_ERR_EVAL_ERROR, std::move(message)) + { + } +}; + +/** + * \class RangeError + * \brief Value is out of range. + */ +class RangeError : public Error { +public: + /** + * Construct an RangeError. + * + * \param message the message + */ + inline RangeError(std::string message) noexcept + : Error(DUK_ERR_RANGE_ERROR, std::move(message)) + { + } +}; + +/** + * \class ReferenceError + * \brief Trying to use a variable that does not exist. + */ +class ReferenceError : public Error { +public: + /** + * Construct an ReferenceError. + * + * \param message the message + */ + inline ReferenceError(std::string message) noexcept + : Error(DUK_ERR_REFERENCE_ERROR, std::move(message)) + { + } +}; + +/** + * \class SyntaxError + * \brief Syntax error in the script. + */ +class SyntaxError : public Error { +public: + /** + * Construct an SyntaxError. + * + * \param message the message + */ + inline SyntaxError(std::string message) noexcept + : Error(DUK_ERR_SYNTAX_ERROR, std::move(message)) + { + } +}; + +/** + * \class TypeError + * \brief Invalid type given. + */ +class TypeError : public Error { +public: + /** + * Construct an TypeError. + * + * \param message the message + */ + inline TypeError(std::string message) noexcept + : Error(DUK_ERR_TYPE_ERROR, std::move(message)) + { + } +}; + +/** + * \class URIError + * \brief URI manipulation failure. + */ +class URIError : public Error { +public: + /** + * Construct an URIError. + * + * \param message the message + */ + inline URIError(std::string message) noexcept + : Error(DUK_ERR_URI_ERROR, std::move(message)) + { + } +}; + +/** + * Get the error object when a JavaScript error has been thrown (e.g. eval failure). + * + * \param ctx the context + * \param index the index + * \param pop if true, also remove the exception from the stack + * \return the information + */ +inline Exception dukx_exception(duk_context *ctx, int index, bool pop = true) +{ + Exception ex; + + index = duk_normalize_index(ctx, index); + + duk_get_prop_string(ctx, index, "name"); + ex.name = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "message"); + ex.message = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "fileName"); + ex.fileName = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "lineNumber"); + ex.lineNumber = duk_to_int(ctx, -1); + duk_get_prop_string(ctx, index, "stack"); + ex.stack = duk_to_string(ctx, -1); + duk_pop_n(ctx, 5); + + if (pop) + duk_remove(ctx, index); + + return ex; +} + +/** + * Enumerate an object or an array at the specified index. + * + * \param ctx the context + * \param index the object or array index + * \param flags the optional flags to pass to duk_enum + * \param getvalue set to true if you want to extract the value + * \param func the function to call for each properties + */ +template <typename Func> +void dukx_enumerate(duk_context *ctx, int index, duk_uint_t flags, duk_bool_t getvalue, Func &&func) +{ + duk_enum(ctx, index, flags); + + while (duk_next(ctx, -1, getvalue)) { + func(ctx); + duk_pop_n(ctx, 1 + (getvalue ? 1 : 0)); + } + + duk_pop(ctx); +} + +/** + * Throw an ECMAScript exception. + * + * \param ctx the context + * \param ex the exception + */ +template <typename Exception> +void dukx_throw(duk_context *ctx, const Exception &ex) +{ + ex.raise(ctx); +} + +/** + * Get a string, return 0 if not a string. + * + * \param ctx the context + * \param index the index + * \return the string + */ +inline std::string dukx_get_std_string(duk_context *ctx, int index) +{ + duk_size_t size; + const char *text = duk_get_lstring(ctx, index, &size); + + return std::string(text, size); +} + +/** + * Require a string, throws a JavaScript exception if not a string. + * + * \param ctx the context + * \param index the index + * \return the string + */ +inline std::string dukx_require_std_string(duk_context *ctx, int index) +{ + duk_size_t size; + const char *text = duk_require_lstring(ctx, index, &size); + + return std::string(text, size); +} + +/** + * Push a C++ string. + * + * \param ctx the context + * \param str the string + */ +inline void dukx_push_std_string(duk_context *ctx, const std::string &str) +{ + duk_push_lstring(ctx, str.data(), str.length()); +} + +/** + * Get an array. + * + * \param ctx the context + * \param index the array index + * \param get the conversion function (e.g. duk_get_int) + */ +template <typename Getter> +auto dukx_get_array(duk_context *ctx, duk_idx_t index, Getter &&get) +{ + using T = decltype(get(ctx, 0)); + + std::vector<T> result; + std::size_t length = duk_get_length(ctx, index); + + for (std::size_t i = 0; i < length; ++i) { + duk_get_prop_index(ctx, -1, i); + result.push_back(get(ctx, -1)); + duk_pop(ctx); + } + + return result; +} + +/** + * Push an array. + * + * \param ctx the context + * \param values the values + * \param push the function to push values + */ +template <typename T, typename Pusher> +void dukx_push_array(duk_context *ctx, const std::vector<T> &values, Pusher &&push) +{ + duk_push_array(ctx); + + int i = 0; + for (auto x : values) { + push(ctx, x); + duk_put_prop_index(ctx, -2, i++); + } +} + +} // !irccd + +#endif // !IRCCD_DUKTAPE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-directory.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,391 @@ +/* + * js-directory.cpp -- Irccd.Directory API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <fstream> +#include <regex> +#include <stdexcept> +#include <string> + +#include "duktape.hpp" +#include "fs.hpp" +#include "mod-directory.hpp" +#include "mod-irccd.hpp" +#include "path.hpp" +#include "plugin-js.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +namespace { + +std::string path(duk_context *ctx) +{ + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, "path"); + + if (duk_get_type(ctx, -1) != DUK_TYPE_STRING) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object"); + + auto ret = dukx_get_std_string(ctx, -1); + + if (ret.empty()) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path"); + + duk_pop_n(ctx, 2); + + return ret; +} + +/* + * Find an entry recursively (or not) in a directory using a predicate which can + * be used to test for regular expression, equality. + * + * Do not use this function directly, use: + * + * - findName + * - findRegex + */ +template <typename Pred> +std::string findPath(const std::string &base, bool recursive, Pred pred) +{ + /* + * For performance reason, we first iterate over all entries that are + * not directories to avoid going deeper recursively if the requested + * file is in the current directory. + */ + auto entries = fs::readdir(base); + + for (const auto &entry : entries) + if (entry.type != fs::Entry::Dir && pred(entry.name)) + return base + entry.name; + + if (!recursive) + return ""; + + for (const auto &entry : entries) { + if (entry.type == fs::Entry::Dir) { + std::string next = base + entry.name + fs::separator(); + std::string path = findPath(next, true, pred); + + if (!path.empty()) + return path; + } + } + + return ""; +} + +/* + * Helper for finding by equality. + */ +std::string findName(std::string base, const std::string &pattern, bool recursive) +{ + return findPath(base, recursive, [&] (const std::string &entryname) -> bool { + return pattern == entryname; + }); +} + +/* + * Helper for finding by regular expression + */ +std::string findRegex(const std::string &base, std::string pattern, bool recursive) +{ + std::regex regexp(pattern, std::regex::ECMAScript); + std::smatch smatch; + + return findPath(base, recursive, [&] (const std::string &entryname) -> bool { + return std::regex_match(entryname, smatch, regexp); + }); +} + +/* + * Generic find function for: + * + * - Directory.find + * - Directory.prototype.find + * + * The patternIndex is the argument where to test if the argument is a regex or + * a string. + */ +duk_ret_t find(duk_context *ctx, std::string base, bool recursive, int patternIndex) +{ + base = path::clean(base); + + try { + std::string path; + + if (duk_is_string(ctx, patternIndex)) + path = findName(base, duk_get_string(ctx, patternIndex), recursive); + else { + // Check if it's a valid RegExp object. + duk_get_global_string(ctx, "RegExp"); + auto isRegex = duk_instanceof(ctx, patternIndex, -1); + duk_pop(ctx); + + if (isRegex) { + duk_get_prop_string(ctx, patternIndex, "source"); + auto pattern = duk_to_string(ctx, -1); + duk_pop(ctx); + + path = findRegex(base, pattern, recursive); + } else + duk_error(ctx, DUK_ERR_TYPE_ERROR, "pattern must be a string or a regex expression"); + } + + if (path.empty()) + return 0; + + dukx_push_std_string(ctx, path); + } catch (const std::exception &ex) { + duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); + } + + return 1; +} + +/* + * Generic remove function for: + * + * - Directory.remove + * - Directory.prototype.remove + */ +duk_ret_t remove(duk_context *ctx, const std::string &path, bool recursive) +{ + if (!fs::isDirectory(path)) + dukx_throw(ctx, SystemError(EINVAL, "not a directory")); + + if (!recursive) { +#if defined(_WIN32) + ::RemoveDirectory(path.c_str()); +#else + ::remove(path.c_str()); +#endif + } else + fs::rmdir(path.c_str()); + + return 0; +} + +/* + * Method: Directory.find(pattern, recursive) + * -------------------------------------------------------- + * + * Synonym of Directory.find(path, pattern, recursive) but the path is taken + * from the directory object. + * + * Arguments: + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined if not found. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodFind(duk_context *ctx) +{ + return find(ctx, path(ctx), duk_get_boolean(ctx, 1), 0); +} + +/* + * Method: Directory.remove(recursive) + * -------------------------------------------------------- + * + * Synonym of Directory.remove(recursive) but the path is taken from the + * directory object. + * + * Arguments: + * - recursive, recursively or not (default: false). + * Throws: + * - Any exception on error. + */ +duk_ret_t methodRemove(duk_context *ctx) +{ + return remove(ctx, path(ctx), duk_get_boolean(ctx, 0)); +} + +const duk_function_list_entry methods[] = { + { "find", methodFind, DUK_VARARGS }, + { "remove", methodRemove, 1 }, + { nullptr, nullptr, 0 } +}; + +/* + * Directory "static" functions + * ------------------------------------------------------------------ + */ + +/* + * Function: Irccd.Directory(path, flags) [constructor] + * -------------------------------------------------------- + * + * Opens and read the directory at the specified path. + * + * Arguments: + * - path, the path to the directory, + * - flags, the optional flags (default: 0). + * Throws: + * - Any exception on error + */ +duk_ret_t constructor(duk_context *ctx) +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + try { + std::string path = duk_require_string(ctx, 0); + std::int8_t flags = duk_get_uint(ctx, 1); + + if (!fs::isDirectory(path)) + dukx_throw(ctx, SystemError(EINVAL, "not a directory")); + + std::vector<fs::Entry> list = fs::readdir(path, flags); + + duk_push_this(ctx); + duk_push_string(ctx, "count"); + duk_push_int(ctx, list.size()); + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk_push_string(ctx, "path"); + dukx_push_std_string(ctx, path); + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + duk_push_string(ctx, "entries"); + duk_push_array(ctx); + + for (unsigned i = 0; i < list.size(); ++i) { + duk_push_object(ctx); + dukx_push_std_string(ctx, list[i].name); + duk_put_prop_string(ctx, -2, "name"); + duk_push_int(ctx, list[i].type); + duk_put_prop_string(ctx, -2, "type"); + duk_put_prop_index(ctx, -2, i); + } + + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + } catch (const std::exception &ex) { + dukx_throw(ctx, SystemError(errno, ex.what())); + } + + return 0; +} + +/* + * Function: Irccd.Directory.find(path, pattern, recursive) + * -------------------------------------------------------- + * + * Find an entry by a pattern or a regular expression. + * + * Arguments: + * - path, the base path, + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined on errors or not found. + */ +duk_ret_t funcFind(duk_context *ctx) +{ + return find(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 2), 1); +} + +/* + * Function: Irccd.Directory.remove(path, recursive) + * -------------------------------------------------------- + * + * Remove the directory optionally recursively. + * + * Arguments: + * - path, the path to the directory, + * - recursive, recursively or not (default: false). + * Throws: + * - Any exception on error. + */ +duk_ret_t funcRemove(duk_context *ctx) +{ + return remove(ctx, duk_require_string(ctx, 0), duk_get_boolean(ctx, 1)); +} + +/* + * Function: Irccd.Directory.mkdir(path, mode = 0700) + * -------------------------------------------------------- + * + * Create a directory specified by path. It will create needed subdirectories + * just like you have invoked mkdir -p. + * + * Arguments: + * - path, the path to the directory, + * - mode, the mode, not available on all platforms. + * Throws: + * - Any exception on error. + */ +duk_ret_t funcMkdir(duk_context *ctx) +{ + try { + fs::mkdir( + duk_require_string(ctx, 0), + duk_is_number(ctx, 1) ? duk_get_int(ctx, 1) : 0700 + ); + } catch (const std::exception &ex) { + dukx_throw(ctx, SystemError(errno, ex.what())); + } + + return 0; +} + +const duk_function_list_entry functions[] = { + { "find", funcFind, DUK_VARARGS }, + { "mkdir", funcMkdir, DUK_VARARGS }, + { "remove", funcRemove, DUK_VARARGS }, + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "Dot", static_cast<int>(fs::Dot) }, + { "DotDot", static_cast<int>(fs::DotDot) }, + { "TypeUnknown", static_cast<int>(fs::Entry::Unknown) }, + { "TypeDir", static_cast<int>(fs::Entry::Dir) }, + { "TypeFile", static_cast<int>(fs::Entry::File) }, + { "TypeLink", static_cast<int>(fs::Entry::Link) }, + { nullptr, 0 } +}; + +} // !namespace + +DirectoryModule::DirectoryModule() noexcept + : Module("Irccd.Directory") +{ +} + +void DirectoryModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_c_function(plugin->context(), constructor, 2); + duk_put_number_list(plugin->context(), -1, constants); + duk_put_function_list(plugin->context(), -1, functions); + dukx_push_std_string(plugin->context(), std::string{fs::separator()}); + duk_put_prop_string(plugin->context(), -2, "separator"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, methods); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "Directory"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-directory.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-directory.hpp -- Irccd.Directory API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_DIRECTORY_HPP +#define IRCCD_MOD_DIRECTORY_HPP + +/** + * \file mod-directory.hpp + * \brief Irccd.Directory JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Directory JavaScript API. + * \ingroup modules + */ +class DirectoryModule : public Module { +public: + /** + * Irccd.Directory. + */ + IRCCD_EXPORT DirectoryModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_DIRECTORY_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-elapsed-timer.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,161 @@ +/* + * js-elapsed-timer.cpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "elapsed-timer.hpp" +#include "mod-elapsed-timer.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +namespace { + +const char *Signature("\xff""\xff""irccd-elapsed-timer-ptr"); + +ElapsedTimer *self(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, Signature); + auto ptr = static_cast<ElapsedTimer *>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); + + return ptr; +} + +/* + * Method: ElapsedTimer.pause + * ------------------------------------------------------------------ + * + * Pause the timer, without resetting the current elapsed time stored. + */ +duk_ret_t pause(duk_context *ctx) +{ + self(ctx)->pause(); + + return 0; +} + +/* + * Method: ElapsedTimer.reset + * ------------------------------------------------------------------ + * + * Reset the elapsed time to 0, the status is not modified. + */ +duk_ret_t reset(duk_context *ctx) +{ + self(ctx)->reset(); + + return 0; +} + +/* + * Method: ElapsedTimer.restart + * ------------------------------------------------------------------ + * + * Restart the timer without resetting the current elapsed time. + */ +duk_ret_t restart(duk_context *ctx) +{ + self(ctx)->restart(); + + return 0; +} + +/* + * Method: ElapsedTimer.elapsed + * ------------------------------------------------------------------ + * + * Get the number of elapsed milliseconds. + * + * Returns: + * The time elapsed. + */ +duk_ret_t elapsed(duk_context *ctx) +{ + duk_push_uint(ctx, self(ctx)->elapsed()); + + return 1; +} + +/* + * Function: Irccd.ElapsedTimer() [constructor] + * ------------------------------------------------------------------ + * + * Construct a new ElapsedTimer object. + */ +duk_ret_t constructor(duk_context *ctx) +{ + duk_push_this(ctx); + duk_push_pointer(ctx, new ElapsedTimer); + duk_put_prop_string(ctx, -2, Signature); + duk_pop(ctx); + + return 0; +} + +/* + * Function: Irccd.ElapsedTimer() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk_ret_t destructor(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, Signature); + delete static_cast<ElapsedTimer *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, Signature); + + return 0; +} + +const duk_function_list_entry methods[] = { + { "elapsed", elapsed, 0 }, + { "pause", pause, 0 }, + { "reset", reset, 0 }, + { "restart", restart, 0 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +ElapsedTimerModule::ElapsedTimerModule() noexcept + : Module("Irccd.ElapsedTimer") +{ +} + +void ElapsedTimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_c_function(plugin->context(), constructor, 0); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, methods); + duk_push_c_function(plugin->context(), destructor, 1); + duk_set_finalizer(plugin->context(), -2); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "ElapsedTimer"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-elapsed-timer.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-elapsed-timer.hpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_ELAPSED_TIMER_HPP +#define IRCCD_MOD_ELAPSED_TIMER_HPP + +/** + * \file mod-elapsed-timer.hpp + * \brief Irccd.ElapsedTimer JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.ElapsedTimer JavaScript API. + * \ingroup modules + */ +class ElapsedTimerModule : public Module { +public: + /** + * Irccd.ElapsedTimer. + */ + IRCCD_EXPORT ElapsedTimerModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_ELAPSED_TIMER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-file.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,693 @@ +/* + * js-file.cpp -- Irccd.File API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <array> +#include <cassert> +#include <iterator> +#include <vector> + +#include "sysconfig.hpp" + +#if defined(HAVE_STAT) +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include "fs.hpp" +#include "mod-file.hpp" +#include "mod-irccd.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +namespace { + +const char *Signature("\xff""\xff""irccd-file-ptr"); +const char *Prototype("\xff""\xff""irccd-file-prototype"); + +#if defined(HAVE_STAT) + +/* + * pushStat + * ------------------------------------------------------------------ + */ + +void pushStat(duk_context *ctx, const struct stat &st) +{ + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + +#if defined(HAVE_STAT_ST_ATIME) + duk_push_int(ctx, st.st_atime); + duk_put_prop_string(ctx, -2, "atime"); +#endif +#if defined(HAVE_STAT_ST_BLKSIZE) + duk_push_int(ctx, st.st_blksize); + duk_put_prop_string(ctx, -2, "blksize"); +#endif +#if defined(HAVE_STAT_ST_BLOCKS) + duk_push_int(ctx, st.st_blocks); + duk_put_prop_string(ctx, -2, "blocks"); +#endif +#if defined(HAVE_STAT_ST_CTIME) + duk_push_int(ctx, st.st_ctime); + duk_put_prop_string(ctx, -2, "ctime"); +#endif +#if defined(HAVE_STAT_ST_DEV) + duk_push_int(ctx, st.st_dev); + duk_put_prop_string(ctx, -2, "dev"); +#endif +#if defined(HAVE_STAT_ST_GID) + duk_push_int(ctx, st.st_gid); + duk_put_prop_string(ctx, -2, "gid"); +#endif +#if defined(HAVE_STAT_ST_INO) + duk_push_int(ctx, st.st_ino); + duk_put_prop_string(ctx, -2, "ino"); +#endif +#if defined(HAVE_STAT_ST_MODE) + duk_push_int(ctx, st.st_mode); + duk_put_prop_string(ctx, -2, "mode"); +#endif +#if defined(HAVE_STAT_ST_MTIME) + duk_push_int(ctx, st.st_mtime); + duk_put_prop_string(ctx, -2, "mtime"); +#endif +#if defined(HAVE_STAT_ST_NLINK) + duk_push_int(ctx, st.st_nlink); + duk_put_prop_string(ctx, -2, "nlink"); +#endif +#if defined(HAVE_STAT_ST_RDEV) + duk_push_int(ctx, st.st_rdev); + duk_put_prop_string(ctx, -2, "rdev"); +#endif +#if defined(HAVE_STAT_ST_SIZE) + duk_push_int(ctx, st.st_size); + duk_put_prop_string(ctx, -2, "size"); +#endif +#if defined(HAVE_STAT_ST_UID) + duk_push_int(ctx, st.st_uid); + duk_put_prop_string(ctx, -2, "uid"); +#endif +} + +#endif // !HAVE_STAT + +// Remove trailing \r for CRLF line style. +inline std::string clearCr(std::string input) +{ + if (input.length() > 0 && input.back() == '\r') + input.pop_back(); + + return input; +} + +File *self(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, Signature); + auto ptr = static_cast<File *>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + return ptr; +} + +/* + * File methods. + * ------------------------------------------------------------------ + */ + +/* + * Method: File.basename() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.basename(path)` but with the path from the file. + * + * duk_ret_turns: + * The base name. + */ +duk_ret_t methodBasename(duk_context *ctx) +{ + dukx_push_std_string(ctx, fs::baseName(self(ctx)->path())); + + return 1; +} + +/* + * Method: File.close() + * -------------------------------------------------------- + * + * Force close of the file, automatically called when object is collected. + */ +duk_ret_t methodClose(duk_context *ctx) +{ + self(ctx)->close(); + + return 0; +} + +/* + * Method: File.dirname() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.dirname(path)` but with the path from the file. + * + * duk_ret_turns: + * The directory name. + */ +duk_ret_t methodDirname(duk_context *ctx) +{ + dukx_push_std_string(ctx, fs::dirName(self(ctx)->path())); + + return 1; +} + +/* + * Method: File.lines() + * -------------------------------------------------------- + * + * Read all lines and return an array. + * + * duk_ret_turns: + * An array with all lines. + * Throws + * - Any exception on error. + */ +duk_ret_t methodLines(duk_context *ctx) +{ + duk_push_array(ctx); + + std::FILE *fp = self(ctx)->handle(); + std::string buffer; + std::array<char, 128> data; + std::int32_t i = 0; + + while (std::fgets(&data[0], data.size(), fp) != nullptr) { + buffer += data.data(); + + auto pos = buffer.find('\n'); + + if (pos != std::string::npos) { + dukx_push_std_string(ctx, clearCr(buffer.substr(0, pos))); + duk_put_prop_index(ctx, -2, i++); + + buffer.erase(0, pos + 1); + } + } + + // Maybe an error in the stream. + if (std::ferror(fp)) + dukx_throw(ctx, SystemError()); + + // Missing '\n' in end of file. + if (!buffer.empty()) { + dukx_push_std_string(ctx, clearCr(buffer)); + duk_put_prop_index(ctx, -2, i++); + } + + return 1; +} + +/* + * Method: File.read(amount) + * -------------------------------------------------------- + * + * Read the specified amount of characters or the whole file. + * + * Arguments: + * - amount, the amount of characters or -1 to read all (Optional, default: -1). + * duk_ret_turns: + * The string. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodRead(duk_context *ctx) +{ + auto file = self(ctx); + auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; + + if (amount == 0 || file->handle() == nullptr) + return 0; + + try { + std::string data; + std::size_t total = 0; + + if (amount < 0) { + std::array<char, 128> buffer; + std::size_t nread; + + while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), file->handle())) > 0) { + if (std::ferror(file->handle())) + dukx_throw(ctx, SystemError()); + + std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); + total += nread; + } + } else { + data.resize((std::size_t)amount); + total = std::fread(&data[0], sizeof (data[0]), (std::size_t)amount, file->handle()); + + if (std::ferror(file->handle())) + dukx_throw(ctx, SystemError()); + + data.resize(total); + } + + dukx_push_std_string(ctx, data); + } catch (const std::exception &) { + dukx_throw(ctx, SystemError()); + } + + return 1; +} + +/* + * Method: File.readline() + * -------------------------------------------------------- + * + * Read the next line available. + * + * duk_ret_turns: + * The next line or undefined if eof. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodReadline(duk_context *ctx) +{ + std::FILE *fp = self(ctx)->handle(); + std::string result; + + if (fp == nullptr || std::feof(fp)) + return 0; + for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) + result += (char)ch; + if (std::ferror(fp)) + dukx_throw(ctx, SystemError()); + + dukx_push_std_string(ctx, clearCr(result)); + + return 1; +} + +/* + * Method: File.remove() + * -------------------------------------------------------- + * + * Synonym of File.remove(path) but with the path from the file. + * + * Throws: + * - Any exception on error. + */ +duk_ret_t methodRemove(duk_context *ctx) +{ + if (::remove(self(ctx)->path().c_str()) < 0) + dukx_throw(ctx, SystemError()); + + return 0; +} + +/* + * Method: File.seek(type, amount) + * -------------------------------------------------------- + * + * Sets the position in the file. + * + * Arguments: + * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), + * - amount, the new offset. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodSeek(duk_context *ctx) +{ + auto fp = self(ctx)->handle(); + auto type = duk_require_int(ctx, 0); + auto amount = duk_require_int(ctx, 1); + + if (fp != nullptr && std::fseek(fp, amount, type) != 0) + dukx_throw(ctx, SystemError()); + + return 0; +} + +#if defined(HAVE_STAT) + +/* + * Method: File.stat() [optional] + * -------------------------------------------------------- + * + * Synonym of File.stat(path) but with the path from the file. + * + * duk_ret_turns: + * The stat information. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodStat(duk_context *ctx) +{ + auto file = self(ctx); + struct stat st; + + if (file->handle() == nullptr && ::stat(file->path().c_str(), &st) < 0) + dukx_throw(ctx, SystemError()); + else + pushStat(ctx, st); + + return 1; +} + +#endif // !HAVE_STAT + +/* + * Method: File.tell() + * -------------------------------------------------------- + * + * Get the actual position in the file. + * + * duk_ret_turns: + * The position. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodTell(duk_context *ctx) +{ + auto fp = self(ctx)->handle(); + long pos; + + if (fp == nullptr) + return 0; + + if ((pos = std::ftell(fp)) == -1L) + dukx_throw(ctx, SystemError()); + else + duk_push_int(ctx, pos); + + return 1; +} + +/* + * Method: File.write(data) + * -------------------------------------------------------- + * + * Write some characters to the file. + * + * Arguments: + * - data, the character to write. + * duk_ret_turns: + * The number of bytes written. + * Throws: + * - Any exception on error. + */ +duk_ret_t methodWrite(duk_context *ctx) +{ + std::FILE *fp = self(ctx)->handle(); + std::string data = duk_require_string(ctx, 0); + + if (fp == nullptr) + return 0; + + std::size_t nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); + + if (std::ferror(fp)) + dukx_throw(ctx, SystemError()); + + duk_push_uint(ctx, nwritten); + + return 1; +} + +const duk_function_list_entry methods[] = { + { "basename", methodBasename, 0 }, + { "close", methodClose, 0 }, + { "dirname", methodDirname, 0 }, + { "lines", methodLines, 0 }, + { "read", methodRead, 1 }, + { "readline", methodReadline, 0 }, + { "remove", methodRemove, 0 }, + { "seek", methodSeek, 2 }, +#if defined(HAVE_STAT) + { "stat", methodStat, 0 }, +#endif + { "tell", methodTell, 0 }, + { "write", methodWrite, 1 }, + { nullptr, nullptr, 0 } +}; + +/* + * File "static" functions + * ------------------------------------------------------------------ + */ + +/* + * Function: Irccd.File(path, mode) [constructor] + * -------------------------------------------------------- + * + * Open a file specified by path with the specified mode. + * + * Arguments: + * - path, the path to the file, + * - mode, the mode string. + * Throws: + * - Any exception on error. + */ +duk_ret_t constructor(duk_context *ctx) +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + try { + dukx_new_file(ctx, new File(duk_require_string(ctx, 0), duk_require_string(ctx, 1))); + } catch (const std::exception &) { + dukx_throw(ctx, SystemError()); + } + + return 0; +} + +/* + * Function: Irccd.File() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk_ret_t destructor(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, Signature); + delete static_cast<File *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, Signature); + + return 0; +} + +/* + * Function: Irccd.File.basename(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file basename as specified in `basename(3)` C function. + * + * Arguments: + * - path, the path to the file. + * duk_ret_turns: + * The base name. + */ +duk_ret_t functionBasename(duk_context *ctx) +{ + dukx_push_std_string(ctx, fs::baseName(duk_require_string(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.File.dirname(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file directory name as specified in `dirname(3)` C function. + * + * Arguments: + * - path, the path to the file. + * duk_ret_turns: + * The directory name. + */ +duk_ret_t functionDirname(duk_context *ctx) +{ + dukx_push_std_string(ctx, fs::dirName(duk_require_string(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.File.exists(path) + * -------------------------------------------------------- + * + * Check if the file exists. + * + * Arguments: + * - path, the path to the file. + * duk_ret_turns: + * True if exists. + * Throws: + * - Any exception if we don't have access. + */ +duk_ret_t functionExists(duk_context *ctx) +{ + duk_push_boolean(ctx, fs::exists(duk_require_string(ctx, 0))); + + return 1; +} + +/* + * function Irccd.File.remove(path) + * -------------------------------------------------------- + * + * Remove the file at the specified path. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Any exception on error. + */ +duk_ret_t functionRemove(duk_context *ctx) +{ + if (::remove(duk_require_string(ctx, 0)) < 0) + dukx_throw(ctx, SystemError()); + + return 0; +} + +#if defined(HAVE_STAT) + +/* + * function Irccd.File.stat(path) [optional] + * -------------------------------------------------------- + * + * Get file information at the specified path. + * + * Arguments: + * - path, the path to the file. + * duk_ret_turns: + * The stat information. + * Throws: + * - Any exception on error. + */ +duk_ret_t functionStat(duk_context *ctx) +{ + struct stat st; + + if (::stat(duk_require_string(ctx, 0), &st) < 0) + dukx_throw(ctx, SystemError()); + + pushStat(ctx, st); + + return 1; +} + +#endif // !HAVE_STAT + +const duk_function_list_entry functions[] = { + { "basename", functionBasename, 1 }, + { "dirname", functionDirname, 1 }, + { "exists", functionExists, 1 }, + { "remove", functionRemove, 1 }, +#if defined(HAVE_STAT) + { "stat", functionStat, 1 }, +#endif + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "SeekCur", SEEK_CUR }, + { "SeekEnd", SEEK_END }, + { "SeekSet", SEEK_SET }, + { nullptr, 0 } +}; + +} // !namespace + +FileModule::FileModule() noexcept + : Module("Irccd.File") +{ +} + +void FileModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_c_function(plugin->context(), constructor, 2); + duk_put_number_list(plugin->context(), -1, constants); + duk_put_function_list(plugin->context(), -1, functions); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, methods); + duk_push_c_function(plugin->context(), destructor, 1); + duk_set_finalizer(plugin->context(), -2); + duk_dup(plugin->context(), -1); + duk_put_global_string(plugin->context(), Prototype); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "File"); + duk_pop(plugin->context()); +} + +void dukx_new_file(duk_context *ctx, File *fp) +{ + assert(ctx); + assert(fp); + + StackAssert sa(ctx); + + duk_push_this(ctx); + duk_push_pointer(ctx, fp); + duk_put_prop_string(ctx, -2, Signature); + duk_pop(ctx); +} + +void dukx_push_file(duk_context *ctx, File *fp) +{ + assert(ctx); + assert(fp); + + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, fp); + duk_put_prop_string(ctx, -2, Signature); + duk_get_global_string(ctx, Prototype); + duk_set_prototype(ctx, -2); +} + +File *dukx_require_file(duk_context *ctx, duk_idx_t index) +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, Signature)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + duk_get_prop_string(ctx, index, Signature); + File *file = static_cast<File *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return file; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-file.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,185 @@ +/* + * mod-file.hpp -- Irccd.File API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_FILE_HPP +#define IRCCD_MOD_FILE_HPP + +/** + * \file mod-file.hpp + * \brief Irccd.File JavaScript API. + */ + +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <functional> +#include <stdexcept> +#include <string> + +#include "duktape.hpp" +#include "module.hpp" + +namespace irccd { + +/** + * \class File + * \brief Object for Javascript to perform I/O. + * + * This class can be constructed to Javascript. + * + * It is used in: + * + * - Irccd.File [constructor] + * - Irccd.System.popen (optional) + */ +class File { +private: + File(const File &) = delete; + File &operator=(const File &) = delete; + + File(File &&) = delete; + File &operator=(File &&) = delete; + +private: + std::string m_path; + std::FILE *m_stream; + std::function<void (std::FILE *)> m_destructor; + +public: + /** + * Construct a file specified by path + * + * \param path the path + * \param mode the mode string (for std::fopen) + * \throw std::runtime_error on failures + */ + inline File(std::string path, const std::string &mode) + : m_path(std::move(path)) + , m_destructor([] (std::FILE *fp) { std::fclose(fp); }) + { + if ((m_stream = std::fopen(m_path.c_str(), mode.c_str())) == nullptr) + throw std::runtime_error(std::strerror(errno)); + } + + /** + * Construct a file from a already created FILE pointer (e.g. popen). + * + * The class takes ownership of fp and will close it. + * + * \pre destructor must not be null + * \param fp the file pointer + * \param destructor the function to close fp (e.g. std::fclose) + */ + inline File(std::FILE *fp, std::function<void (std::FILE *)> destructor) noexcept + : m_stream(fp) + , m_destructor(std::move(destructor)) + { + assert(m_destructor != nullptr); + } + + /** + * Closes the file. + */ + virtual ~File() noexcept + { + close(); + } + + /** + * Get the path. + * + * \return the path + * \warning empty when constructed from the FILE constructor + */ + inline const std::string &path() const noexcept + { + return m_path; + } + + /** + * Get the handle. + * + * \return the handle or nullptr if the stream was closed + */ + inline std::FILE *handle() noexcept + { + return m_stream; + } + + /** + * Force close, can be safely called multiple times. + */ + inline void close() noexcept + { + if (m_stream) { + m_destructor(m_stream); + m_stream = nullptr; + } + } +}; + +/** + * \brief Irccd.File JavaScript API. + * \ingroup modules + */ +class FileModule : public Module { +public: + /** + * Irccd.File. + */ + IRCCD_EXPORT FileModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +/** + * Construct the file as this. + * + * The object prototype takes ownership of fp and will be deleted once + * collected. + * + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file + */ +IRCCD_EXPORT void dukx_new_file(duk_context *ctx, File *fp); + +/** + * Push a file. + * + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file + */ +IRCCD_EXPORT void dukx_push_file(duk_context *ctx, File *fp); + +/** + * Require a file. Raises a JavaScript error if not a File. + * + * \param ctx the context + * \param index the index + */ +IRCCD_EXPORT File *dukx_require_file(duk_context *ctx, duk_idx_t index); + +} // !irccd + +#endif // !IRCCD_MOD_FILE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-irccd.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,211 @@ +/* + * js-irccd.cpp -- Irccd API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cerrno> +#include <string> +#include <unordered_map> + +#include "mod-irccd.hpp" +#include "plugin-js.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +namespace { + +const std::unordered_map<std::string, int> errors{ + { "E2BIG", E2BIG }, + { "EACCES", EACCES }, + { "EADDRINUSE", EADDRINUSE }, + { "EADDRNOTAVAIL", EADDRNOTAVAIL }, + { "EAFNOSUPPORT", EAFNOSUPPORT }, + { "EAGAIN", EAGAIN }, + { "EALREADY", EALREADY }, + { "EBADF", EBADF }, + { "EBADMSG", EBADMSG }, + { "EBUSY", EBUSY }, + { "ECANCELED", ECANCELED }, + { "ECHILD", ECHILD }, + { "ECONNABORTED", ECONNABORTED }, + { "ECONNREFUSED", ECONNREFUSED }, + { "ECONNRESET", ECONNRESET }, + { "EDEADLK", EDEADLK }, + { "EDESTADDRREQ", EDESTADDRREQ }, + { "EDOM", EDOM }, + { "EEXIST", EEXIST }, + { "EFAULT", EFAULT }, + { "EFBIG", EFBIG }, + { "EHOSTUNREACH", EHOSTUNREACH }, + { "EIDRM", EIDRM }, + { "EILSEQ", EILSEQ }, + { "EINPROGRESS", EINPROGRESS }, + { "EINTR", EINTR }, + { "EINVAL", EINVAL }, + { "EIO", EIO }, + { "EISCONN", EISCONN }, + { "EISDIR", EISDIR }, + { "ELOOP", ELOOP }, + { "EMFILE", EMFILE }, + { "EMLINK", EMLINK }, + { "EMSGSIZE", EMSGSIZE }, + { "ENAMETOOLONG", ENAMETOOLONG }, + { "ENETDOWN", ENETDOWN }, + { "ENETRESET", ENETRESET }, + { "ENETUNREACH", ENETUNREACH }, + { "ENFILE", ENFILE }, + { "ENOBUFS", ENOBUFS }, + { "ENODATA", ENODATA }, + { "ENODEV", ENODEV }, + { "ENOENT", ENOENT }, + { "ENOEXEC", ENOEXEC }, + { "ENOLCK", ENOLCK }, + { "ENOLINK", ENOLINK }, + { "ENOMEM", ENOMEM }, + { "ENOMSG", ENOMSG }, + { "ENOPROTOOPT", ENOPROTOOPT }, + { "ENOSPC", ENOSPC }, + { "ENOSR", ENOSR }, + { "ENOSTR", ENOSTR }, + { "ENOSYS", ENOSYS }, + { "ENOTCONN", ENOTCONN }, + { "ENOTDIR", ENOTDIR }, + { "ENOTEMPTY", ENOTEMPTY }, + { "ENOTRECOVERABLE", ENOTRECOVERABLE }, + { "ENOTSOCK", ENOTSOCK }, + { "ENOTSUP", ENOTSUP }, + { "ENOTTY", ENOTTY }, + { "ENXIO", ENXIO }, + { "EOPNOTSUPP", EOPNOTSUPP }, + { "EOVERFLOW", EOVERFLOW }, + { "EOWNERDEAD", EOWNERDEAD }, + { "EPERM", EPERM }, + { "EPIPE", EPIPE }, + { "EPROTO", EPROTO }, + { "EPROTONOSUPPORT", EPROTONOSUPPORT }, + { "EPROTOTYPE", EPROTOTYPE }, + { "ERANGE", ERANGE }, + { "EROFS", EROFS }, + { "ESPIPE", ESPIPE }, + { "ESRCH", ESRCH }, + { "ETIME", ETIME }, + { "ETIMEDOUT", ETIMEDOUT }, + { "ETXTBSY", ETXTBSY }, + { "EWOULDBLOCK", EWOULDBLOCK }, + { "EXDEV", EXDEV } +}; + +duk_ret_t constructor(duk_context *ctx) +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "errno"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "SystemError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +} // !namespace + +SystemError::SystemError() + : m_errno(errno) + , m_message(std::strerror(m_errno)) +{ +} + +SystemError::SystemError(int e, std::string message) + : m_errno(e) + , m_message(std::move(message)) +{ +} + +void SystemError::raise(duk_context *ctx) const +{ + StackAssert sa(ctx, 0); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "SystemError"); + duk_remove(ctx, -2); + duk_push_int(ctx, m_errno); + dukx_push_std_string(ctx, m_message); + duk_new(ctx, 2); + duk_throw(ctx); +} + +IrccdModule::IrccdModule() noexcept + : Module("Irccd") +{ +} + +void IrccdModule::load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + // Irccd. + duk_push_object(plugin->context()); + + // Version. + duk_push_object(plugin->context()); + duk_push_int(plugin->context(), IRCCD_VERSION_MAJOR); + duk_put_prop_string(plugin->context(), -2, "major"); + duk_push_int(plugin->context(), IRCCD_VERSION_MINOR); + duk_put_prop_string(plugin->context(), -2, "minor"); + duk_push_int(plugin->context(), IRCCD_VERSION_PATCH); + duk_put_prop_string(plugin->context(), -2, "patch"); + duk_put_prop_string(plugin->context(), -2, "version"); + + // Create the SystemError that inherits from Error. + duk_push_c_function(plugin->context(), constructor, 2); + + // Put errno codes into the Irccd.SystemError object. + for (const auto &pair : errors) { + duk_push_int(plugin->context(), pair.second); + duk_put_prop_string(plugin->context(), -2, pair.first.c_str()); + } + + duk_push_object(plugin->context()); + duk_get_global_string(plugin->context(), "Error"); + duk_get_prop_string(plugin->context(), -1, "prototype"); + duk_remove(plugin->context(), -2); + duk_set_prototype(plugin->context(), -2); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "SystemError"); + + // Set Irccd as global. + duk_put_global_string(plugin->context(), "Irccd"); + + // Store global instance. + duk_push_pointer(plugin->context(), &irccd); + duk_put_global_string(plugin->context(), "\xff""\xff""irccd-ref"); +} + +Irccd &dukx_get_irccd(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_get_global_string(ctx, "\xff""\xff""irccd-ref"); + Irccd *irccd = static_cast<Irccd *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *irccd; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-irccd.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,93 @@ +/* + * mod-irccd.hpp -- Irccd API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_IRCCD_HPP +#define IRCCD_MOD_IRCCD_HPP + +/** + * \file mod-irccd.hpp + * \brief Irccd JavaScript API. + */ + +#include <cerrno> +#include <cstring> +#include <string> + +#include "duktape.hpp" +#include "module.hpp" + +namespace irccd { + +/** + * \brief Custom JavaScript exception for system error. + */ +class SystemError { +private: + int m_errno; + std::string m_message; + +public: + /** + * Create a system error from the current errno value. + */ + IRCCD_EXPORT SystemError(); + + /** + * Create a system error with the given errno and message. + * + * \param e the errno number + * \param message the message + */ + IRCCD_EXPORT SystemError(int e, std::string message); + + /** + * Raise the SystemError. + * + * \param ctx the context + */ + IRCCD_EXPORT void raise(duk_context *ctx) const; +}; + +/** + * \brief Irccd JavaScript API. + * \ingroup modules + */ +class IrccdModule : public Module { +public: + /** + * Irccd. + */ + IRCCD_EXPORT IrccdModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +/** + * Get irccd instance stored in this context. + * + * \param ctx the context + * \return the irccd reference + */ +Irccd &dukx_get_irccd(duk_context *ctx); + +} // !irccd + +#endif // !IRCCD_MOD_IRCCD_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-logger.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,102 @@ +/* + * mod-logger.cpp -- Irccd.Logger API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mod-logger.hpp" +#include "mod-plugin.hpp" +#include "logger.hpp" +#include "plugin-js.hpp" + +namespace irccd { + +namespace { + +duk_ret_t print(duk_context *ctx, std::ostream &out) +{ + out << "plugin " << dukx_get_plugin(ctx)->name() << ": " << duk_require_string(ctx, 0) << std::endl; + + return 0; +} + +/* + * Function: Irccd.Logger.info(message) + * -------------------------------------------------------- + * + * Write a verbose message. + * + * Arguments: + * - message, the message. + */ +duk_ret_t info(duk_context *ctx) +{ + return print(ctx, log::info()); +} + +/* + * Function: Irccd.Logger.warning(message) + * -------------------------------------------------------- + * + * Write a warning message. + * + * Arguments: + * - message, the warning. + */ +duk_ret_t warning(duk_context *ctx) +{ + return print(ctx, log::warning()); +} + +/* + * Function: Logger.debug(message) + * -------------------------------------------------------- + * + * Write a debug message, only shown if irccd is compiled in debug. + * + * Arguments: + * - message, the message. + */ +duk_ret_t debug(duk_context *ctx) +{ + return print(ctx, log::debug()); +} + +const duk_function_list_entry functions[] = { + { "info", info, 1 }, + { "warning", warning, 1 }, + { "debug", debug, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +LoggerModule::LoggerModule() noexcept + : Module("Irccd.Logger") +{ +} + +void LoggerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, functions); + duk_put_prop_string(plugin->context(), -2, "Logger"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-logger.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-logger.hpp -- Irccd.Logger API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_LOGGER_HPP +#define IRCCD_MOD_LOGGER_HPP + +/** + * \file mod-logger.hpp + * \brief Irccd.Logger JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Logger JavaScript API. + * \ingroup modules + */ +class LoggerModule : public Module { +public: + /** + * Irccd.Logger. + */ + IRCCD_EXPORT LoggerModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_LOGGER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-plugin.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,356 @@ +/* + * js-plugin.cpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "irccd.hpp" +#include "plugin-js.hpp" +#include "service-plugin.hpp" +#include "mod-irccd.hpp" +#include "mod-plugin.hpp" + +namespace irccd { + +namespace { + +const char PluginGlobal[] = "\xff""\xff""irccd-plugin-ptr"; + +/* + * wrap + * ------------------------------------------------------------------ + * + * Wrap function for these functions because they all takes the same arguments. + * + * - load, + * - reload, + * - unload. + */ +template <typename Func> +duk_idx_t wrap(duk_context *ctx, int nret, Func &&func) +{ + std::string name = duk_require_string(ctx, 0); + + try { + func(dukx_get_irccd(ctx), name); + } catch (const std::out_of_range &ex) { + dukx_throw(ctx, ReferenceError(ex.what())); + } catch (const std::exception &ex) { + dukx_throw(ctx, Error(ex.what())); + } + + return nret; +} + +/* + * set + * ------------------------------------------------------------------ + * + * This setter is used to replace the Irccd.Plugin.(config|format) property when + * the plugin assign a new one. + * + * Because the plugin configuration always has higher priority, when a new + * object is assigned to 'config' or to the 'format' property, the plugin + * configuration is merged to the assigned one, adding or replacing any values. + * + * Example: + * + * Plugin 'xyz' does: + * + * Irccd.Plugin.config = { + * mode: "simple", + * level: "123" + * }; + * + * The user configuration is: + * + * [plugin.xyz] + * mode = "hard" + * path = "/var" + * + * The final user table looks like this: + * + * Irccd.Plugin.config = { + * mode: "hard", + * level: "123", + * path: "/var" + */ +duk_ret_t set(duk_context *ctx, const char *name) +{ + if (!duk_is_object(ctx, 0)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name); + + // Merge old table with new one. + duk_get_global_string(ctx, name); + duk_enum(ctx, -1, 0); + + while (duk_next(ctx, -1, true)) + duk_put_prop(ctx, 0); + + // Pop enum and old table. + duk_pop_2(ctx); + + // Replace the old table with the new assigned one. + duk_put_global_string(ctx, name); + + return 0; +} + +/* + * get + * ------------------------------------------------------------------ + * + * Get the Irccd.Plugin.(config|format) property. + */ +duk_ret_t get(duk_context *ctx, const char *name) +{ + duk_get_global_string(ctx, name); + + return 1; +} + +/* + * setConfig + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.config property. + */ +duk_ret_t setConfig(duk_context *ctx) +{ + return set(ctx, JsPlugin::ConfigProperty); +} + +/* + * getConfig + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.config property. + */ +duk_ret_t getConfig(duk_context *ctx) +{ + return get(ctx, JsPlugin::ConfigProperty); +} + +/* + * setFormat + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.format property. + */ +duk_ret_t setFormat(duk_context *ctx) +{ + return set(ctx, JsPlugin::FormatProperty); +} + +/* + * getFormat + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.format property. + */ +duk_ret_t getFormat(duk_context *ctx) +{ + return get(ctx, JsPlugin::FormatProperty); +} + +/* + * Function: Irccd.Plugin.info([name]) + * ------------------------------------------------------------------ + * + * Get information about a plugin. + * + * The returned object as the following properties: + * + * - name: (string) the plugin identifier, + * - author: (string) the author, + * - license: (string) the license, + * - summary: (string) a short description, + * - version: (string) the version + * + * Arguments: + * - name, the plugin identifier, if not specified the current plugin is + * selected. + * Returns: + * The plugin information or undefined if the plugin was not found. + */ +duk_idx_t info(duk_context *ctx) +{ + std::shared_ptr<Plugin> plugin; + + if (duk_get_top(ctx) >= 1) + plugin = dukx_get_irccd(ctx).plugins().get(duk_require_string(ctx, 0)); + else + plugin = dukx_get_plugin(ctx); + + if (!plugin) + return 0; + + duk_push_object(ctx); + dukx_push_std_string(ctx, plugin->name()); + duk_put_prop_string(ctx, -2, "name"); + dukx_push_std_string(ctx, plugin->author()); + duk_put_prop_string(ctx, -2, "author"); + dukx_push_std_string(ctx, plugin->license()); + duk_put_prop_string(ctx, -2, "license"); + dukx_push_std_string(ctx, plugin->summary()); + duk_put_prop_string(ctx, -2, "summary"); + dukx_push_std_string(ctx, plugin->version()); + duk_put_prop_string(ctx, -2, "version"); + + return 1; +} + +/* + * Function: Irccd.Plugin.list() + * ------------------------------------------------------------------ + * + * Get the list of plugins, the array returned contains all plugin names. + * + * Returns: + * The list of all plugin names. + */ +duk_idx_t list(duk_context *ctx) +{ + dukx_push_array(ctx, dukx_get_irccd(ctx).plugins().list(), [] (auto ctx, auto plugin) { + dukx_push_std_string(ctx, plugin->name()); + }); + + return 1; +} + +/* + * Function: Irccd.Plugin.load(name) + * ------------------------------------------------------------------ + * + * Load a plugin by name. This function will search through the standard + * directories. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk_idx_t load(duk_context *ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.plugins().load(name); + }); +} + +/* + * Function: Irccd.Plugin.reload(name) + * ------------------------------------------------------------------ + * + * Reload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk_idx_t reload(duk_context *ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.plugins().reload(name); + }); +} + +/* + * Function: Irccd.Plugin.unload(name) + * ------------------------------------------------------------------ + * + * Unload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Error on errors, + * - ReferenceError if the plugin was not found. + */ +duk_idx_t unload(duk_context *ctx) +{ + return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { + irccd.plugins().unload(name); + }); +} + +const duk_function_list_entry functions[] = { + { "info", info, DUK_VARARGS }, + { "list", list, 0 }, + { "load", load, 1 }, + { "reload", reload, 1 }, + { "unload", unload, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +PluginModule::PluginModule() noexcept + : Module("Irccd.Plugin") +{ +} + +void PluginModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_push_pointer(plugin->context(), new std::shared_ptr<JsPlugin>(plugin)); + duk_put_global_string(plugin->context(), PluginGlobal); + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, functions); + + // 'config' property. + duk_push_string(plugin->context(), "config"); + duk_push_c_function(plugin->context(), getConfig, 0); + duk_push_c_function(plugin->context(), setConfig, 1); + duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // 'format' property. + duk_push_string(plugin->context(), "format"); + duk_push_c_function(plugin->context(), getFormat, 0); + duk_push_c_function(plugin->context(), setFormat, 1); + duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + duk_put_prop_string(plugin->context(), -2, "Plugin"); + duk_pop(plugin->context()); +} + +void PluginModule::unload(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_push_global_object(plugin->context()); + duk_get_prop_string(plugin->context(), -1, PluginGlobal); + delete static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(plugin->context(), -1)); + duk_pop(plugin->context()); + duk_del_prop_string(plugin->context(), -1, PluginGlobal); + duk_pop(plugin->context()); +} + +std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_get_global_string(ctx, PluginGlobal); + auto plugin = static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *plugin; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-plugin.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,64 @@ +/* + * mod-plugin.hpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_PLUGIN_HPP +#define IRCCD_MOD_PLUGIN_HPP + +/** + * \file mod-plugin.hpp + * \brief Irccd.Plugin JavaScript API. + */ + +#include "duktape.hpp" +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Plugin JavaScript API. + * \ingroup modules + */ +class PluginModule : public Module { +public: + /** + * Irccd.Plugin. + */ + IRCCD_EXPORT PluginModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; + + /** + * \copydoc Module::unload + */ + IRCCD_EXPORT void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +/** + * Access the plugin stored in this context. + * + * \param ctx the context + * \return the plugin + */ +std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx); + +} // !irccd + +#endif // !IRCCD_MOD_PLUGIN_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-server.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,580 @@ +/* + * js-server.cpp -- Irccd.Server API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <sstream> +#include <unordered_map> + +#include "irccd.hpp" +#include "mod-irccd.hpp" +#include "mod-server.hpp" +#include "plugin-js.hpp" +#include "server.hpp" +#include "service-server.hpp" + +namespace irccd { + +namespace { + +const char *Signature("\xff""\xff""irccd-server-ptr"); +const char *Prototype("\xff""\xff""irccd-server-prototype"); + +std::shared_ptr<Server> self(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, Signature); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + return *static_cast<std::shared_ptr<Server> *>(ptr); +} + +/* + * Method: Server.cmode(channel, mode) + * ------------------------------------------------------------------ + * + * Change a channel mode. + * + * Arguments: + * - channel, the channel, + * - mode, the mode. + */ +duk_ret_t cmode(duk_context *ctx) +{ + self(ctx)->cmode(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.cnotice(channel, message) + * ------------------------------------------------------------------ + * + * Send a channel notice. + * + * Arguments: + * - channel, the channel, + * - message, the message. + */ +duk_ret_t cnotice(duk_context *ctx) +{ + self(ctx)->cnotice(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.info() + * ------------------------------------------------------------------ + * + * Get the server information as an object containing the following properties: + * + * name: the server unique name + * host: the host name + * port: the port number + * ssl: true if using ssl + * sslVerify: true if ssl was verified + * channels: an array of all channels + */ +duk_ret_t info(duk_context *ctx) +{ + auto server = self(ctx); + + duk_push_object(ctx); + dukx_push_std_string(ctx, server->name()); + duk_put_prop_string(ctx, -2, "name"); + dukx_push_std_string(ctx, server->host()); + duk_put_prop_string(ctx, -2, "host"); + duk_push_int(ctx, server->port()); + duk_put_prop_string(ctx, -2, "port"); + duk_push_boolean(ctx, server->flags() & Server::Ssl); + duk_put_prop_string(ctx, -2, "ssl"); + duk_push_boolean(ctx, server->flags() & Server::SslVerify); + duk_put_prop_string(ctx, -2, "sslVerify"); + dukx_push_std_string(ctx, server->commandCharacter()); + duk_put_prop_string(ctx, -2, "commandChar"); + dukx_push_std_string(ctx, server->realname()); + duk_put_prop_string(ctx, -2, "realname"); + dukx_push_std_string(ctx, server->nickname()); + duk_put_prop_string(ctx, -2, "nickname"); + dukx_push_std_string(ctx, server->username()); + duk_put_prop_string(ctx, -2, "username"); + dukx_push_array(ctx, server->channels(), [&] (auto ctx, auto channel) { + dukx_push_std_string(ctx, channel); + }); + duk_put_prop_string(ctx, -2, "channels"); + + return 1; +} + +/* + * Method: Server.invite(target, channel) + * ------------------------------------------------------------------ + * + * Invite someone to a channel. + * + * Arguments: + * - target, the target to invite, + * - channel, the channel. + */ +duk_ret_t invite(duk_context *ctx) +{ + self(ctx)->invite(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.join(channel, password = undefined) + * ------------------------------------------------------------------ + * + * Join a channel with an optional password. + * + * Arguments: + * - channel, the channel to join, + * - password, the password or undefined to not use. + */ +duk_ret_t join(duk_context *ctx) +{ + self(ctx)->join(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.kick(target, channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Kick someone from a channel. + * + * Arguments: + * - target, the target to kick, + * - channel, the channel, + * - reason, the optional reason or undefined to not set. + */ +duk_ret_t kick(duk_context *ctx) +{ + self(ctx)->kick(duk_require_string(ctx, 0), duk_require_string(ctx, 1), dukx_get_std_string(ctx, 2)); + + return 0; +} + +/* + * Method: Server.me(target, message) + * ------------------------------------------------------------------ + * + * Send a CTCP Action. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + */ +duk_ret_t me(duk_context *ctx) +{ + self(ctx)->me(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.message(target, message) + * ------------------------------------------------------------------ + * + * Send a message. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + */ +duk_ret_t message(duk_context *ctx) +{ + self(ctx)->message(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.mode(mode) + * ------------------------------------------------------------------ + * + * Change your mode. + * + * Arguments: + * - mode, the new mode. + */ +duk_ret_t mode(duk_context *ctx) +{ + self(ctx)->mode(duk_require_string(ctx, 0)); + + return 0; +} + +/* + * Method: Server.names(channel) + * ------------------------------------------------------------------ + * + * Get the list of names from a channel. + * + * Arguments: + * - channel, the channel. + */ +duk_ret_t names(duk_context *ctx) +{ + self(ctx)->names(duk_require_string(ctx, 0)); + + return 0; +} + +/* + * Method: Server.nick(nickname) + * ------------------------------------------------------------------ + * + * Change the nickname. + * + * Arguments: + * - nickname, the nickname. + */ +duk_ret_t nick(duk_context *ctx) +{ + self(ctx)->setNickname(duk_require_string(ctx, 0)); + + return 0; +} + +/* + * Method: Server.notice(target, message) + * ------------------------------------------------------------------ + * + * Send a private notice. + * + * Arguments: + * - target, the target, + * - message, the notice message. + */ +duk_ret_t notice(duk_context *ctx) +{ + self(ctx)->notice(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.part(channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Leave a channel. + * + * Arguments: + * - channel, the channel to leave, + * - reason, the optional reason, keep undefined for portability. + */ +duk_ret_t part(duk_context *ctx) +{ + self(ctx)->part(duk_require_string(ctx, 0), dukx_get_std_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.send(raw) + * ------------------------------------------------------------------ + * + * Send a raw message to the IRC server. + * + * Arguments: + * - raw, the raw message (without terminators). + */ +duk_ret_t send(duk_context *ctx) +{ + self(ctx)->send(duk_require_string(ctx, 0)); + + return 0; +} + +/* + * Method: Server.topic(channel, topic) + * ------------------------------------------------------------------ + * + * Change a channel topic. + * + * Arguments: + * - channel, the channel, + * - topic, the new topic. + */ +duk_ret_t topic(duk_context *ctx) +{ + self(ctx)->topic(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + return 0; +} + +/* + * Method: Server.whois(target) + * ------------------------------------------------------------------ + * + * Get whois information. + * + * Arguments: + * - target, the target. + */ +duk_ret_t whois(duk_context *ctx) +{ + self(ctx)->whois(duk_require_string(ctx, 0)); + + return 0; +} + +/* + * Method: Server.toString() + * ------------------------------------------------------------------ + * + * Convert the object to std::string, convenience for adding the object + * as property key. + * + * duk_ret_turns: + * The server name (unique). + */ +duk_ret_t toString(duk_context *ctx) +{ + dukx_push_std_string(ctx, self(ctx)->name()); + + return 1; +} + +/* + * Function: Irccd.Server(params) [constructor] + * ------------------------------------------------------------------ + * + * Construct a new server. + * + * Params must be filled with the following properties: + * + * name: the name, + * host: the host, + * ipv6: true to use ipv6, (Optional: default false) + * port: the port number, (Optional: default 6667) + * password: the password, (Optional: default none) + * channels: array of channels (Optiona: default empty) + * ssl: true to use ssl, (Optional: default false) + * sslVerify: true to verify (Optional: default true) + * nickname: "nickname", (Optional, default: irccd) + * username: "user name", (Optional, default: irccd) + * realname: "real name", (Optional, default: IRC Client Daemon) + * commandChar: "!", (Optional, the command char, default: "!") + */ +duk_ret_t constructor(duk_context *ctx) +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + duk_check_type(ctx, 0, DUK_TYPE_OBJECT); + + try { + auto json = duk_json_encode(ctx, 0); + auto s = Server::fromJson(nlohmann::json::parse(json)); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<Server>(std::move(s))); + duk_put_prop_string(ctx, -2, Signature); + duk_pop(ctx); + } catch (const std::exception &ex) { + duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); + } + + return 0; +} + +/* + * Function: Irccd.Server() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +duk_ret_t destructor(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, Signature); + delete static_cast<std::shared_ptr<Server> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, Signature); + + return 0; +} + +/* + * Function: Irccd.Server.add(s) + * ------------------------------------------------------------------ + * + * Register a new server to the irccd instance. + * + * Arguments: + * - s, the server to add. + */ +duk_ret_t add(duk_context *ctx) +{ + dukx_get_irccd(ctx).servers().add(dukx_require_server(ctx, 0)); + + return 0; +} + +/* + * Function: Irccd.Server.find(name) + * ------------------------------------------------------------------ + * + * Find a server by name. + * + * Arguments: + * - name, the server name + * duk_ret_turns: + * The server object or undefined if not found. + */ +duk_ret_t find(duk_context *ctx) +{ + auto server = dukx_get_irccd(ctx).servers().get(duk_require_string(ctx, 0)); + + if (!server) + return 0; + + dukx_push_server(ctx, server); + + return 1; +} + +/* + * Function: Irccd.Server.list() + * ------------------------------------------------------------------ + * + * Get the map of all loaded servers. + * + * duk_ret_turns: + * An object with string-to-servers pairs. + */ +duk_ret_t list(duk_context *ctx) +{ + duk_push_object(ctx); + + for (const auto &server : dukx_get_irccd(ctx).servers().servers()) { + dukx_push_server(ctx, server); + duk_put_prop_string(ctx, -2, server->name().c_str()); + } + + return 1; +} + +/* + * Function: Irccd.Server.remove(name) + * ------------------------------------------------------------------ + * + * Remove a server from the irccd instance. You can pass the server object since + * it's coercible to a string. + * + * Arguments: + * - name the server name. + */ +duk_ret_t remove(duk_context *ctx) +{ + dukx_get_irccd(ctx).servers().remove(duk_require_string(ctx, 0)); + + return 0; +} + +const duk_function_list_entry methods[] = { + { "cmode", cmode, 2 }, + { "cnotice", cnotice, 2 }, + { "info", info, 0 }, + { "invite", invite, 2 }, + { "join", join, DUK_VARARGS }, + { "kick", kick, DUK_VARARGS }, + { "me", me, 2 }, + { "message", message, 2 }, + { "mode", mode, 1 }, + { "names", names, 1 }, + { "nick", nick, 1 }, + { "notice", notice, 2 }, + { "part", part, DUK_VARARGS }, + { "send", send, 1 }, + { "topic", topic, 2 }, + { "whois", whois, 1 }, + { "toString", toString, 0 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "add", add, 1 }, + { "find", find, 1 }, + { "list", list, 0 }, + { "remove", remove, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +ServerModule::ServerModule() noexcept + : Module("Irccd.Server") +{ +} + +void ServerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_c_function(plugin->context(), constructor, 1); + duk_put_function_list(plugin->context(), -1, functions); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, methods); + duk_push_c_function(plugin->context(), destructor, 1); + duk_set_finalizer(plugin->context(), -2); + duk_dup_top(plugin->context()); + duk_put_global_string(plugin->context(), Prototype); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "Server"); + duk_pop(plugin->context()); +} + +void dukx_push_server(duk_context *ctx, std::shared_ptr<Server> server) +{ + assert(ctx); + assert(server); + + StackAssert sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, new std::shared_ptr<Server>(std::move(server))); + duk_put_prop_string(ctx, -2, Signature); + duk_get_global_string(ctx, Prototype); + duk_set_prototype(ctx, -2); +} + +std::shared_ptr<Server> dukx_require_server(duk_context *ctx, duk_idx_t index) +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, Signature)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + duk_get_prop_string(ctx, index, Signature); + auto file = *static_cast<std::shared_ptr<Server> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return file; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-server.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,70 @@ +/* + * mod-server.hpp -- Irccd.Server API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_SERVER_HPP +#define IRCCD_MOD_SERVER_HPP + +/** + * \file mod-server.hpp + * \brief Irccd.Server JavaScript API. + */ + +#include "duktape.hpp" +#include "module.hpp" +#include "server.hpp" + +namespace irccd { + +/** + * \brief Irccd.Server JavaScript API. + * \ingroup modules + */ +class ServerModule : public Module { +public: + /** + * Irccd.Server. + */ + IRCCD_EXPORT ServerModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +/** + * Push a server. + * + * \pre server != nullptr + * \param ctx the context + * \param server the server + */ +IRCCD_EXPORT void dukx_push_server(duk_context *ctx, std::shared_ptr<Server> server); + +/** + * Require a server. Raise a JavaScript error if not a Server. + * + * \param ctx the context + * \param index the index + * \return the server + */ +IRCCD_EXPORT std::shared_ptr<Server> dukx_require_server(duk_context *ctx, duk_idx_t index); + +} // !irccd + +#endif // !IRCCD_JS_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-system.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,243 @@ +/* + * js-system.cpp -- Irccd.System API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <chrono> +#include <cstdlib> +#include <thread> + +#include "sysconfig.hpp" + +#if defined(HAVE_POPEN) +# include <cstdio> +#endif + +#include "mod-file.hpp" +#include "mod-irccd.hpp" +#include "mod-system.hpp" +#include "plugin-js.hpp" +#include "system.hpp" + +namespace irccd { + +namespace { + +/* + * Function: Irccd.System.env(key) + * ------------------------------------------------------------------ + * + * Get an environment system variable. + * + * Arguments: + * - key, the environment variable. + * Returns: + * The value. + */ +duk_ret_t env(duk_context *ctx) +{ + dukx_push_std_string(ctx, sys::env(dukx_get_std_string(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.System.exec(cmd) + * ------------------------------------------------------------------ + * + * Execute a system command. + * + * Arguments: + * - cmd, the command to execute. + */ +duk_ret_t exec(duk_context *ctx) +{ + std::system(duk_get_string(ctx, 0)); + + return 0; +} + +/* + * Function: Irccd.System.home() + * ------------------------------------------------------------------ + * + * Get the operating system user's home. + * + * Returns: + * The user home directory. + */ +duk_ret_t home(duk_context *ctx) +{ + dukx_push_std_string(ctx, sys::home()); + + return 1; +} + +/* + * Function: Irccd.System.name() + * ------------------------------------------------------------------ + * + * Get the operating system name. + * + * Returns: + * The system name. + */ +duk_ret_t name(duk_context *ctx) +{ + dukx_push_std_string(ctx, sys::name()); + + return 1; +} + +#if defined(HAVE_POPEN) + +/* + * Function: Irccd.System.popen(cmd, mode) [optional] + * ------------------------------------------------------------------ + * + * Wrapper for popen(3) if the function is available. + * + * Arguments: + * - cmd, the command to execute, + * - mode, the mode (e.g. "r"). + * Returns: + * A Irccd.File object. + * Throws + * - Irccd.SystemError on failures. + */ +duk_ret_t popen(duk_context *ctx) +{ + auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + if (fp == nullptr) + dukx_throw(ctx, SystemError()); + + dukx_push_file(ctx, new File(fp, [] (std::FILE *fp) { ::pclose(fp); })); + + return 1; +} + +#endif // !HAVE_POPEN + +/* + * Function: Irccd.System.sleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in seconds. + */ +duk_ret_t sleep(duk_context *ctx) +{ + std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); + + return 0; +} + +/* + * Function: Irccd.System.ticks() + * ------------------------------------------------------------------ + * + * Get the number of milliseconds since irccd was started. + * + * Returns: + * The number of milliseconds. + */ +duk_ret_t ticks(duk_context *ctx) +{ + duk_push_int(ctx, sys::ticks()); + + return 1; +} + +/* + * Function: Irccd.System.usleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in microseconds. + */ +duk_ret_t usleep(duk_context *ctx) +{ + std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); + + return 0; +} + +/* + * Function: Irccd.System.uptime() + * ------------------------------------------------------------------ + * + * Get the system uptime. + * + * Returns: + * The system uptime. + */ +duk_ret_t uptime(duk_context *ctx) +{ + duk_push_int(ctx, sys::uptime()); + + return 0; +} + +/* + * Function: Irccd.System.version() + * ------------------------------------------------------------------ + * + * Get the operating system version. + * + * Returns: + * The system version. + */ +duk_ret_t version(duk_context *ctx) +{ + dukx_push_std_string(ctx, sys::version()); + + return 1; +} + +const duk_function_list_entry functions[] = { + { "env", env, 1 }, + { "exec", exec, 1 }, + { "home", home, 0 }, + { "name", name, 0 }, +#if defined(HAVE_POPEN) + { "popen", popen, 2 }, +#endif + { "sleep", sleep, 1 }, + { "ticks", ticks, 0 }, + { "uptime", uptime, 0 }, + { "usleep", usleep, 1 }, + { "version", version, 0 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +SystemModule::SystemModule() noexcept + : Module("Irccd.System") +{ +} + +void SystemModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, functions); + duk_put_prop_string(plugin->context(), -2, "System"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-system.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-system.hpp -- Irccd.System API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_SYSTEM_HPP +#define IRCCD_MOD_SYSTEM_HPP + +/** + * \file mod-system.hpp + * \brief Irccd.System JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.System JavaScript API. + * \ingroup modules + */ +class SystemModule : public Module { +public: + /** + * Irccd.System. + */ + IRCCD_EXPORT SystemModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_SYSTEM_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-timer.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,209 @@ +/* + * js-timer.cpp -- Irccd.Timer API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <format.h> + +#include "irccd.hpp" +#include "logger.hpp" +#include "mod-irccd.hpp" +#include "mod-timer.hpp" +#include "mod-plugin.hpp" +#include "plugin-js.hpp" +#include "timer.hpp" + +using namespace fmt::literals; + +namespace irccd { + +namespace { + +const char *Signature("\xff""\xff""irccd-timer-ptr"); +const char *CallbackTable("\xff""\xff""irccd-timer-callbacks"); + +void handleSignal(std::weak_ptr<JsPlugin> ptr, std::string key) +{ + auto plugin = ptr.lock(); + + if (!plugin) + return; + + auto &irccd = dukx_get_irccd(plugin->context()); + + irccd.post([plugin, key] (Irccd &) { + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), CallbackTable); + duk_get_prop_string(plugin->context(), -1, key.c_str()); + duk_remove(plugin->context(), -2); + + if (duk_is_callable(plugin->context(), -1)) { + if (duk_pcall(plugin->context(), 0) != 0) + log::warning("plugin {}: {}"_format(plugin->name(), dukx_exception(plugin->context(), -1).stack)); + else + duk_pop(plugin->context()); + } else + duk_pop(plugin->context()); + }); +} + +std::shared_ptr<Timer> self(duk_context *ctx) +{ + StackAssert sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, Signature); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); + + return *static_cast<std::shared_ptr<Timer> *>(ptr); +} + +/* + * Method: Timer.start() + * -------------------------------------------------------- + * + * Start the timer. If the timer is already started the method is a no-op. + */ +duk_ret_t start(duk_context *ctx) +{ + auto timer = self(ctx); + + if (!timer->isRunning()) + timer->start(); + + return 0; +} + +/* + * Method: Timer.stop() + * -------------------------------------------------------- + * + * Stop the timer. + */ +duk_ret_t stop(duk_context *ctx) +{ + auto timer = self(ctx); + + if (timer->isRunning()) + timer->stop(); + + return 0; +} + +const duk_function_list_entry methods[] = { + { "start", start, 0 }, + { "stop", stop, 0 }, + { nullptr, nullptr, 0 } +}; + +/* + * Function: Irccd.Timer(type, delay, callback) [constructor] + * -------------------------------------------------------- + * + * Create a new timer object. + * + * Arguments: + * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), + * - delay, the interval in milliseconds, + * - callback, the function to call. + */ +duk_ret_t constructor(duk_context *ctx) +{ + // Check parameters. + auto type = duk_require_int(ctx, 0); + auto delay = duk_require_int(ctx, 1); + + if (type < static_cast<int>(TimerType::Single) || type > static_cast<int>(TimerType::Repeat)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); + if (delay < 0) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); + if (!duk_is_callable(ctx, 2)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); + + // Construct the timer in 'this'. + auto timer = std::make_shared<Timer>(static_cast<TimerType>(type), delay); + auto hash = std::to_string(reinterpret_cast<std::uintptr_t>(timer.get())); + + timer->onSignal.connect(std::bind(handleSignal, std::weak_ptr<JsPlugin>(dukx_get_plugin(ctx)), hash)); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<Timer>(std::move(timer))); + duk_put_prop_string(ctx, -2, Signature); + duk_push_string(ctx, hash.c_str()); + duk_put_prop_string(ctx, -2, "\xff""\xff""timer-key"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + StackAssert sa(ctx); + + duk_get_prop_string(ctx, 0, "\xff""\xff""timer-key"); + auto hash = duk_get_string(ctx, -1); + duk_pop(ctx); + duk_get_prop_string(ctx, 0, Signature); + static_cast<std::shared_ptr<Timer> *>(duk_to_pointer(ctx, -1))->get()->stop(); + delete static_cast<std::shared_ptr<Timer> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_get_global_string(ctx, CallbackTable); + duk_del_prop_string(ctx, -1, hash); + duk_pop(ctx); + log::debug("plugin: timer destroyed"); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); + + // Save a callback function into the callback table. + duk_get_global_string(ctx, CallbackTable); + duk_dup(ctx, 2); + duk_put_prop_string(ctx, -2, hash.c_str()); + duk_pop(ctx); + + return 0; +} + +const duk_number_list_entry constants[] = { + { "Single", static_cast<int>(TimerType::Single) }, + { "Repeat", static_cast<int>(TimerType::Repeat) }, + { nullptr, 0 } +}; + +} // !namespace + +TimerModule::TimerModule() noexcept + : Module("Irccd.Timer") +{ +} + +void TimerModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_c_function(plugin->context(), constructor, 3); + duk_put_number_list(plugin->context(), -1, constants); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, methods); + duk_put_prop_string(plugin->context(), -2, "prototype"); + duk_put_prop_string(plugin->context(), -2, "Timer"); + duk_pop(plugin->context()); + duk_push_object(plugin->context()); + duk_put_global_string(plugin->context(), CallbackTable); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-timer.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-timer.hpp -- Irccd.Timer API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_TIMER_HPP +#define IRCCD_MOD_TIMER_HPP + +/** + * \file mod-timer.hpp + * \brief Irccd.Timer JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Timer JavaScript API. + * \ingroup modules + */ +class TimerModule : public Module { +public: + /** + * Irccd.Timer. + */ + IRCCD_EXPORT TimerModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_TIMER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-unicode.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,152 @@ +/* + * js-unicode.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "duktape.hpp" +#include "mod-unicode.hpp" +#include "plugin-js.hpp" +#include "unicode.hpp" + +namespace irccd { + +namespace { + +/* + * Function: Irccd.Unicode.isDigit(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the digit category. + */ +duk_ret_t isDigit(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::isdigit(duk_get_int(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isLetter(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the letter category. + */ +duk_ret_t isLetter(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::isalpha(duk_get_int(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isLower(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is lower case. + */ +duk_ret_t isLower(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::islower(duk_get_int(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isSpace(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the space category. + */ +duk_ret_t isSpace(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::isspace(duk_get_int(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isTitle(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is title case. + */ +duk_ret_t isTitle(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::istitle(duk_get_int(ctx, 0))); + + return 1; +} + +/* + * Function: Irccd.Unicode.isUpper(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is upper case. + */ +duk_ret_t isUpper(duk_context *ctx) +{ + duk_push_boolean(ctx, unicode::isupper(duk_get_int(ctx, 0))); + + return 1; +} + +const duk_function_list_entry functions[] = { + { "isDigit", isDigit, 1 }, + { "isLetter", isLetter, 1 }, + { "isLower", isLower, 1 }, + { "isSpace", isSpace, 1 }, + { "isTitle", isTitle, 1 }, + { "isUpper", isUpper, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +UnicodeModule::UnicodeModule() noexcept + : Module("Irccd.Unicode") +{ +} + +void UnicodeModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, functions); + duk_put_prop_string(plugin->context(), -2, "Unicode"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-unicode.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-unicode.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_UNICODE_HPP +#define IRCCD_MOD_UNICODE_HPP + +/** + * \file mod-unicode.hpp + * \brief Irccd.Unicode JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Unicode JavaScript API. + * \ingroup modules + */ +class UnicodeModule : public Module { +public: + /** + * Irccd.Unicode. + */ + IRCCD_EXPORT UnicodeModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_UNICODE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-util.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,150 @@ +/* + * js-util.cpp -- Irccd.Util API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <libircclient.h> + +#include "mod-util.hpp" +#include "plugin-js.hpp" +#include "util.hpp" + +namespace irccd { + +namespace { + +/** + * Read parameters for Irccd.Util.format function, the object is defined as + * following: + * + * { + * date: the date object + * flags: the flags (not implemented yet) + * field1: a field to substitute in #{} pattern + * field2: a field to substitute in #{} pattern + * fieldn: ... + * } + */ +util::Substitution getSubstitution(duk_context *ctx, int index) +{ + util::Substitution params; + + if (!duk_is_object(ctx, index)) + return params; + + dukx_enumerate(ctx, index, 0, true, [&] (duk_context *) { + if (dukx_get_std_string(ctx, -2) == "date") + params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000); + else + params.keywords.insert({dukx_get_std_string(ctx, -2), dukx_get_std_string(ctx, -1)}); + }); + + return params; +} + +/* + * Function: Irccd.Util.format(text, parameters) + * -------------------------------------------------------- + * + * Format a string with templates. + * + * Arguments: + * - input, the text to update, + * - params, the parameters. + * Returns: + * The converted text. + */ +duk_ret_t format(duk_context *ctx) +{ + try { + dukx_push_std_string(ctx, util::format(dukx_get_std_string(ctx, 0), getSubstitution(ctx, 1))); + } catch (const std::exception &ex) { + dukx_throw(ctx, SyntaxError(ex.what())); + } + + return 1; +} + +/* + * Function: Irccd.Util.splituser(ident) + * -------------------------------------------------------- + * + * Return the nickname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The nickname. + */ +duk_ret_t splituser(duk_context *ctx) +{ + auto target = duk_require_string(ctx, 0); + char nick[32] = {0}; + + irc_target_get_nick(target, nick, sizeof (nick) -1); + duk_push_string(ctx, nick); + + return 1; +} + +/* + * Function: Irccd.Util.splithost(ident) + * -------------------------------------------------------- + * + * Return the hostname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The hostname. + */ +duk_ret_t splithost(duk_context *ctx) +{ + auto target = duk_require_string(ctx, 0); + char host[32] = {0}; + + irc_target_get_host(target, host, sizeof (host) -1); + duk_push_string(ctx, host); + + return 1; +} + +const duk_function_list_entry functions[] = { + { "format", format, DUK_VARARGS }, + { "splituser", splituser, 1 }, + { "splithost", splithost, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +UtilModule::UtilModule() noexcept + : Module("Irccd.Util") +{ +} + +void UtilModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) +{ + StackAssert sa(plugin->context()); + + duk_get_global_string(plugin->context(), "Irccd"); + duk_push_object(plugin->context()); + duk_put_function_list(plugin->context(), -1, functions); + duk_put_prop_string(plugin->context(), -2, "Util"); + duk_pop(plugin->context()); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/mod-util.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,50 @@ +/* + * mod-util.hpp -- Irccd.Util API + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MOD_UTIL_HPP +#define IRCCD_MOD_UTIL_HPP + +/** + * \file mod-util.hpp + * \brief Irccd.Util JavaScript API. + */ + +#include "module.hpp" + +namespace irccd { + +/** + * \brief Irccd.Util JavaScript API. + * \ingroup modules + */ +class UtilModule : public Module { +public: + /** + * Irccd.Util. + */ + IRCCD_EXPORT UtilModule() noexcept; + + /** + * \copydoc Module::load + */ + IRCCD_EXPORT void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) override; +}; + +} // !irccd + +#endif // !IRCCD_MOD_UTIL_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/module.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,102 @@ +/* + * module.hpp -- JavaScript API module + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_MODULE_HPP +#define IRCCD_MODULE_HPP + +/** + * \file module.hpp + * \brief JavaScript API module. + */ + +/** + * \defgroup modules JavaScript modules + * \brief Modules for the JavaScript API. + */ + +#include <cassert> +#include <memory> + +#include "sysconfig.hpp" +#include "util.hpp" + +namespace irccd { + +class Irccd; +class JsPlugin; + +/** + * \brief JavaScript API module. + */ +class Module { +private: + std::string m_name; + +public: + /** + * Default constructor. + * + * \pre !name.empty() + */ + inline Module(std::string name) noexcept + : m_name(std::move(name)) + { + assert(!m_name.empty()); + } + + /** + * Virtual destructor defaulted. + */ + virtual ~Module() = default; + + /** + * Get the module name. + * + * \return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Load the module into the JavaScript plugin. + * + * \param irccd the irccd instance + * \param plugin the plugin + */ + virtual void load(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) + { + util::unused(irccd, plugin); + } + + /** + * Unload the module from the JavaScript plugin. + * + * \param irccd the irccd instance + * \param plugin the plugin + */ + virtual void unload(Irccd &irccd, const std::shared_ptr<JsPlugin> &plugin) + { + util::unused(irccd, plugin); + } +}; + +} // !irccd + +#endif // !IRCCD_MODULE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/plugin-js.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,454 @@ +/* + * plugin-js.cpp -- JavaScript plugins for irccd + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "sysconfig.hpp" + +#if defined(HAVE_STAT) +# include <sys/stat.h> +# include <cerrno> +# include <cstring> +#endif + +#include "fs.hpp" +#include "irccd.hpp" +#include "logger.hpp" +#include "mod-plugin.hpp" +#include "mod-server.hpp" +#include "plugin-js.hpp" +#include "service-plugin.hpp" +#include "timer.hpp" + +namespace irccd { + +const char JsPlugin::ConfigProperty[] = "\xff""\xff""irccd-plugin-config"; +const char JsPlugin::FormatProperty[] = "\xff""\xff""irccd-plugin-format"; + +std::unordered_map<std::string, std::string> JsPlugin::getTable(const char *name) const +{ + StackAssert sa(m_context); + std::unordered_map<std::string, std::string> result; + + duk_get_global_string(m_context, name); + dukx_enumerate(m_context, -1, 0, true, [&] (auto ctx) { + result.emplace(duk_to_string(ctx, -2), duk_to_string(ctx, -1)); + }); + duk_pop(m_context); + + return result; +} + +void JsPlugin::putTable(const char *name, const std::unordered_map<std::string, std::string> &vars) +{ + StackAssert sa(m_context); + + duk_get_global_string(m_context, name); + + for (const auto &pair : vars) { + dukx_push_std_string(m_context, pair.second); + duk_put_prop_string(m_context, -2, pair.first.c_str()); + } + + duk_pop(m_context); +} + +void JsPlugin::call(const std::string &name, unsigned nargs) +{ + duk_get_global_string(m_context, name.c_str()); + + if (duk_get_type(m_context, -1) == DUK_TYPE_UNDEFINED) + // Function not defined, remove the undefined value and all arguments. + duk_pop_n(m_context, nargs + 1); + else { + // Call the function and discard the result. + duk_insert(m_context, -nargs - 1); + + if (duk_pcall(m_context, nargs) != 0) + throw dukx_exception(m_context, -1, true); + + duk_pop(m_context); + } +} + +void JsPlugin::putVars() +{ + StackAssert sa(m_context); + + duk_push_pointer(m_context, this); + duk_put_global_string(m_context, "\xff""\xff""plugin"); + dukx_push_std_string(m_context, name()); + duk_put_global_string(m_context, "\xff""\xff""name"); + dukx_push_std_string(m_context, path()); + duk_put_global_string(m_context, "\xff""\xff""path"); +} + +void JsPlugin::putPath(const std::string &varname, const std::string &append, path::Path type) +{ + StackAssert sa(m_context); + + bool found = true; + std::string foundpath; + + // Use the first existing directory available. + for (const auto &p : path::list(type)) { + foundpath = path::clean(p + append); + + if (fs::exists(foundpath)) { + found = true; + break; + } + } + + // Use the system as default. + if (!found) + foundpath = path::clean(path::get(type, path::OwnerSystem) + append); + + duk_get_global_string(m_context, "Irccd"); + duk_get_prop_string(m_context, -1, "Plugin"); + dukx_push_std_string(m_context, foundpath); + duk_put_prop_string(m_context, -2, varname.c_str()); + duk_pop_2(m_context); +} + +JsPlugin::JsPlugin(std::string name, std::string path) + : Plugin(name, path) +{ + /* + * Create two special tables for configuration and formats, they are + * referenced later as + * + * - Irccd.Plugin.config + * - Irccd.Plugin.format + * + * In mod-plugin.cpp. + */ + duk_push_object(m_context); + duk_put_global_string(m_context, ConfigProperty); + duk_push_object(m_context); + duk_put_global_string(m_context, FormatProperty); +} + +void JsPlugin::onChannelMode(Irccd &, const ChannelModeEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.mode); + dukx_push_std_string(m_context, event.argument); + call("onChannelMode", 5); +} + +void JsPlugin::onChannelNotice(Irccd &, const ChannelNoticeEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.message); + call("onChannelNotice", 4); +} + +void JsPlugin::onCommand(Irccd &, const MessageEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.message); + call("onCommand", 4); +} + +void JsPlugin::onConnect(Irccd &, const ConnectEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + call("onConnect", 1); +} + +void JsPlugin::onInvite(Irccd &, const InviteEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + call("onInvite", 3); +} + +void JsPlugin::onJoin(Irccd &, const JoinEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + call("onJoin", 3); +} + +void JsPlugin::onKick(Irccd &, const KickEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.target); + dukx_push_std_string(m_context, event.reason); + call("onKick", 5); +} + +void JsPlugin::onLoad(Irccd &irccd) +{ + StackAssert sa(m_context); + + /* + * Duktape currently emit useless warnings when a file do + * not exists so we do a homemade access. + */ +#if defined(HAVE_STAT) + struct stat st; + + if (::stat(path().c_str(), &st) < 0) + throw std::runtime_error(std::strerror(errno)); +#endif + + /* + * dataPath: DATA + plugin/name (e.g ~/.local/share/irccd/plugins/<name>/) + * configPath: CONFIG + plugin/name (e.g ~/.config/irccd/plugin/<name>/) + */ + putVars(); + putPath("dataPath", "plugin/" + name(), path::PathData); + putPath("configPath", "plugin/" + name(), path::PathConfig); + putPath("cachePath", "plugin/" + name(), path::PathCache); + + // Try to load the file (does not call onLoad yet). + if (duk_peval_file(m_context, path().c_str()) != 0) + throw dukx_exception(m_context, -1, true); + + duk_pop(m_context); + + /* + * We put configuration and formats after loading the file and before + * calling onLoad to allow the plugin adding configuration to + * Irccd.Plugin.(config|format) before the user. + */ + setConfig(irccd.plugins().config(name())); + setFormats(irccd.plugins().formats(name())); + + // Read metadata . + duk_get_global_string(m_context, "info"); + + if (duk_get_type(m_context, -1) == DUK_TYPE_OBJECT) { + // 'author' + duk_get_prop_string(m_context, -1, "author"); + setAuthor(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : author()); + duk_pop(m_context); + + // 'license' + duk_get_prop_string(m_context, -1, "license"); + setLicense(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : license()); + duk_pop(m_context); + + // 'summary' + duk_get_prop_string(m_context, -1, "summary"); + setSummary(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : summary()); + duk_pop(m_context); + + // 'version' + duk_get_prop_string(m_context, -1, "version"); + setVersion(duk_is_string(m_context, -1) ? duk_get_string(m_context, -1) : version()); + duk_pop(m_context); + } + + duk_pop(m_context); + call("onLoad", 0); +} + +void JsPlugin::onMessage(Irccd &, const MessageEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.message); + call("onMessage", 4); +} + +void JsPlugin::onMe(Irccd &, const MeEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.message); + call("onMe", 4); +} + +void JsPlugin::onMode(Irccd &, const ModeEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.mode); + call("onMode", 3); +} + +void JsPlugin::onNames(Irccd &, const NamesEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.channel); + dukx_push_array(m_context, event.names, dukx_push_std_string); + call("onNames", 3); +} + +void JsPlugin::onNick(Irccd &, const NickEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.nickname); + call("onNick", 3); +} + +void JsPlugin::onNotice(Irccd &, const NoticeEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.message); + call("onNotice", 3); +} + +void JsPlugin::onPart(Irccd &, const PartEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.reason); + call("onPart", 4); +} + +void JsPlugin::onQuery(Irccd &, const QueryEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.message); + call("onQuery", 3); +} + +void JsPlugin::onQueryCommand(Irccd &, const QueryEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.message); + call("onQueryCommand", 3); +} + +void JsPlugin::onReload(Irccd &) +{ + StackAssert sa(m_context); + + call("onReload"); +} + +void JsPlugin::onTopic(Irccd &, const TopicEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + dukx_push_std_string(m_context, event.origin); + dukx_push_std_string(m_context, event.channel); + dukx_push_std_string(m_context, event.topic); + call("onTopic", 4); +} + +void JsPlugin::onUnload(Irccd &) +{ + StackAssert sa(m_context); + + call("onUnload"); +} + +void JsPlugin::onWhois(Irccd &, const WhoisEvent &event) +{ + StackAssert sa(m_context); + + dukx_push_server(m_context, std::move(event.server)); + duk_push_object(m_context); + dukx_push_std_string(m_context, event.whois.nick); + duk_put_prop_string(m_context, -2, "nickname"); + dukx_push_std_string(m_context, event.whois.user); + duk_put_prop_string(m_context, -2, "username"); + dukx_push_std_string(m_context, event.whois.realname); + duk_put_prop_string(m_context, -2, "realname"); + dukx_push_std_string(m_context, event.whois.host); + duk_put_prop_string(m_context, -2, "host"); + dukx_push_array(m_context, event.whois.channels, dukx_push_std_string); + duk_put_prop_string(m_context, -2, "channels"); + call("onWhois", 2); +} + +std::shared_ptr<Plugin> JsPluginLoader::open(const std::string &id, + const std::string &path) noexcept +{ + if (path.rfind(".js") == std::string::npos) + return nullptr; + + try { + return std::make_shared<JsPlugin>(id, path); + } catch (const std::exception &ex) { + log::warning() << "plugin " << id << ": " << ex.what() << std::endl; + } + + return nullptr; +} + +std::shared_ptr<Plugin> JsPluginLoader::find(const std::string &id) noexcept +{ + for (const auto &dir : path::list(path::PathPlugins)) { + auto path = dir + id + ".js"; + + if (!fs::isReadable(path)) + continue; + + log::info() << "plugin " << id << ": trying " << path << std::endl; + + return open(id, path); + } + + return nullptr; +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/plugin-js.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,236 @@ +/* + * plugin-js.hpp -- JavaScript plugins for irccd + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_PLUGIN_JS_HPP +#define IRCCD_PLUGIN_JS_HPP + +/** + * \file plugin-js.hpp + * \brief JavaScript plugins for irccd. + */ + +#include "duktape.hpp" +#include "path.hpp" +#include "plugin.hpp" + +namespace irccd { + +/** + * \brief JavaScript plugins for irccd. + * \ingroup plugins + */ +class JsPlugin : public Plugin { +public: + /** + * Global property where to read/write plugin configuration (object). + */ + static const char ConfigProperty[]; + + /** + * Global property where to read/write plugin formats (object). + */ + static const char FormatProperty[]; + +private: + // JavaScript context + UniqueContext m_context; + + // Private helpers. + std::unordered_map<std::string, std::string> getTable(const char *name) const; + void putTable(const char *name, const std::unordered_map<std::string, std::string> &vars); + void call(const std::string &name, unsigned nargs = 0); + void putVars(); + void putPath(const std::string &varname, const std::string &append, path::Path type); + +public: + /** + * Constructor. + * + * \param name the plugin name + * \param path the path to the plugin + */ + IRCCD_EXPORT JsPlugin(std::string name, std::string path); + + /** + * Access the Duktape context. + * + * \return the context + */ + inline UniqueContext &context() noexcept + { + return m_context; + } + + /** + * \copydoc Plugin::config + */ + PluginConfig config() override + { + return getTable(ConfigProperty); + } + + /** + * \copydoc Plugin::setConfig + */ + void setConfig(PluginConfig config) override + { + putTable(ConfigProperty, config); + } + + /** + * \copydoc Plugin::formats + */ + PluginFormats formats() override + { + return getTable(FormatProperty); + } + + /** + * \copydoc Plugin::setFormats + */ + void setFormats(PluginFormats formats) override + { + putTable(FormatProperty, formats); + } + + /** + * \copydoc Plugin::onCommand + */ + IRCCD_EXPORT void onCommand(Irccd &irccd, const MessageEvent &event) override; + + /** + * \copydoc Plugin::onConnect + */ + IRCCD_EXPORT void onConnect(Irccd &irccd, const ConnectEvent &event) override; + + /** + * \copydoc Plugin::onChannelMode + */ + IRCCD_EXPORT void onChannelMode(Irccd &irccd, const ChannelModeEvent &event) override; + + /** + * \copydoc Plugin::onChannelNotice + */ + IRCCD_EXPORT void onChannelNotice(Irccd &irccd, const ChannelNoticeEvent &event) override; + + /** + * \copydoc Plugin::onInvite + */ + IRCCD_EXPORT void onInvite(Irccd &irccd, const InviteEvent &event) override; + + /** + * \copydoc Plugin::onJoin + */ + IRCCD_EXPORT void onJoin(Irccd &irccd, const JoinEvent &event) override; + + /** + * \copydoc Plugin::onKick + */ + IRCCD_EXPORT void onKick(Irccd &irccd, const KickEvent &event) override; + + /** + * \copydoc Plugin::onLoad + */ + IRCCD_EXPORT void onLoad(Irccd &irccd) override; + + /** + * \copydoc Plugin::onMessage + */ + IRCCD_EXPORT void onMessage(Irccd &irccd, const MessageEvent &event) override; + + /** + * \copydoc Plugin::onMe + */ + IRCCD_EXPORT void onMe(Irccd &irccd, const MeEvent &event) override; + + /** + * \copydoc Plugin::onMode + */ + IRCCD_EXPORT void onMode(Irccd &irccd, const ModeEvent &event) override; + + /** + * \copydoc Plugin::onNames + */ + IRCCD_EXPORT void onNames(Irccd &irccd, const NamesEvent &event) override; + + /** + * \copydoc Plugin::onNick + */ + IRCCD_EXPORT void onNick(Irccd &irccd, const NickEvent &event) override; + + /** + * \copydoc Plugin::onNotice + */ + IRCCD_EXPORT void onNotice(Irccd &irccd, const NoticeEvent &event) override; + + /** + * \copydoc Plugin::onPart + */ + IRCCD_EXPORT void onPart(Irccd &irccd, const PartEvent &event) override; + + /** + * \copydoc Plugin::onQuery + */ + IRCCD_EXPORT void onQuery(Irccd &irccd, const QueryEvent &event) override; + + /** + * \copydoc Plugin::onQueryCommand + */ + IRCCD_EXPORT void onQueryCommand(Irccd &irccd, const QueryEvent &event) override; + + /** + * \copydoc Plugin::onReload + */ + IRCCD_EXPORT void onReload(Irccd &irccd) override; + + /** + * \copydoc Plugin::onTopic + */ + IRCCD_EXPORT void onTopic(Irccd &irccd, const TopicEvent &event) override; + + /** + * \copydoc Plugin::onUnload + */ + IRCCD_EXPORT void onUnload(Irccd &irccd) override; + + /** + * \copydoc Plugin::onWhois + */ + IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override; +}; + +/** + * \brief Implementation for searching Javascript plugins. + */ +class JsPluginLoader : public PluginLoader { +public: + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr<Plugin> open(const std::string &id, + const std::string &path) noexcept override; + + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr<Plugin> find(const std::string &id) noexcept override; +}; + +} // !irccd + +#endif // !IRCCD_PLUGIN_JS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/timer.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,89 @@ +/* + * timer.cpp -- threaded timers + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <chrono> + +#include "timer.hpp" + +namespace irccd { + +void Timer::run() +{ + while (m_state != Stopped) { + std::unique_lock<std::mutex> lock(m_mutex); + + // Wait in case the timer is paused. + m_condition.wait(lock, [&] () { + return m_state != Paused; + }); + + if (m_state != Running) + continue; + + // Wait the timer delay or the interrupt. + m_condition.wait_for(lock, std::chrono::milliseconds(m_delay), [&] () { + return m_state != Running; + }); + + if (m_state == Running) { + // Signal process. + onSignal(); + + if (m_type == TimerType::Single) + m_state = Stopped; + } + } + + onEnd(); +} + +Timer::Timer(TimerType type, unsigned delay) noexcept + : m_type(type) + , m_delay(delay) + , m_thread(std::bind(&Timer::run, this)) +{ +} + +Timer::~Timer() +{ + assert(m_state != Running); + + try { + m_state = Stopped; + m_condition.notify_one(); + m_thread.join(); + } catch (...) { + } +} + +void Timer::start() +{ + assert(m_state != Running); + + m_state = Running; + m_condition.notify_one(); +} + +void Timer::stop() +{ + m_state = Paused; + m_condition.notify_one(); +} + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/timer.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,156 @@ +/* + * timer.hpp -- threaded timers + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_TIMER_HPP +#define IRCCD_TIMER_HPP + +/** + * \file timer.hpp + * \brief Provides interval based timers for JavaScript + */ + +#include <atomic> +#include <condition_variable> +#include <functional> +#include <mutex> +#include <thread> + +#include "signals.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +/** + * \enum TimerType + * \brief Type of timer + */ +enum class TimerType { + Single, //!< The timer ends after execution + Repeat //!< The timer loops +}; + +/** + * \class Timer + * \brief Timer class + * + * A timer is a thread object that emits a signal periodically or just one time. It is perfectly pausable and resumable + * to reuse the same object. + * + * The delay is configured in milliseconds and the user has choice to use any + * delay needed. + * + * We use a condition variable to wait for the specified delay unless the timer + * must be stopped. + */ +class Timer { +public: + /** + * Signal: onSignal + * ---------------------------------------------------------- + * + * Called when the timeout expires. + */ + Signal<> onSignal; + + /** + * Signal: onEnd + * ---------------------------------------------------------- + * + * Called when the timeout ends. + */ + Signal<> onEnd; + +private: + enum { + Paused, + Running, + Stopped + }; + + TimerType m_type; + unsigned m_delay; + + // Thread management. + std::atomic<int> m_state{Paused}; + std::mutex m_mutex; + std::condition_variable m_condition; + std::thread m_thread; + + void run(); + +public: + /** + * Timer constructor. + * + * The timer is not started, use start(). + * + * \param type the timer type + * \param delay the delay in milliseconds + * \post isRunning() returns false + */ + IRCCD_EXPORT Timer(TimerType type, unsigned delay) noexcept; + + /** + * Destructor, closes the thread. + * + * \pre stop() must have been called. + */ + IRCCD_EXPORT virtual ~Timer(); + + /** + * Start the thread. + * + * \pre isRunning() must return false + * \pre onSignal() must have been called + * \pre onEnd() must have been called + * \note Thread-safe + */ + IRCCD_EXPORT void start(); + + /** + * Stop the timer, may be used by the user to stop it. + * + * \note Thread-safe + */ + IRCCD_EXPORT void stop(); + + /** + * Get the type of timer. + * + * \return the type. + */ + inline TimerType type() const noexcept + { + return m_type; + } + + /** + * Tells if the timer has still a running thread. + * + * \return true if still alive + * \note Thread-safe + */ + inline bool isRunning() const noexcept + { + return m_state == Running; + } +}; + +} // !irccd + +#endif // !IRCCD_TIMER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/unicode.cpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,4796 @@ +/* + * unicode.cpp -- UTF-8 to UTF-32 conversions and various operations + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "unicode.hpp" + +/* + * The following code has been generated from Go mkrunetype adapted to our + * needs. + */ + +namespace irccd { + +namespace unicode { + +#define nelem(x) (sizeof (x) / sizeof ((x)[0])) + +namespace { + +const char32_t *rbsearch(char32_t c, const char32_t *t, int n, int ne) noexcept +{ + const char32_t *p; + int m; + + while (n > 1) { + m = n >> 1; + p = t + m * ne; + + if (c >= p[0]) { + t = p; + n = n - m; + } else + n = m; + } + + if (n && c >= t[0]) + return t; + + return nullptr; +} + +} // !namespace + +namespace { + +const char32_t isspacer[] = { + 0x0009, 0x000d, + 0x0020, 0x0020, + 0x0085, 0x0085, + 0x00a0, 0x00a0, + 0x1680, 0x1680, + 0x2000, 0x200a, + 0x2028, 0x2029, + 0x202f, 0x202f, + 0x205f, 0x205f, + 0x3000, 0x3000, + 0xfeff, 0xfeff, +}; + +} // !namespace + +bool isspace(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, isspacer, nelem (isspacer)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + return false; +} + +namespace { + +const char32_t isdigitr[] = { + 0x0030, 0x0039, + 0x0660, 0x0669, + 0x06f0, 0x06f9, + 0x07c0, 0x07c9, + 0x0966, 0x096f, + 0x09e6, 0x09ef, + 0x0a66, 0x0a6f, + 0x0ae6, 0x0aef, + 0x0b66, 0x0b6f, + 0x0be6, 0x0bef, + 0x0c66, 0x0c6f, + 0x0ce6, 0x0cef, + 0x0d66, 0x0d6f, + 0x0de6, 0x0def, + 0x0e50, 0x0e59, + 0x0ed0, 0x0ed9, + 0x0f20, 0x0f29, + 0x1040, 0x1049, + 0x1090, 0x1099, + 0x17e0, 0x17e9, + 0x1810, 0x1819, + 0x1946, 0x194f, + 0x19d0, 0x19d9, + 0x1a80, 0x1a89, + 0x1a90, 0x1a99, + 0x1b50, 0x1b59, + 0x1bb0, 0x1bb9, + 0x1c40, 0x1c49, + 0x1c50, 0x1c59, + 0xa620, 0xa629, + 0xa8d0, 0xa8d9, + 0xa900, 0xa909, + 0xa9d0, 0xa9d9, + 0xa9f0, 0xa9f9, + 0xaa50, 0xaa59, + 0xabf0, 0xabf9, + 0xff10, 0xff19, + 0x104a0, 0x104a9, + 0x11066, 0x1106f, + 0x110f0, 0x110f9, + 0x11136, 0x1113f, + 0x111d0, 0x111d9, + 0x112f0, 0x112f9, + 0x114d0, 0x114d9, + 0x11650, 0x11659, + 0x116c0, 0x116c9, + 0x118e0, 0x118e9, + 0x16a60, 0x16a69, + 0x16b50, 0x16b59, + 0x1d7ce, 0x1d7ff, +}; + +} // !namespace + +bool isdigit(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, isdigitr, nelem (isdigitr)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + return false; +} + +namespace { + +const char32_t isalphar[] = { + 0x0041, 0x005a, + 0x0061, 0x007a, + 0x00c0, 0x00d6, + 0x00d8, 0x00f6, + 0x00f8, 0x02c1, + 0x02c6, 0x02d1, + 0x02e0, 0x02e4, + 0x0370, 0x0374, + 0x0376, 0x0377, + 0x037a, 0x037d, + 0x0388, 0x038a, + 0x038e, 0x03a1, + 0x03a3, 0x03f5, + 0x03f7, 0x0481, + 0x048a, 0x052f, + 0x0531, 0x0556, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0620, 0x064a, + 0x066e, 0x066f, + 0x0671, 0x06d3, + 0x06e5, 0x06e6, + 0x06ee, 0x06ef, + 0x06fa, 0x06fc, + 0x0712, 0x072f, + 0x074d, 0x07a5, + 0x07ca, 0x07ea, + 0x07f4, 0x07f5, + 0x0800, 0x0815, + 0x0840, 0x0858, + 0x08a0, 0x08b2, + 0x0904, 0x0939, + 0x0958, 0x0961, + 0x0971, 0x0980, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a72, 0x0a74, + 0x0a85, 0x0a8d, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0ae0, 0x0ae1, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b35, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c39, + 0x0c58, 0x0c59, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0cf1, 0x0cf2, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d3a, + 0x0d60, 0x0d61, + 0x0d7a, 0x0d7f, + 0x0d85, 0x0d96, + 0x0d9a, 0x0db1, + 0x0db3, 0x0dbb, + 0x0dc0, 0x0dc6, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eb0, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edf, + 0x0f40, 0x0f47, + 0x0f49, 0x0f6c, + 0x0f88, 0x0f8c, + 0x1000, 0x102a, + 0x1050, 0x1055, + 0x105a, 0x105d, + 0x1065, 0x1066, + 0x106e, 0x1070, + 0x1075, 0x1081, + 0x10a0, 0x10c5, + 0x10d0, 0x10fa, + 0x10fc, 0x1248, + 0x124a, 0x124d, + 0x1250, 0x1256, + 0x125a, 0x125d, + 0x1260, 0x1288, + 0x128a, 0x128d, + 0x1290, 0x12b0, + 0x12b2, 0x12b5, + 0x12b8, 0x12be, + 0x12c2, 0x12c5, + 0x12c8, 0x12d6, + 0x12d8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135a, + 0x1380, 0x138f, + 0x13a0, 0x13f4, + 0x1401, 0x166c, + 0x166f, 0x167f, + 0x1681, 0x169a, + 0x16a0, 0x16ea, + 0x16f1, 0x16f8, + 0x1700, 0x170c, + 0x170e, 0x1711, + 0x1720, 0x1731, + 0x1740, 0x1751, + 0x1760, 0x176c, + 0x176e, 0x1770, + 0x1780, 0x17b3, + 0x1820, 0x1877, + 0x1880, 0x18a8, + 0x18b0, 0x18f5, + 0x1900, 0x191e, + 0x1950, 0x196d, + 0x1970, 0x1974, + 0x1980, 0x19ab, + 0x19c1, 0x19c7, + 0x1a00, 0x1a16, + 0x1a20, 0x1a54, + 0x1b05, 0x1b33, + 0x1b45, 0x1b4b, + 0x1b83, 0x1ba0, + 0x1bae, 0x1baf, + 0x1bba, 0x1be5, + 0x1c00, 0x1c23, + 0x1c4d, 0x1c4f, + 0x1c5a, 0x1c7d, + 0x1ce9, 0x1cec, + 0x1cee, 0x1cf1, + 0x1cf5, 0x1cf6, + 0x1d00, 0x1dbf, + 0x1e00, 0x1f15, + 0x1f18, 0x1f1d, + 0x1f20, 0x1f45, + 0x1f48, 0x1f4d, + 0x1f50, 0x1f57, + 0x1f5f, 0x1f7d, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x2090, 0x209c, + 0x210a, 0x2113, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x212f, 0x2139, + 0x213c, 0x213f, + 0x2145, 0x2149, + 0x2183, 0x2184, + 0x2c00, 0x2c2e, + 0x2c30, 0x2c5e, + 0x2c60, 0x2ce4, + 0x2ceb, 0x2cee, + 0x2cf2, 0x2cf3, + 0x2d00, 0x2d25, + 0x2d30, 0x2d67, + 0x2d80, 0x2d96, + 0x2da0, 0x2da6, + 0x2da8, 0x2dae, + 0x2db0, 0x2db6, + 0x2db8, 0x2dbe, + 0x2dc0, 0x2dc6, + 0x2dc8, 0x2dce, + 0x2dd0, 0x2dd6, + 0x2dd8, 0x2dde, + 0x3005, 0x3006, + 0x3031, 0x3035, + 0x303b, 0x303c, + 0x3041, 0x3096, + 0x309d, 0x309f, + 0x30a1, 0x30fa, + 0x30fc, 0x30ff, + 0x3105, 0x312d, + 0x3131, 0x318e, + 0x31a0, 0x31ba, + 0x31f0, 0x31ff, + 0x3400, 0x4db5, + 0x4e00, 0x9fcc, + 0xa000, 0xa48c, + 0xa4d0, 0xa4fd, + 0xa500, 0xa60c, + 0xa610, 0xa61f, + 0xa62a, 0xa62b, + 0xa640, 0xa66e, + 0xa67f, 0xa69d, + 0xa6a0, 0xa6e5, + 0xa717, 0xa71f, + 0xa722, 0xa788, + 0xa78b, 0xa78e, + 0xa790, 0xa7ad, + 0xa7b0, 0xa7b1, + 0xa7f7, 0xa801, + 0xa803, 0xa805, + 0xa807, 0xa80a, + 0xa80c, 0xa822, + 0xa840, 0xa873, + 0xa882, 0xa8b3, + 0xa8f2, 0xa8f7, + 0xa90a, 0xa925, + 0xa930, 0xa946, + 0xa960, 0xa97c, + 0xa984, 0xa9b2, + 0xa9e0, 0xa9e4, + 0xa9e6, 0xa9ef, + 0xa9fa, 0xa9fe, + 0xaa00, 0xaa28, + 0xaa40, 0xaa42, + 0xaa44, 0xaa4b, + 0xaa60, 0xaa76, + 0xaa7e, 0xaaaf, + 0xaab5, 0xaab6, + 0xaab9, 0xaabd, + 0xaadb, 0xaadd, + 0xaae0, 0xaaea, + 0xaaf2, 0xaaf4, + 0xab01, 0xab06, + 0xab09, 0xab0e, + 0xab11, 0xab16, + 0xab20, 0xab26, + 0xab28, 0xab2e, + 0xab30, 0xab5a, + 0xab5c, 0xab5f, + 0xab64, 0xab65, + 0xabc0, 0xabe2, + 0xac00, 0xd7a3, + 0xd7b0, 0xd7c6, + 0xd7cb, 0xd7fb, + 0xf900, 0xfa6d, + 0xfa70, 0xfad9, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdfb, + 0xfe70, 0xfe74, + 0xfe76, 0xfefc, + 0xff21, 0xff3a, + 0xff41, 0xff5a, + 0xff66, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, + 0x10000, 0x1000b, + 0x1000d, 0x10026, + 0x10028, 0x1003a, + 0x1003c, 0x1003d, + 0x1003f, 0x1004d, + 0x10050, 0x1005d, + 0x10080, 0x100fa, + 0x10280, 0x1029c, + 0x102a0, 0x102d0, + 0x10300, 0x1031f, + 0x10330, 0x10340, + 0x10342, 0x10349, + 0x10350, 0x10375, + 0x10380, 0x1039d, + 0x103a0, 0x103c3, + 0x103c8, 0x103cf, + 0x10400, 0x1049d, + 0x10500, 0x10527, + 0x10530, 0x10563, + 0x10600, 0x10736, + 0x10740, 0x10755, + 0x10760, 0x10767, + 0x10800, 0x10805, + 0x1080a, 0x10835, + 0x10837, 0x10838, + 0x1083f, 0x10855, + 0x10860, 0x10876, + 0x10880, 0x1089e, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109b7, + 0x109be, 0x109bf, + 0x10a10, 0x10a13, + 0x10a15, 0x10a17, + 0x10a19, 0x10a33, + 0x10a60, 0x10a7c, + 0x10a80, 0x10a9c, + 0x10ac0, 0x10ac7, + 0x10ac9, 0x10ae4, + 0x10b00, 0x10b35, + 0x10b40, 0x10b55, + 0x10b60, 0x10b72, + 0x10b80, 0x10b91, + 0x10c00, 0x10c48, + 0x11003, 0x11037, + 0x11083, 0x110af, + 0x110d0, 0x110e8, + 0x11103, 0x11126, + 0x11150, 0x11172, + 0x11183, 0x111b2, + 0x111c1, 0x111c4, + 0x11200, 0x11211, + 0x11213, 0x1122b, + 0x112b0, 0x112de, + 0x11305, 0x1130c, + 0x1130f, 0x11310, + 0x11313, 0x11328, + 0x1132a, 0x11330, + 0x11332, 0x11333, + 0x11335, 0x11339, + 0x1135d, 0x11361, + 0x11480, 0x114af, + 0x114c4, 0x114c5, + 0x11580, 0x115ae, + 0x11600, 0x1162f, + 0x11680, 0x116aa, + 0x118a0, 0x118df, + 0x11ac0, 0x11af8, + 0x12000, 0x12398, + 0x13000, 0x1342e, + 0x16800, 0x16a38, + 0x16a40, 0x16a5e, + 0x16ad0, 0x16aed, + 0x16b00, 0x16b2f, + 0x16b40, 0x16b43, + 0x16b63, 0x16b77, + 0x16b7d, 0x16b8f, + 0x16f00, 0x16f44, + 0x16f93, 0x16f9f, + 0x1b000, 0x1b001, + 0x1bc00, 0x1bc6a, + 0x1bc70, 0x1bc7c, + 0x1bc80, 0x1bc88, + 0x1bc90, 0x1bc99, + 0x1d400, 0x1d454, + 0x1d456, 0x1d49c, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d51e, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d552, 0x1d6a5, + 0x1d6a8, 0x1d6c0, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6fa, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d734, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d76e, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d7a8, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7cb, + 0x1e800, 0x1e8c4, + 0x1ee00, 0x1ee03, + 0x1ee05, 0x1ee1f, + 0x1ee21, 0x1ee22, + 0x1ee29, 0x1ee32, + 0x1ee34, 0x1ee37, + 0x1ee4d, 0x1ee4f, + 0x1ee51, 0x1ee52, + 0x1ee61, 0x1ee62, + 0x1ee67, 0x1ee6a, + 0x1ee6c, 0x1ee72, + 0x1ee74, 0x1ee77, + 0x1ee79, 0x1ee7c, + 0x1ee80, 0x1ee89, + 0x1ee8b, 0x1ee9b, + 0x1eea1, 0x1eea3, + 0x1eea5, 0x1eea9, + 0x1eeab, 0x1eebb, + 0x20000, 0x2a6d6, + 0x2a700, 0x2b734, + 0x2b740, 0x2b81d, + 0x2f800, 0x2fa1d, +}; + +} // !namespace + +namespace { + +const char32_t isalphas[] = { + 0x00aa, + 0x00b5, + 0x00ba, + 0x02ec, + 0x02ee, + 0x037f, + 0x0386, + 0x038c, + 0x0559, + 0x06d5, + 0x06ff, + 0x0710, + 0x07b1, + 0x07fa, + 0x081a, + 0x0824, + 0x0828, + 0x093d, + 0x0950, + 0x09b2, + 0x09bd, + 0x09ce, + 0x0a5e, + 0x0abd, + 0x0ad0, + 0x0b3d, + 0x0b71, + 0x0b83, + 0x0b9c, + 0x0bd0, + 0x0c3d, + 0x0cbd, + 0x0cde, + 0x0d3d, + 0x0d4e, + 0x0dbd, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0ebd, + 0x0ec6, + 0x0f00, + 0x103f, + 0x1061, + 0x108e, + 0x10c7, + 0x10cd, + 0x1258, + 0x12c0, + 0x17d7, + 0x17dc, + 0x18aa, + 0x1aa7, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1fbe, + 0x2071, + 0x207f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x214e, + 0x2d27, + 0x2d2d, + 0x2d6f, + 0x2e2f, + 0xa8fb, + 0xa9cf, + 0xaa7a, + 0xaab1, + 0xaac0, + 0xaac2, + 0xfb1d, + 0xfb3e, + 0x10808, + 0x1083c, + 0x10a00, + 0x11176, + 0x111da, + 0x1133d, + 0x114c7, + 0x11644, + 0x118ff, + 0x16f50, + 0x1d4a2, + 0x1d4bb, + 0x1d546, + 0x1ee24, + 0x1ee27, + 0x1ee39, + 0x1ee3b, + 0x1ee42, + 0x1ee47, + 0x1ee49, + 0x1ee4b, + 0x1ee54, + 0x1ee57, + 0x1ee59, + 0x1ee5b, + 0x1ee5d, + 0x1ee5f, + 0x1ee64, + 0x1ee7e, +}; + +} // !namespace + +bool isalpha(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, isalphar, nelem (isalphar)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, isalphas, nelem (isalphas), 1); + + if (p && c == p[0]) + return true; + + return false; +} + +namespace { + +const char32_t isupperr[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03d2, 0x03d4, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x210b, 0x210d, + 0x2110, 0x2112, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x2130, 0x2133, + 0x213e, 0x213f, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xa7aa, 0xa7ad, + 0xa7b0, 0xa7b1, + 0xff21, 0xff3a, + 0x10400, 0x10427, + 0x118a0, 0x118bf, + 0x1d400, 0x1d419, + 0x1d434, 0x1d44d, + 0x1d468, 0x1d481, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b5, + 0x1d4d0, 0x1d4e9, + 0x1d504, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d538, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d56c, 0x1d585, + 0x1d5a0, 0x1d5b9, + 0x1d5d4, 0x1d5ed, + 0x1d608, 0x1d621, + 0x1d63c, 0x1d655, + 0x1d670, 0x1d689, + 0x1d6a8, 0x1d6c0, + 0x1d6e2, 0x1d6fa, + 0x1d71c, 0x1d734, + 0x1d756, 0x1d76e, + 0x1d790, 0x1d7a8, +}; + +} // !namespace + +namespace { + +const char32_t isuppers[] = { + 0x0100, + 0x0102, + 0x0104, + 0x0106, + 0x0108, + 0x010a, + 0x010c, + 0x010e, + 0x0110, + 0x0112, + 0x0114, + 0x0116, + 0x0118, + 0x011a, + 0x011c, + 0x011e, + 0x0120, + 0x0122, + 0x0124, + 0x0126, + 0x0128, + 0x012a, + 0x012c, + 0x012e, + 0x0130, + 0x0132, + 0x0134, + 0x0136, + 0x0139, + 0x013b, + 0x013d, + 0x013f, + 0x0141, + 0x0143, + 0x0145, + 0x0147, + 0x014a, + 0x014c, + 0x014e, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x015a, + 0x015c, + 0x015e, + 0x0160, + 0x0162, + 0x0164, + 0x0166, + 0x0168, + 0x016a, + 0x016c, + 0x016e, + 0x0170, + 0x0172, + 0x0174, + 0x0176, + 0x017b, + 0x017d, + 0x0184, + 0x01a2, + 0x01a4, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c4, + 0x01c7, + 0x01ca, + 0x01cd, + 0x01cf, + 0x01d1, + 0x01d3, + 0x01d5, + 0x01d7, + 0x01d9, + 0x01db, + 0x01de, + 0x01e0, + 0x01e2, + 0x01e4, + 0x01e6, + 0x01e8, + 0x01ea, + 0x01ec, + 0x01ee, + 0x01f1, + 0x01f4, + 0x01fa, + 0x01fc, + 0x01fe, + 0x0200, + 0x0202, + 0x0204, + 0x0206, + 0x0208, + 0x020a, + 0x020c, + 0x020e, + 0x0210, + 0x0212, + 0x0214, + 0x0216, + 0x0218, + 0x021a, + 0x021c, + 0x021e, + 0x0220, + 0x0222, + 0x0224, + 0x0226, + 0x0228, + 0x022a, + 0x022c, + 0x022e, + 0x0230, + 0x0232, + 0x0241, + 0x0248, + 0x024a, + 0x024c, + 0x024e, + 0x0370, + 0x0372, + 0x0376, + 0x037f, + 0x0386, + 0x038c, + 0x03cf, + 0x03d8, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x03e2, + 0x03e4, + 0x03e6, + 0x03e8, + 0x03ea, + 0x03ec, + 0x03ee, + 0x03f4, + 0x03f7, + 0x0460, + 0x0462, + 0x0464, + 0x0466, + 0x0468, + 0x046a, + 0x046c, + 0x046e, + 0x0470, + 0x0472, + 0x0474, + 0x0476, + 0x0478, + 0x047a, + 0x047c, + 0x047e, + 0x0480, + 0x048a, + 0x048c, + 0x048e, + 0x0490, + 0x0492, + 0x0494, + 0x0496, + 0x0498, + 0x049a, + 0x049c, + 0x049e, + 0x04a0, + 0x04a2, + 0x04a4, + 0x04a6, + 0x04a8, + 0x04aa, + 0x04ac, + 0x04ae, + 0x04b0, + 0x04b2, + 0x04b4, + 0x04b6, + 0x04b8, + 0x04ba, + 0x04bc, + 0x04be, + 0x04c3, + 0x04c5, + 0x04c7, + 0x04c9, + 0x04cb, + 0x04cd, + 0x04d0, + 0x04d2, + 0x04d4, + 0x04d6, + 0x04d8, + 0x04da, + 0x04dc, + 0x04de, + 0x04e0, + 0x04e2, + 0x04e4, + 0x04e6, + 0x04e8, + 0x04ea, + 0x04ec, + 0x04ee, + 0x04f0, + 0x04f2, + 0x04f4, + 0x04f6, + 0x04f8, + 0x04fa, + 0x04fc, + 0x04fe, + 0x0500, + 0x0502, + 0x0504, + 0x0506, + 0x0508, + 0x050a, + 0x050c, + 0x050e, + 0x0510, + 0x0512, + 0x0514, + 0x0516, + 0x0518, + 0x051a, + 0x051c, + 0x051e, + 0x0520, + 0x0522, + 0x0524, + 0x0526, + 0x0528, + 0x052a, + 0x052c, + 0x052e, + 0x10c7, + 0x10cd, + 0x1e00, + 0x1e02, + 0x1e04, + 0x1e06, + 0x1e08, + 0x1e0a, + 0x1e0c, + 0x1e0e, + 0x1e10, + 0x1e12, + 0x1e14, + 0x1e16, + 0x1e18, + 0x1e1a, + 0x1e1c, + 0x1e1e, + 0x1e20, + 0x1e22, + 0x1e24, + 0x1e26, + 0x1e28, + 0x1e2a, + 0x1e2c, + 0x1e2e, + 0x1e30, + 0x1e32, + 0x1e34, + 0x1e36, + 0x1e38, + 0x1e3a, + 0x1e3c, + 0x1e3e, + 0x1e40, + 0x1e42, + 0x1e44, + 0x1e46, + 0x1e48, + 0x1e4a, + 0x1e4c, + 0x1e4e, + 0x1e50, + 0x1e52, + 0x1e54, + 0x1e56, + 0x1e58, + 0x1e5a, + 0x1e5c, + 0x1e5e, + 0x1e60, + 0x1e62, + 0x1e64, + 0x1e66, + 0x1e68, + 0x1e6a, + 0x1e6c, + 0x1e6e, + 0x1e70, + 0x1e72, + 0x1e74, + 0x1e76, + 0x1e78, + 0x1e7a, + 0x1e7c, + 0x1e7e, + 0x1e80, + 0x1e82, + 0x1e84, + 0x1e86, + 0x1e88, + 0x1e8a, + 0x1e8c, + 0x1e8e, + 0x1e90, + 0x1e92, + 0x1e94, + 0x1e9e, + 0x1ea0, + 0x1ea2, + 0x1ea4, + 0x1ea6, + 0x1ea8, + 0x1eaa, + 0x1eac, + 0x1eae, + 0x1eb0, + 0x1eb2, + 0x1eb4, + 0x1eb6, + 0x1eb8, + 0x1eba, + 0x1ebc, + 0x1ebe, + 0x1ec0, + 0x1ec2, + 0x1ec4, + 0x1ec6, + 0x1ec8, + 0x1eca, + 0x1ecc, + 0x1ece, + 0x1ed0, + 0x1ed2, + 0x1ed4, + 0x1ed6, + 0x1ed8, + 0x1eda, + 0x1edc, + 0x1ede, + 0x1ee0, + 0x1ee2, + 0x1ee4, + 0x1ee6, + 0x1ee8, + 0x1eea, + 0x1eec, + 0x1eee, + 0x1ef0, + 0x1ef2, + 0x1ef4, + 0x1ef6, + 0x1ef8, + 0x1efa, + 0x1efc, + 0x1efe, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1f5f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x2145, + 0x2183, + 0x2c60, + 0x2c67, + 0x2c69, + 0x2c6b, + 0x2c72, + 0x2c75, + 0x2c82, + 0x2c84, + 0x2c86, + 0x2c88, + 0x2c8a, + 0x2c8c, + 0x2c8e, + 0x2c90, + 0x2c92, + 0x2c94, + 0x2c96, + 0x2c98, + 0x2c9a, + 0x2c9c, + 0x2c9e, + 0x2ca0, + 0x2ca2, + 0x2ca4, + 0x2ca6, + 0x2ca8, + 0x2caa, + 0x2cac, + 0x2cae, + 0x2cb0, + 0x2cb2, + 0x2cb4, + 0x2cb6, + 0x2cb8, + 0x2cba, + 0x2cbc, + 0x2cbe, + 0x2cc0, + 0x2cc2, + 0x2cc4, + 0x2cc6, + 0x2cc8, + 0x2cca, + 0x2ccc, + 0x2cce, + 0x2cd0, + 0x2cd2, + 0x2cd4, + 0x2cd6, + 0x2cd8, + 0x2cda, + 0x2cdc, + 0x2cde, + 0x2ce0, + 0x2ce2, + 0x2ceb, + 0x2ced, + 0x2cf2, + 0xa640, + 0xa642, + 0xa644, + 0xa646, + 0xa648, + 0xa64a, + 0xa64c, + 0xa64e, + 0xa650, + 0xa652, + 0xa654, + 0xa656, + 0xa658, + 0xa65a, + 0xa65c, + 0xa65e, + 0xa660, + 0xa662, + 0xa664, + 0xa666, + 0xa668, + 0xa66a, + 0xa66c, + 0xa680, + 0xa682, + 0xa684, + 0xa686, + 0xa688, + 0xa68a, + 0xa68c, + 0xa68e, + 0xa690, + 0xa692, + 0xa694, + 0xa696, + 0xa698, + 0xa69a, + 0xa722, + 0xa724, + 0xa726, + 0xa728, + 0xa72a, + 0xa72c, + 0xa72e, + 0xa732, + 0xa734, + 0xa736, + 0xa738, + 0xa73a, + 0xa73c, + 0xa73e, + 0xa740, + 0xa742, + 0xa744, + 0xa746, + 0xa748, + 0xa74a, + 0xa74c, + 0xa74e, + 0xa750, + 0xa752, + 0xa754, + 0xa756, + 0xa758, + 0xa75a, + 0xa75c, + 0xa75e, + 0xa760, + 0xa762, + 0xa764, + 0xa766, + 0xa768, + 0xa76a, + 0xa76c, + 0xa76e, + 0xa779, + 0xa77b, + 0xa780, + 0xa782, + 0xa784, + 0xa786, + 0xa78b, + 0xa78d, + 0xa790, + 0xa792, + 0xa796, + 0xa798, + 0xa79a, + 0xa79c, + 0xa79e, + 0xa7a0, + 0xa7a2, + 0xa7a4, + 0xa7a6, + 0xa7a8, + 0x1d49c, + 0x1d4a2, + 0x1d546, + 0x1d7ca, +}; + +} // !namespace + +bool isupper(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, isupperr, nelem (isupperr)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, isuppers, nelem (isuppers), 1); + + if (p && c == p[0]) + return true; + + return false; +} + +namespace { + +const char32_t islowerr[] = { + 0x0061, 0x007a, + 0x00df, 0x00f6, + 0x00f8, 0x00ff, + 0x0137, 0x0138, + 0x0148, 0x0149, + 0x017e, 0x0180, + 0x018c, 0x018d, + 0x0199, 0x019b, + 0x01aa, 0x01ab, + 0x01b9, 0x01ba, + 0x01bd, 0x01bf, + 0x01dc, 0x01dd, + 0x01ef, 0x01f0, + 0x0233, 0x0239, + 0x023f, 0x0240, + 0x024f, 0x0293, + 0x0295, 0x02af, + 0x037b, 0x037d, + 0x03ac, 0x03ce, + 0x03d0, 0x03d1, + 0x03d5, 0x03d7, + 0x03ef, 0x03f3, + 0x03fb, 0x03fc, + 0x0430, 0x045f, + 0x04ce, 0x04cf, + 0x0561, 0x0587, + 0x1d00, 0x1d2b, + 0x1d6b, 0x1d77, + 0x1d79, 0x1d9a, + 0x1e95, 0x1e9d, + 0x1eff, 0x1f07, + 0x1f10, 0x1f15, + 0x1f20, 0x1f27, + 0x1f30, 0x1f37, + 0x1f40, 0x1f45, + 0x1f50, 0x1f57, + 0x1f60, 0x1f67, + 0x1f70, 0x1f7d, + 0x1f80, 0x1f87, + 0x1f90, 0x1f97, + 0x1fa0, 0x1fa7, + 0x1fb0, 0x1fb4, + 0x1fb6, 0x1fb7, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fc7, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fd7, + 0x1fe0, 0x1fe7, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ff7, + 0x210e, 0x210f, + 0x213c, 0x213d, + 0x2146, 0x2149, + 0x2170, 0x217f, + 0x24d0, 0x24e9, + 0x2c30, 0x2c5e, + 0x2c65, 0x2c66, + 0x2c73, 0x2c74, + 0x2c76, 0x2c7b, + 0x2ce3, 0x2ce4, + 0x2d00, 0x2d25, + 0xa72f, 0xa731, + 0xa771, 0xa778, + 0xa793, 0xa795, + 0xab30, 0xab5a, + 0xab64, 0xab65, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xff41, 0xff5a, + 0x10428, 0x1044f, + 0x118c0, 0x118df, + 0x1d41a, 0x1d433, + 0x1d44e, 0x1d454, + 0x1d456, 0x1d467, + 0x1d482, 0x1d49b, + 0x1d4b6, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d4cf, + 0x1d4ea, 0x1d503, + 0x1d51e, 0x1d537, + 0x1d552, 0x1d56b, + 0x1d586, 0x1d59f, + 0x1d5ba, 0x1d5d3, + 0x1d5ee, 0x1d607, + 0x1d622, 0x1d63b, + 0x1d656, 0x1d66f, + 0x1d68a, 0x1d6a5, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6e1, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d71b, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d755, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d78f, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7c9, +}; + +} // !namespace + +namespace { + +const char32_t islowers[] = { + 0x00b5, + 0x0101, + 0x0103, + 0x0105, + 0x0107, + 0x0109, + 0x010b, + 0x010d, + 0x010f, + 0x0111, + 0x0113, + 0x0115, + 0x0117, + 0x0119, + 0x011b, + 0x011d, + 0x011f, + 0x0121, + 0x0123, + 0x0125, + 0x0127, + 0x0129, + 0x012b, + 0x012d, + 0x012f, + 0x0131, + 0x0133, + 0x0135, + 0x013a, + 0x013c, + 0x013e, + 0x0140, + 0x0142, + 0x0144, + 0x0146, + 0x014b, + 0x014d, + 0x014f, + 0x0151, + 0x0153, + 0x0155, + 0x0157, + 0x0159, + 0x015b, + 0x015d, + 0x015f, + 0x0161, + 0x0163, + 0x0165, + 0x0167, + 0x0169, + 0x016b, + 0x016d, + 0x016f, + 0x0171, + 0x0173, + 0x0175, + 0x0177, + 0x017a, + 0x017c, + 0x0183, + 0x0185, + 0x0188, + 0x0192, + 0x0195, + 0x019e, + 0x01a1, + 0x01a3, + 0x01a5, + 0x01a8, + 0x01ad, + 0x01b0, + 0x01b4, + 0x01b6, + 0x01c6, + 0x01c9, + 0x01cc, + 0x01ce, + 0x01d0, + 0x01d2, + 0x01d4, + 0x01d6, + 0x01d8, + 0x01da, + 0x01df, + 0x01e1, + 0x01e3, + 0x01e5, + 0x01e7, + 0x01e9, + 0x01eb, + 0x01ed, + 0x01f3, + 0x01f5, + 0x01f9, + 0x01fb, + 0x01fd, + 0x01ff, + 0x0201, + 0x0203, + 0x0205, + 0x0207, + 0x0209, + 0x020b, + 0x020d, + 0x020f, + 0x0211, + 0x0213, + 0x0215, + 0x0217, + 0x0219, + 0x021b, + 0x021d, + 0x021f, + 0x0221, + 0x0223, + 0x0225, + 0x0227, + 0x0229, + 0x022b, + 0x022d, + 0x022f, + 0x0231, + 0x023c, + 0x0242, + 0x0247, + 0x0249, + 0x024b, + 0x024d, + 0x0371, + 0x0373, + 0x0377, + 0x0390, + 0x03d9, + 0x03db, + 0x03dd, + 0x03df, + 0x03e1, + 0x03e3, + 0x03e5, + 0x03e7, + 0x03e9, + 0x03eb, + 0x03ed, + 0x03f5, + 0x03f8, + 0x0461, + 0x0463, + 0x0465, + 0x0467, + 0x0469, + 0x046b, + 0x046d, + 0x046f, + 0x0471, + 0x0473, + 0x0475, + 0x0477, + 0x0479, + 0x047b, + 0x047d, + 0x047f, + 0x0481, + 0x048b, + 0x048d, + 0x048f, + 0x0491, + 0x0493, + 0x0495, + 0x0497, + 0x0499, + 0x049b, + 0x049d, + 0x049f, + 0x04a1, + 0x04a3, + 0x04a5, + 0x04a7, + 0x04a9, + 0x04ab, + 0x04ad, + 0x04af, + 0x04b1, + 0x04b3, + 0x04b5, + 0x04b7, + 0x04b9, + 0x04bb, + 0x04bd, + 0x04bf, + 0x04c2, + 0x04c4, + 0x04c6, + 0x04c8, + 0x04ca, + 0x04cc, + 0x04d1, + 0x04d3, + 0x04d5, + 0x04d7, + 0x04d9, + 0x04db, + 0x04dd, + 0x04df, + 0x04e1, + 0x04e3, + 0x04e5, + 0x04e7, + 0x04e9, + 0x04eb, + 0x04ed, + 0x04ef, + 0x04f1, + 0x04f3, + 0x04f5, + 0x04f7, + 0x04f9, + 0x04fb, + 0x04fd, + 0x04ff, + 0x0501, + 0x0503, + 0x0505, + 0x0507, + 0x0509, + 0x050b, + 0x050d, + 0x050f, + 0x0511, + 0x0513, + 0x0515, + 0x0517, + 0x0519, + 0x051b, + 0x051d, + 0x051f, + 0x0521, + 0x0523, + 0x0525, + 0x0527, + 0x0529, + 0x052b, + 0x052d, + 0x052f, + 0x1e01, + 0x1e03, + 0x1e05, + 0x1e07, + 0x1e09, + 0x1e0b, + 0x1e0d, + 0x1e0f, + 0x1e11, + 0x1e13, + 0x1e15, + 0x1e17, + 0x1e19, + 0x1e1b, + 0x1e1d, + 0x1e1f, + 0x1e21, + 0x1e23, + 0x1e25, + 0x1e27, + 0x1e29, + 0x1e2b, + 0x1e2d, + 0x1e2f, + 0x1e31, + 0x1e33, + 0x1e35, + 0x1e37, + 0x1e39, + 0x1e3b, + 0x1e3d, + 0x1e3f, + 0x1e41, + 0x1e43, + 0x1e45, + 0x1e47, + 0x1e49, + 0x1e4b, + 0x1e4d, + 0x1e4f, + 0x1e51, + 0x1e53, + 0x1e55, + 0x1e57, + 0x1e59, + 0x1e5b, + 0x1e5d, + 0x1e5f, + 0x1e61, + 0x1e63, + 0x1e65, + 0x1e67, + 0x1e69, + 0x1e6b, + 0x1e6d, + 0x1e6f, + 0x1e71, + 0x1e73, + 0x1e75, + 0x1e77, + 0x1e79, + 0x1e7b, + 0x1e7d, + 0x1e7f, + 0x1e81, + 0x1e83, + 0x1e85, + 0x1e87, + 0x1e89, + 0x1e8b, + 0x1e8d, + 0x1e8f, + 0x1e91, + 0x1e93, + 0x1e9f, + 0x1ea1, + 0x1ea3, + 0x1ea5, + 0x1ea7, + 0x1ea9, + 0x1eab, + 0x1ead, + 0x1eaf, + 0x1eb1, + 0x1eb3, + 0x1eb5, + 0x1eb7, + 0x1eb9, + 0x1ebb, + 0x1ebd, + 0x1ebf, + 0x1ec1, + 0x1ec3, + 0x1ec5, + 0x1ec7, + 0x1ec9, + 0x1ecb, + 0x1ecd, + 0x1ecf, + 0x1ed1, + 0x1ed3, + 0x1ed5, + 0x1ed7, + 0x1ed9, + 0x1edb, + 0x1edd, + 0x1edf, + 0x1ee1, + 0x1ee3, + 0x1ee5, + 0x1ee7, + 0x1ee9, + 0x1eeb, + 0x1eed, + 0x1eef, + 0x1ef1, + 0x1ef3, + 0x1ef5, + 0x1ef7, + 0x1ef9, + 0x1efb, + 0x1efd, + 0x1fbe, + 0x210a, + 0x2113, + 0x212f, + 0x2134, + 0x2139, + 0x214e, + 0x2184, + 0x2c61, + 0x2c68, + 0x2c6a, + 0x2c6c, + 0x2c71, + 0x2c81, + 0x2c83, + 0x2c85, + 0x2c87, + 0x2c89, + 0x2c8b, + 0x2c8d, + 0x2c8f, + 0x2c91, + 0x2c93, + 0x2c95, + 0x2c97, + 0x2c99, + 0x2c9b, + 0x2c9d, + 0x2c9f, + 0x2ca1, + 0x2ca3, + 0x2ca5, + 0x2ca7, + 0x2ca9, + 0x2cab, + 0x2cad, + 0x2caf, + 0x2cb1, + 0x2cb3, + 0x2cb5, + 0x2cb7, + 0x2cb9, + 0x2cbb, + 0x2cbd, + 0x2cbf, + 0x2cc1, + 0x2cc3, + 0x2cc5, + 0x2cc7, + 0x2cc9, + 0x2ccb, + 0x2ccd, + 0x2ccf, + 0x2cd1, + 0x2cd3, + 0x2cd5, + 0x2cd7, + 0x2cd9, + 0x2cdb, + 0x2cdd, + 0x2cdf, + 0x2ce1, + 0x2cec, + 0x2cee, + 0x2cf3, + 0x2d27, + 0x2d2d, + 0xa641, + 0xa643, + 0xa645, + 0xa647, + 0xa649, + 0xa64b, + 0xa64d, + 0xa64f, + 0xa651, + 0xa653, + 0xa655, + 0xa657, + 0xa659, + 0xa65b, + 0xa65d, + 0xa65f, + 0xa661, + 0xa663, + 0xa665, + 0xa667, + 0xa669, + 0xa66b, + 0xa66d, + 0xa681, + 0xa683, + 0xa685, + 0xa687, + 0xa689, + 0xa68b, + 0xa68d, + 0xa68f, + 0xa691, + 0xa693, + 0xa695, + 0xa697, + 0xa699, + 0xa69b, + 0xa723, + 0xa725, + 0xa727, + 0xa729, + 0xa72b, + 0xa72d, + 0xa733, + 0xa735, + 0xa737, + 0xa739, + 0xa73b, + 0xa73d, + 0xa73f, + 0xa741, + 0xa743, + 0xa745, + 0xa747, + 0xa749, + 0xa74b, + 0xa74d, + 0xa74f, + 0xa751, + 0xa753, + 0xa755, + 0xa757, + 0xa759, + 0xa75b, + 0xa75d, + 0xa75f, + 0xa761, + 0xa763, + 0xa765, + 0xa767, + 0xa769, + 0xa76b, + 0xa76d, + 0xa76f, + 0xa77a, + 0xa77c, + 0xa77f, + 0xa781, + 0xa783, + 0xa785, + 0xa787, + 0xa78c, + 0xa78e, + 0xa791, + 0xa797, + 0xa799, + 0xa79b, + 0xa79d, + 0xa79f, + 0xa7a1, + 0xa7a3, + 0xa7a5, + 0xa7a7, + 0xa7a9, + 0xa7fa, + 0x1d4bb, + 0x1d7cb, +}; + +} // !namespace + +bool islower(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, islowerr, nelem (islowerr)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, islowers, nelem (islowers), 1); + + if (p && c == p[0]) + return true; + + return false; +} + +namespace { + +const char32_t istitler[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xa7aa, 0xa7ad, + 0xa7b0, 0xa7b1, + 0xff21, 0xff3a, + 0x10400, 0x10427, + 0x118a0, 0x118bf, +}; + +} // !namespace + +namespace { + +const char32_t istitles[] = { + 0x0100, + 0x0102, + 0x0104, + 0x0106, + 0x0108, + 0x010a, + 0x010c, + 0x010e, + 0x0110, + 0x0112, + 0x0114, + 0x0116, + 0x0118, + 0x011a, + 0x011c, + 0x011e, + 0x0120, + 0x0122, + 0x0124, + 0x0126, + 0x0128, + 0x012a, + 0x012c, + 0x012e, + 0x0132, + 0x0134, + 0x0136, + 0x0139, + 0x013b, + 0x013d, + 0x013f, + 0x0141, + 0x0143, + 0x0145, + 0x0147, + 0x014a, + 0x014c, + 0x014e, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x015a, + 0x015c, + 0x015e, + 0x0160, + 0x0162, + 0x0164, + 0x0166, + 0x0168, + 0x016a, + 0x016c, + 0x016e, + 0x0170, + 0x0172, + 0x0174, + 0x0176, + 0x017b, + 0x017d, + 0x0184, + 0x01a2, + 0x01a4, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c5, + 0x01c8, + 0x01cb, + 0x01cd, + 0x01cf, + 0x01d1, + 0x01d3, + 0x01d5, + 0x01d7, + 0x01d9, + 0x01db, + 0x01de, + 0x01e0, + 0x01e2, + 0x01e4, + 0x01e6, + 0x01e8, + 0x01ea, + 0x01ec, + 0x01ee, + 0x01f2, + 0x01f4, + 0x01fa, + 0x01fc, + 0x01fe, + 0x0200, + 0x0202, + 0x0204, + 0x0206, + 0x0208, + 0x020a, + 0x020c, + 0x020e, + 0x0210, + 0x0212, + 0x0214, + 0x0216, + 0x0218, + 0x021a, + 0x021c, + 0x021e, + 0x0220, + 0x0222, + 0x0224, + 0x0226, + 0x0228, + 0x022a, + 0x022c, + 0x022e, + 0x0230, + 0x0232, + 0x0241, + 0x0248, + 0x024a, + 0x024c, + 0x024e, + 0x0370, + 0x0372, + 0x0376, + 0x037f, + 0x0386, + 0x038c, + 0x03cf, + 0x03d8, + 0x03da, + 0x03dc, + 0x03de, + 0x03e0, + 0x03e2, + 0x03e4, + 0x03e6, + 0x03e8, + 0x03ea, + 0x03ec, + 0x03ee, + 0x03f7, + 0x0460, + 0x0462, + 0x0464, + 0x0466, + 0x0468, + 0x046a, + 0x046c, + 0x046e, + 0x0470, + 0x0472, + 0x0474, + 0x0476, + 0x0478, + 0x047a, + 0x047c, + 0x047e, + 0x0480, + 0x048a, + 0x048c, + 0x048e, + 0x0490, + 0x0492, + 0x0494, + 0x0496, + 0x0498, + 0x049a, + 0x049c, + 0x049e, + 0x04a0, + 0x04a2, + 0x04a4, + 0x04a6, + 0x04a8, + 0x04aa, + 0x04ac, + 0x04ae, + 0x04b0, + 0x04b2, + 0x04b4, + 0x04b6, + 0x04b8, + 0x04ba, + 0x04bc, + 0x04be, + 0x04c3, + 0x04c5, + 0x04c7, + 0x04c9, + 0x04cb, + 0x04cd, + 0x04d0, + 0x04d2, + 0x04d4, + 0x04d6, + 0x04d8, + 0x04da, + 0x04dc, + 0x04de, + 0x04e0, + 0x04e2, + 0x04e4, + 0x04e6, + 0x04e8, + 0x04ea, + 0x04ec, + 0x04ee, + 0x04f0, + 0x04f2, + 0x04f4, + 0x04f6, + 0x04f8, + 0x04fa, + 0x04fc, + 0x04fe, + 0x0500, + 0x0502, + 0x0504, + 0x0506, + 0x0508, + 0x050a, + 0x050c, + 0x050e, + 0x0510, + 0x0512, + 0x0514, + 0x0516, + 0x0518, + 0x051a, + 0x051c, + 0x051e, + 0x0520, + 0x0522, + 0x0524, + 0x0526, + 0x0528, + 0x052a, + 0x052c, + 0x052e, + 0x10c7, + 0x10cd, + 0x1e00, + 0x1e02, + 0x1e04, + 0x1e06, + 0x1e08, + 0x1e0a, + 0x1e0c, + 0x1e0e, + 0x1e10, + 0x1e12, + 0x1e14, + 0x1e16, + 0x1e18, + 0x1e1a, + 0x1e1c, + 0x1e1e, + 0x1e20, + 0x1e22, + 0x1e24, + 0x1e26, + 0x1e28, + 0x1e2a, + 0x1e2c, + 0x1e2e, + 0x1e30, + 0x1e32, + 0x1e34, + 0x1e36, + 0x1e38, + 0x1e3a, + 0x1e3c, + 0x1e3e, + 0x1e40, + 0x1e42, + 0x1e44, + 0x1e46, + 0x1e48, + 0x1e4a, + 0x1e4c, + 0x1e4e, + 0x1e50, + 0x1e52, + 0x1e54, + 0x1e56, + 0x1e58, + 0x1e5a, + 0x1e5c, + 0x1e5e, + 0x1e60, + 0x1e62, + 0x1e64, + 0x1e66, + 0x1e68, + 0x1e6a, + 0x1e6c, + 0x1e6e, + 0x1e70, + 0x1e72, + 0x1e74, + 0x1e76, + 0x1e78, + 0x1e7a, + 0x1e7c, + 0x1e7e, + 0x1e80, + 0x1e82, + 0x1e84, + 0x1e86, + 0x1e88, + 0x1e8a, + 0x1e8c, + 0x1e8e, + 0x1e90, + 0x1e92, + 0x1e94, + 0x1ea0, + 0x1ea2, + 0x1ea4, + 0x1ea6, + 0x1ea8, + 0x1eaa, + 0x1eac, + 0x1eae, + 0x1eb0, + 0x1eb2, + 0x1eb4, + 0x1eb6, + 0x1eb8, + 0x1eba, + 0x1ebc, + 0x1ebe, + 0x1ec0, + 0x1ec2, + 0x1ec4, + 0x1ec6, + 0x1ec8, + 0x1eca, + 0x1ecc, + 0x1ece, + 0x1ed0, + 0x1ed2, + 0x1ed4, + 0x1ed6, + 0x1ed8, + 0x1eda, + 0x1edc, + 0x1ede, + 0x1ee0, + 0x1ee2, + 0x1ee4, + 0x1ee6, + 0x1ee8, + 0x1eea, + 0x1eec, + 0x1eee, + 0x1ef0, + 0x1ef2, + 0x1ef4, + 0x1ef6, + 0x1ef8, + 0x1efa, + 0x1efc, + 0x1efe, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1f5f, + 0x2132, + 0x2183, + 0x2c60, + 0x2c67, + 0x2c69, + 0x2c6b, + 0x2c72, + 0x2c75, + 0x2c82, + 0x2c84, + 0x2c86, + 0x2c88, + 0x2c8a, + 0x2c8c, + 0x2c8e, + 0x2c90, + 0x2c92, + 0x2c94, + 0x2c96, + 0x2c98, + 0x2c9a, + 0x2c9c, + 0x2c9e, + 0x2ca0, + 0x2ca2, + 0x2ca4, + 0x2ca6, + 0x2ca8, + 0x2caa, + 0x2cac, + 0x2cae, + 0x2cb0, + 0x2cb2, + 0x2cb4, + 0x2cb6, + 0x2cb8, + 0x2cba, + 0x2cbc, + 0x2cbe, + 0x2cc0, + 0x2cc2, + 0x2cc4, + 0x2cc6, + 0x2cc8, + 0x2cca, + 0x2ccc, + 0x2cce, + 0x2cd0, + 0x2cd2, + 0x2cd4, + 0x2cd6, + 0x2cd8, + 0x2cda, + 0x2cdc, + 0x2cde, + 0x2ce0, + 0x2ce2, + 0x2ceb, + 0x2ced, + 0x2cf2, + 0xa640, + 0xa642, + 0xa644, + 0xa646, + 0xa648, + 0xa64a, + 0xa64c, + 0xa64e, + 0xa650, + 0xa652, + 0xa654, + 0xa656, + 0xa658, + 0xa65a, + 0xa65c, + 0xa65e, + 0xa660, + 0xa662, + 0xa664, + 0xa666, + 0xa668, + 0xa66a, + 0xa66c, + 0xa680, + 0xa682, + 0xa684, + 0xa686, + 0xa688, + 0xa68a, + 0xa68c, + 0xa68e, + 0xa690, + 0xa692, + 0xa694, + 0xa696, + 0xa698, + 0xa69a, + 0xa722, + 0xa724, + 0xa726, + 0xa728, + 0xa72a, + 0xa72c, + 0xa72e, + 0xa732, + 0xa734, + 0xa736, + 0xa738, + 0xa73a, + 0xa73c, + 0xa73e, + 0xa740, + 0xa742, + 0xa744, + 0xa746, + 0xa748, + 0xa74a, + 0xa74c, + 0xa74e, + 0xa750, + 0xa752, + 0xa754, + 0xa756, + 0xa758, + 0xa75a, + 0xa75c, + 0xa75e, + 0xa760, + 0xa762, + 0xa764, + 0xa766, + 0xa768, + 0xa76a, + 0xa76c, + 0xa76e, + 0xa779, + 0xa77b, + 0xa780, + 0xa782, + 0xa784, + 0xa786, + 0xa78b, + 0xa78d, + 0xa790, + 0xa792, + 0xa796, + 0xa798, + 0xa79a, + 0xa79c, + 0xa79e, + 0xa7a0, + 0xa7a2, + 0xa7a4, + 0xa7a6, + 0xa7a8, +}; + +} // !namespace + +bool istitle(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, istitler, nelem (istitler)/2, 2); + + if (p && c >= p[0] && c <= p[1]) + return true; + + p = rbsearch(c, istitles, nelem (istitles), 1); + + if (p && c == p[0]) + return true; + + return false; +} + +namespace { + +const char32_t toupperr[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, + 0x118c0, 0x118df, 1048544, +}; + +} // !namespace + +namespace { + +const char32_t touppers[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0101, 1048575, + 0x0103, 1048575, + 0x0105, 1048575, + 0x0107, 1048575, + 0x0109, 1048575, + 0x010b, 1048575, + 0x010d, 1048575, + 0x010f, 1048575, + 0x0111, 1048575, + 0x0113, 1048575, + 0x0115, 1048575, + 0x0117, 1048575, + 0x0119, 1048575, + 0x011b, 1048575, + 0x011d, 1048575, + 0x011f, 1048575, + 0x0121, 1048575, + 0x0123, 1048575, + 0x0125, 1048575, + 0x0127, 1048575, + 0x0129, 1048575, + 0x012b, 1048575, + 0x012d, 1048575, + 0x012f, 1048575, + 0x0131, 1048344, + 0x0133, 1048575, + 0x0135, 1048575, + 0x0137, 1048575, + 0x013a, 1048575, + 0x013c, 1048575, + 0x013e, 1048575, + 0x0140, 1048575, + 0x0142, 1048575, + 0x0144, 1048575, + 0x0146, 1048575, + 0x0148, 1048575, + 0x014b, 1048575, + 0x014d, 1048575, + 0x014f, 1048575, + 0x0151, 1048575, + 0x0153, 1048575, + 0x0155, 1048575, + 0x0157, 1048575, + 0x0159, 1048575, + 0x015b, 1048575, + 0x015d, 1048575, + 0x015f, 1048575, + 0x0161, 1048575, + 0x0163, 1048575, + 0x0165, 1048575, + 0x0167, 1048575, + 0x0169, 1048575, + 0x016b, 1048575, + 0x016d, 1048575, + 0x016f, 1048575, + 0x0171, 1048575, + 0x0173, 1048575, + 0x0175, 1048575, + 0x0177, 1048575, + 0x017a, 1048575, + 0x017c, 1048575, + 0x017e, 1048575, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0183, 1048575, + 0x0185, 1048575, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a1, 1048575, + 0x01a3, 1048575, + 0x01a5, 1048575, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b4, 1048575, + 0x01b6, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c5, 1048575, + 0x01c6, 1048574, + 0x01c8, 1048575, + 0x01c9, 1048574, + 0x01cb, 1048575, + 0x01cc, 1048574, + 0x01ce, 1048575, + 0x01d0, 1048575, + 0x01d2, 1048575, + 0x01d4, 1048575, + 0x01d6, 1048575, + 0x01d8, 1048575, + 0x01da, 1048575, + 0x01dc, 1048575, + 0x01dd, 1048497, + 0x01df, 1048575, + 0x01e1, 1048575, + 0x01e3, 1048575, + 0x01e5, 1048575, + 0x01e7, 1048575, + 0x01e9, 1048575, + 0x01eb, 1048575, + 0x01ed, 1048575, + 0x01ef, 1048575, + 0x01f2, 1048575, + 0x01f3, 1048574, + 0x01f5, 1048575, + 0x01f9, 1048575, + 0x01fb, 1048575, + 0x01fd, 1048575, + 0x01ff, 1048575, + 0x0201, 1048575, + 0x0203, 1048575, + 0x0205, 1048575, + 0x0207, 1048575, + 0x0209, 1048575, + 0x020b, 1048575, + 0x020d, 1048575, + 0x020f, 1048575, + 0x0211, 1048575, + 0x0213, 1048575, + 0x0215, 1048575, + 0x0217, 1048575, + 0x0219, 1048575, + 0x021b, 1048575, + 0x021d, 1048575, + 0x021f, 1048575, + 0x0223, 1048575, + 0x0225, 1048575, + 0x0227, 1048575, + 0x0229, 1048575, + 0x022b, 1048575, + 0x022d, 1048575, + 0x022f, 1048575, + 0x0231, 1048575, + 0x0233, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0247, 1048575, + 0x0249, 1048575, + 0x024b, 1048575, + 0x024d, 1048575, + 0x024f, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x025c, 1090895, + 0x0260, 1048371, + 0x0261, 1090891, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026c, 1090881, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0287, 1090858, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x029e, 1090834, + 0x0345, 1048660, + 0x0371, 1048575, + 0x0373, 1048575, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03d9, 1048575, + 0x03db, 1048575, + 0x03dd, 1048575, + 0x03df, 1048575, + 0x03e1, 1048575, + 0x03e3, 1048575, + 0x03e5, 1048575, + 0x03e7, 1048575, + 0x03e9, 1048575, + 0x03eb, 1048575, + 0x03ed, 1048575, + 0x03ef, 1048575, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f3, 1048460, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x0461, 1048575, + 0x0463, 1048575, + 0x0465, 1048575, + 0x0467, 1048575, + 0x0469, 1048575, + 0x046b, 1048575, + 0x046d, 1048575, + 0x046f, 1048575, + 0x0471, 1048575, + 0x0473, 1048575, + 0x0475, 1048575, + 0x0477, 1048575, + 0x0479, 1048575, + 0x047b, 1048575, + 0x047d, 1048575, + 0x047f, 1048575, + 0x0481, 1048575, + 0x048b, 1048575, + 0x048d, 1048575, + 0x048f, 1048575, + 0x0491, 1048575, + 0x0493, 1048575, + 0x0495, 1048575, + 0x0497, 1048575, + 0x0499, 1048575, + 0x049b, 1048575, + 0x049d, 1048575, + 0x049f, 1048575, + 0x04a1, 1048575, + 0x04a3, 1048575, + 0x04a5, 1048575, + 0x04a7, 1048575, + 0x04a9, 1048575, + 0x04ab, 1048575, + 0x04ad, 1048575, + 0x04af, 1048575, + 0x04b1, 1048575, + 0x04b3, 1048575, + 0x04b5, 1048575, + 0x04b7, 1048575, + 0x04b9, 1048575, + 0x04bb, 1048575, + 0x04bd, 1048575, + 0x04bf, 1048575, + 0x04c2, 1048575, + 0x04c4, 1048575, + 0x04c6, 1048575, + 0x04c8, 1048575, + 0x04ca, 1048575, + 0x04cc, 1048575, + 0x04ce, 1048575, + 0x04cf, 1048561, + 0x04d1, 1048575, + 0x04d3, 1048575, + 0x04d5, 1048575, + 0x04d7, 1048575, + 0x04d9, 1048575, + 0x04db, 1048575, + 0x04dd, 1048575, + 0x04df, 1048575, + 0x04e1, 1048575, + 0x04e3, 1048575, + 0x04e5, 1048575, + 0x04e7, 1048575, + 0x04e9, 1048575, + 0x04eb, 1048575, + 0x04ed, 1048575, + 0x04ef, 1048575, + 0x04f1, 1048575, + 0x04f3, 1048575, + 0x04f5, 1048575, + 0x04f7, 1048575, + 0x04f9, 1048575, + 0x04fb, 1048575, + 0x04fd, 1048575, + 0x04ff, 1048575, + 0x0501, 1048575, + 0x0503, 1048575, + 0x0505, 1048575, + 0x0507, 1048575, + 0x0509, 1048575, + 0x050b, 1048575, + 0x050d, 1048575, + 0x050f, 1048575, + 0x0511, 1048575, + 0x0513, 1048575, + 0x0515, 1048575, + 0x0517, 1048575, + 0x0519, 1048575, + 0x051b, 1048575, + 0x051d, 1048575, + 0x051f, 1048575, + 0x0521, 1048575, + 0x0523, 1048575, + 0x0525, 1048575, + 0x0527, 1048575, + 0x0529, 1048575, + 0x052b, 1048575, + 0x052d, 1048575, + 0x052f, 1048575, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e01, 1048575, + 0x1e03, 1048575, + 0x1e05, 1048575, + 0x1e07, 1048575, + 0x1e09, 1048575, + 0x1e0b, 1048575, + 0x1e0d, 1048575, + 0x1e0f, 1048575, + 0x1e11, 1048575, + 0x1e13, 1048575, + 0x1e15, 1048575, + 0x1e17, 1048575, + 0x1e19, 1048575, + 0x1e1b, 1048575, + 0x1e1d, 1048575, + 0x1e1f, 1048575, + 0x1e21, 1048575, + 0x1e23, 1048575, + 0x1e25, 1048575, + 0x1e27, 1048575, + 0x1e29, 1048575, + 0x1e2b, 1048575, + 0x1e2d, 1048575, + 0x1e2f, 1048575, + 0x1e31, 1048575, + 0x1e33, 1048575, + 0x1e35, 1048575, + 0x1e37, 1048575, + 0x1e39, 1048575, + 0x1e3b, 1048575, + 0x1e3d, 1048575, + 0x1e3f, 1048575, + 0x1e41, 1048575, + 0x1e43, 1048575, + 0x1e45, 1048575, + 0x1e47, 1048575, + 0x1e49, 1048575, + 0x1e4b, 1048575, + 0x1e4d, 1048575, + 0x1e4f, 1048575, + 0x1e51, 1048575, + 0x1e53, 1048575, + 0x1e55, 1048575, + 0x1e57, 1048575, + 0x1e59, 1048575, + 0x1e5b, 1048575, + 0x1e5d, 1048575, + 0x1e5f, 1048575, + 0x1e61, 1048575, + 0x1e63, 1048575, + 0x1e65, 1048575, + 0x1e67, 1048575, + 0x1e69, 1048575, + 0x1e6b, 1048575, + 0x1e6d, 1048575, + 0x1e6f, 1048575, + 0x1e71, 1048575, + 0x1e73, 1048575, + 0x1e75, 1048575, + 0x1e77, 1048575, + 0x1e79, 1048575, + 0x1e7b, 1048575, + 0x1e7d, 1048575, + 0x1e7f, 1048575, + 0x1e81, 1048575, + 0x1e83, 1048575, + 0x1e85, 1048575, + 0x1e87, 1048575, + 0x1e89, 1048575, + 0x1e8b, 1048575, + 0x1e8d, 1048575, + 0x1e8f, 1048575, + 0x1e91, 1048575, + 0x1e93, 1048575, + 0x1e95, 1048575, + 0x1e9b, 1048517, + 0x1ea1, 1048575, + 0x1ea3, 1048575, + 0x1ea5, 1048575, + 0x1ea7, 1048575, + 0x1ea9, 1048575, + 0x1eab, 1048575, + 0x1ead, 1048575, + 0x1eaf, 1048575, + 0x1eb1, 1048575, + 0x1eb3, 1048575, + 0x1eb5, 1048575, + 0x1eb7, 1048575, + 0x1eb9, 1048575, + 0x1ebb, 1048575, + 0x1ebd, 1048575, + 0x1ebf, 1048575, + 0x1ec1, 1048575, + 0x1ec3, 1048575, + 0x1ec5, 1048575, + 0x1ec7, 1048575, + 0x1ec9, 1048575, + 0x1ecb, 1048575, + 0x1ecd, 1048575, + 0x1ecf, 1048575, + 0x1ed1, 1048575, + 0x1ed3, 1048575, + 0x1ed5, 1048575, + 0x1ed7, 1048575, + 0x1ed9, 1048575, + 0x1edb, 1048575, + 0x1edd, 1048575, + 0x1edf, 1048575, + 0x1ee1, 1048575, + 0x1ee3, 1048575, + 0x1ee5, 1048575, + 0x1ee7, 1048575, + 0x1ee9, 1048575, + 0x1eeb, 1048575, + 0x1eed, 1048575, + 0x1eef, 1048575, + 0x1ef1, 1048575, + 0x1ef3, 1048575, + 0x1ef5, 1048575, + 0x1ef7, 1048575, + 0x1ef9, 1048575, + 0x1efb, 1048575, + 0x1efd, 1048575, + 0x1eff, 1048575, + 0x1f51, 1048584, + 0x1f53, 1048584, + 0x1f55, 1048584, + 0x1f57, 1048584, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c68, 1048575, + 0x2c6a, 1048575, + 0x2c6c, 1048575, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2c81, 1048575, + 0x2c83, 1048575, + 0x2c85, 1048575, + 0x2c87, 1048575, + 0x2c89, 1048575, + 0x2c8b, 1048575, + 0x2c8d, 1048575, + 0x2c8f, 1048575, + 0x2c91, 1048575, + 0x2c93, 1048575, + 0x2c95, 1048575, + 0x2c97, 1048575, + 0x2c99, 1048575, + 0x2c9b, 1048575, + 0x2c9d, 1048575, + 0x2c9f, 1048575, + 0x2ca1, 1048575, + 0x2ca3, 1048575, + 0x2ca5, 1048575, + 0x2ca7, 1048575, + 0x2ca9, 1048575, + 0x2cab, 1048575, + 0x2cad, 1048575, + 0x2caf, 1048575, + 0x2cb1, 1048575, + 0x2cb3, 1048575, + 0x2cb5, 1048575, + 0x2cb7, 1048575, + 0x2cb9, 1048575, + 0x2cbb, 1048575, + 0x2cbd, 1048575, + 0x2cbf, 1048575, + 0x2cc1, 1048575, + 0x2cc3, 1048575, + 0x2cc5, 1048575, + 0x2cc7, 1048575, + 0x2cc9, 1048575, + 0x2ccb, 1048575, + 0x2ccd, 1048575, + 0x2ccf, 1048575, + 0x2cd1, 1048575, + 0x2cd3, 1048575, + 0x2cd5, 1048575, + 0x2cd7, 1048575, + 0x2cd9, 1048575, + 0x2cdb, 1048575, + 0x2cdd, 1048575, + 0x2cdf, 1048575, + 0x2ce1, 1048575, + 0x2ce3, 1048575, + 0x2cec, 1048575, + 0x2cee, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa641, 1048575, + 0xa643, 1048575, + 0xa645, 1048575, + 0xa647, 1048575, + 0xa649, 1048575, + 0xa64b, 1048575, + 0xa64d, 1048575, + 0xa64f, 1048575, + 0xa651, 1048575, + 0xa653, 1048575, + 0xa655, 1048575, + 0xa657, 1048575, + 0xa659, 1048575, + 0xa65b, 1048575, + 0xa65d, 1048575, + 0xa65f, 1048575, + 0xa661, 1048575, + 0xa663, 1048575, + 0xa665, 1048575, + 0xa667, 1048575, + 0xa669, 1048575, + 0xa66b, 1048575, + 0xa66d, 1048575, + 0xa681, 1048575, + 0xa683, 1048575, + 0xa685, 1048575, + 0xa687, 1048575, + 0xa689, 1048575, + 0xa68b, 1048575, + 0xa68d, 1048575, + 0xa68f, 1048575, + 0xa691, 1048575, + 0xa693, 1048575, + 0xa695, 1048575, + 0xa697, 1048575, + 0xa699, 1048575, + 0xa69b, 1048575, + 0xa723, 1048575, + 0xa725, 1048575, + 0xa727, 1048575, + 0xa729, 1048575, + 0xa72b, 1048575, + 0xa72d, 1048575, + 0xa72f, 1048575, + 0xa733, 1048575, + 0xa735, 1048575, + 0xa737, 1048575, + 0xa739, 1048575, + 0xa73b, 1048575, + 0xa73d, 1048575, + 0xa73f, 1048575, + 0xa741, 1048575, + 0xa743, 1048575, + 0xa745, 1048575, + 0xa747, 1048575, + 0xa749, 1048575, + 0xa74b, 1048575, + 0xa74d, 1048575, + 0xa74f, 1048575, + 0xa751, 1048575, + 0xa753, 1048575, + 0xa755, 1048575, + 0xa757, 1048575, + 0xa759, 1048575, + 0xa75b, 1048575, + 0xa75d, 1048575, + 0xa75f, 1048575, + 0xa761, 1048575, + 0xa763, 1048575, + 0xa765, 1048575, + 0xa767, 1048575, + 0xa769, 1048575, + 0xa76b, 1048575, + 0xa76d, 1048575, + 0xa76f, 1048575, + 0xa77a, 1048575, + 0xa77c, 1048575, + 0xa77f, 1048575, + 0xa781, 1048575, + 0xa783, 1048575, + 0xa785, 1048575, + 0xa787, 1048575, + 0xa78c, 1048575, + 0xa791, 1048575, + 0xa793, 1048575, + 0xa797, 1048575, + 0xa799, 1048575, + 0xa79b, 1048575, + 0xa79d, 1048575, + 0xa79f, 1048575, + 0xa7a1, 1048575, + 0xa7a3, 1048575, + 0xa7a5, 1048575, + 0xa7a7, 1048575, + 0xa7a9, 1048575, +}; + +} // !namespace + +char32_t toupper(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, toupperr, nelem (toupperr)/3, 3); + + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, touppers, nelem (touppers)/2, 2); + + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +} + +namespace { + +const char32_t tolowerr[] = { + 0x0041, 0x005a, 1048608, + 0x00c0, 0x00d6, 1048608, + 0x00d8, 0x00de, 1048608, + 0x0189, 0x018a, 1048781, + 0x01b1, 0x01b2, 1048793, + 0x0388, 0x038a, 1048613, + 0x038e, 0x038f, 1048639, + 0x0391, 0x03a1, 1048608, + 0x03a3, 0x03ab, 1048608, + 0x03fd, 0x03ff, 1048446, + 0x0400, 0x040f, 1048656, + 0x0410, 0x042f, 1048608, + 0x0531, 0x0556, 1048624, + 0x10a0, 0x10c5, 1055840, + 0x1f08, 0x1f0f, 1048568, + 0x1f18, 0x1f1d, 1048568, + 0x1f28, 0x1f2f, 1048568, + 0x1f38, 0x1f3f, 1048568, + 0x1f48, 0x1f4d, 1048568, + 0x1f68, 0x1f6f, 1048568, + 0x1f88, 0x1f8f, 1048568, + 0x1f98, 0x1f9f, 1048568, + 0x1fa8, 0x1faf, 1048568, + 0x1fb8, 0x1fb9, 1048568, + 0x1fba, 0x1fbb, 1048502, + 0x1fc8, 0x1fcb, 1048490, + 0x1fd8, 0x1fd9, 1048568, + 0x1fda, 0x1fdb, 1048476, + 0x1fe8, 0x1fe9, 1048568, + 0x1fea, 0x1feb, 1048464, + 0x1ff8, 0x1ff9, 1048448, + 0x1ffa, 0x1ffb, 1048450, + 0x2160, 0x216f, 1048592, + 0x24b6, 0x24cf, 1048602, + 0x2c00, 0x2c2e, 1048624, + 0x2c7e, 0x2c7f, 1037761, + 0xff21, 0xff3a, 1048608, + 0x10400, 0x10427, 1048616, + 0x118a0, 0x118bf, 1048608, +}; + +} // !namespace + +namespace { + +const char32_t tolowers[] = { + 0x0100, 1048577, + 0x0102, 1048577, + 0x0104, 1048577, + 0x0106, 1048577, + 0x0108, 1048577, + 0x010a, 1048577, + 0x010c, 1048577, + 0x010e, 1048577, + 0x0110, 1048577, + 0x0112, 1048577, + 0x0114, 1048577, + 0x0116, 1048577, + 0x0118, 1048577, + 0x011a, 1048577, + 0x011c, 1048577, + 0x011e, 1048577, + 0x0120, 1048577, + 0x0122, 1048577, + 0x0124, 1048577, + 0x0126, 1048577, + 0x0128, 1048577, + 0x012a, 1048577, + 0x012c, 1048577, + 0x012e, 1048577, + 0x0130, 1048377, + 0x0132, 1048577, + 0x0134, 1048577, + 0x0136, 1048577, + 0x0139, 1048577, + 0x013b, 1048577, + 0x013d, 1048577, + 0x013f, 1048577, + 0x0141, 1048577, + 0x0143, 1048577, + 0x0145, 1048577, + 0x0147, 1048577, + 0x014a, 1048577, + 0x014c, 1048577, + 0x014e, 1048577, + 0x0150, 1048577, + 0x0152, 1048577, + 0x0154, 1048577, + 0x0156, 1048577, + 0x0158, 1048577, + 0x015a, 1048577, + 0x015c, 1048577, + 0x015e, 1048577, + 0x0160, 1048577, + 0x0162, 1048577, + 0x0164, 1048577, + 0x0166, 1048577, + 0x0168, 1048577, + 0x016a, 1048577, + 0x016c, 1048577, + 0x016e, 1048577, + 0x0170, 1048577, + 0x0172, 1048577, + 0x0174, 1048577, + 0x0176, 1048577, + 0x0178, 1048455, + 0x0179, 1048577, + 0x017b, 1048577, + 0x017d, 1048577, + 0x0181, 1048786, + 0x0182, 1048577, + 0x0184, 1048577, + 0x0186, 1048782, + 0x0187, 1048577, + 0x018b, 1048577, + 0x018e, 1048655, + 0x018f, 1048778, + 0x0190, 1048779, + 0x0191, 1048577, + 0x0193, 1048781, + 0x0194, 1048783, + 0x0196, 1048787, + 0x0197, 1048785, + 0x0198, 1048577, + 0x019c, 1048787, + 0x019d, 1048789, + 0x019f, 1048790, + 0x01a0, 1048577, + 0x01a2, 1048577, + 0x01a4, 1048577, + 0x01a6, 1048794, + 0x01a7, 1048577, + 0x01a9, 1048794, + 0x01ac, 1048577, + 0x01ae, 1048794, + 0x01af, 1048577, + 0x01b3, 1048577, + 0x01b5, 1048577, + 0x01b7, 1048795, + 0x01b8, 1048577, + 0x01bc, 1048577, + 0x01c4, 1048578, + 0x01c5, 1048577, + 0x01c7, 1048578, + 0x01c8, 1048577, + 0x01ca, 1048578, + 0x01cb, 1048577, + 0x01cd, 1048577, + 0x01cf, 1048577, + 0x01d1, 1048577, + 0x01d3, 1048577, + 0x01d5, 1048577, + 0x01d7, 1048577, + 0x01d9, 1048577, + 0x01db, 1048577, + 0x01de, 1048577, + 0x01e0, 1048577, + 0x01e2, 1048577, + 0x01e4, 1048577, + 0x01e6, 1048577, + 0x01e8, 1048577, + 0x01ea, 1048577, + 0x01ec, 1048577, + 0x01ee, 1048577, + 0x01f1, 1048578, + 0x01f2, 1048577, + 0x01f4, 1048577, + 0x01f6, 1048479, + 0x01f7, 1048520, + 0x01f8, 1048577, + 0x01fa, 1048577, + 0x01fc, 1048577, + 0x01fe, 1048577, + 0x0200, 1048577, + 0x0202, 1048577, + 0x0204, 1048577, + 0x0206, 1048577, + 0x0208, 1048577, + 0x020a, 1048577, + 0x020c, 1048577, + 0x020e, 1048577, + 0x0210, 1048577, + 0x0212, 1048577, + 0x0214, 1048577, + 0x0216, 1048577, + 0x0218, 1048577, + 0x021a, 1048577, + 0x021c, 1048577, + 0x021e, 1048577, + 0x0220, 1048446, + 0x0222, 1048577, + 0x0224, 1048577, + 0x0226, 1048577, + 0x0228, 1048577, + 0x022a, 1048577, + 0x022c, 1048577, + 0x022e, 1048577, + 0x0230, 1048577, + 0x0232, 1048577, + 0x023a, 1059371, + 0x023b, 1048577, + 0x023d, 1048413, + 0x023e, 1059368, + 0x0241, 1048577, + 0x0243, 1048381, + 0x0244, 1048645, + 0x0245, 1048647, + 0x0246, 1048577, + 0x0248, 1048577, + 0x024a, 1048577, + 0x024c, 1048577, + 0x024e, 1048577, + 0x0370, 1048577, + 0x0372, 1048577, + 0x0376, 1048577, + 0x037f, 1048692, + 0x0386, 1048614, + 0x038c, 1048640, + 0x03cf, 1048584, + 0x03d8, 1048577, + 0x03da, 1048577, + 0x03dc, 1048577, + 0x03de, 1048577, + 0x03e0, 1048577, + 0x03e2, 1048577, + 0x03e4, 1048577, + 0x03e6, 1048577, + 0x03e8, 1048577, + 0x03ea, 1048577, + 0x03ec, 1048577, + 0x03ee, 1048577, + 0x03f4, 1048516, + 0x03f7, 1048577, + 0x03f9, 1048569, + 0x03fa, 1048577, + 0x0460, 1048577, + 0x0462, 1048577, + 0x0464, 1048577, + 0x0466, 1048577, + 0x0468, 1048577, + 0x046a, 1048577, + 0x046c, 1048577, + 0x046e, 1048577, + 0x0470, 1048577, + 0x0472, 1048577, + 0x0474, 1048577, + 0x0476, 1048577, + 0x0478, 1048577, + 0x047a, 1048577, + 0x047c, 1048577, + 0x047e, 1048577, + 0x0480, 1048577, + 0x048a, 1048577, + 0x048c, 1048577, + 0x048e, 1048577, + 0x0490, 1048577, + 0x0492, 1048577, + 0x0494, 1048577, + 0x0496, 1048577, + 0x0498, 1048577, + 0x049a, 1048577, + 0x049c, 1048577, + 0x049e, 1048577, + 0x04a0, 1048577, + 0x04a2, 1048577, + 0x04a4, 1048577, + 0x04a6, 1048577, + 0x04a8, 1048577, + 0x04aa, 1048577, + 0x04ac, 1048577, + 0x04ae, 1048577, + 0x04b0, 1048577, + 0x04b2, 1048577, + 0x04b4, 1048577, + 0x04b6, 1048577, + 0x04b8, 1048577, + 0x04ba, 1048577, + 0x04bc, 1048577, + 0x04be, 1048577, + 0x04c0, 1048591, + 0x04c1, 1048577, + 0x04c3, 1048577, + 0x04c5, 1048577, + 0x04c7, 1048577, + 0x04c9, 1048577, + 0x04cb, 1048577, + 0x04cd, 1048577, + 0x04d0, 1048577, + 0x04d2, 1048577, + 0x04d4, 1048577, + 0x04d6, 1048577, + 0x04d8, 1048577, + 0x04da, 1048577, + 0x04dc, 1048577, + 0x04de, 1048577, + 0x04e0, 1048577, + 0x04e2, 1048577, + 0x04e4, 1048577, + 0x04e6, 1048577, + 0x04e8, 1048577, + 0x04ea, 1048577, + 0x04ec, 1048577, + 0x04ee, 1048577, + 0x04f0, 1048577, + 0x04f2, 1048577, + 0x04f4, 1048577, + 0x04f6, 1048577, + 0x04f8, 1048577, + 0x04fa, 1048577, + 0x04fc, 1048577, + 0x04fe, 1048577, + 0x0500, 1048577, + 0x0502, 1048577, + 0x0504, 1048577, + 0x0506, 1048577, + 0x0508, 1048577, + 0x050a, 1048577, + 0x050c, 1048577, + 0x050e, 1048577, + 0x0510, 1048577, + 0x0512, 1048577, + 0x0514, 1048577, + 0x0516, 1048577, + 0x0518, 1048577, + 0x051a, 1048577, + 0x051c, 1048577, + 0x051e, 1048577, + 0x0520, 1048577, + 0x0522, 1048577, + 0x0524, 1048577, + 0x0526, 1048577, + 0x0528, 1048577, + 0x052a, 1048577, + 0x052c, 1048577, + 0x052e, 1048577, + 0x10c7, 1055840, + 0x10cd, 1055840, + 0x1e00, 1048577, + 0x1e02, 1048577, + 0x1e04, 1048577, + 0x1e06, 1048577, + 0x1e08, 1048577, + 0x1e0a, 1048577, + 0x1e0c, 1048577, + 0x1e0e, 1048577, + 0x1e10, 1048577, + 0x1e12, 1048577, + 0x1e14, 1048577, + 0x1e16, 1048577, + 0x1e18, 1048577, + 0x1e1a, 1048577, + 0x1e1c, 1048577, + 0x1e1e, 1048577, + 0x1e20, 1048577, + 0x1e22, 1048577, + 0x1e24, 1048577, + 0x1e26, 1048577, + 0x1e28, 1048577, + 0x1e2a, 1048577, + 0x1e2c, 1048577, + 0x1e2e, 1048577, + 0x1e30, 1048577, + 0x1e32, 1048577, + 0x1e34, 1048577, + 0x1e36, 1048577, + 0x1e38, 1048577, + 0x1e3a, 1048577, + 0x1e3c, 1048577, + 0x1e3e, 1048577, + 0x1e40, 1048577, + 0x1e42, 1048577, + 0x1e44, 1048577, + 0x1e46, 1048577, + 0x1e48, 1048577, + 0x1e4a, 1048577, + 0x1e4c, 1048577, + 0x1e4e, 1048577, + 0x1e50, 1048577, + 0x1e52, 1048577, + 0x1e54, 1048577, + 0x1e56, 1048577, + 0x1e58, 1048577, + 0x1e5a, 1048577, + 0x1e5c, 1048577, + 0x1e5e, 1048577, + 0x1e60, 1048577, + 0x1e62, 1048577, + 0x1e64, 1048577, + 0x1e66, 1048577, + 0x1e68, 1048577, + 0x1e6a, 1048577, + 0x1e6c, 1048577, + 0x1e6e, 1048577, + 0x1e70, 1048577, + 0x1e72, 1048577, + 0x1e74, 1048577, + 0x1e76, 1048577, + 0x1e78, 1048577, + 0x1e7a, 1048577, + 0x1e7c, 1048577, + 0x1e7e, 1048577, + 0x1e80, 1048577, + 0x1e82, 1048577, + 0x1e84, 1048577, + 0x1e86, 1048577, + 0x1e88, 1048577, + 0x1e8a, 1048577, + 0x1e8c, 1048577, + 0x1e8e, 1048577, + 0x1e90, 1048577, + 0x1e92, 1048577, + 0x1e94, 1048577, + 0x1e9e, 1040961, + 0x1ea0, 1048577, + 0x1ea2, 1048577, + 0x1ea4, 1048577, + 0x1ea6, 1048577, + 0x1ea8, 1048577, + 0x1eaa, 1048577, + 0x1eac, 1048577, + 0x1eae, 1048577, + 0x1eb0, 1048577, + 0x1eb2, 1048577, + 0x1eb4, 1048577, + 0x1eb6, 1048577, + 0x1eb8, 1048577, + 0x1eba, 1048577, + 0x1ebc, 1048577, + 0x1ebe, 1048577, + 0x1ec0, 1048577, + 0x1ec2, 1048577, + 0x1ec4, 1048577, + 0x1ec6, 1048577, + 0x1ec8, 1048577, + 0x1eca, 1048577, + 0x1ecc, 1048577, + 0x1ece, 1048577, + 0x1ed0, 1048577, + 0x1ed2, 1048577, + 0x1ed4, 1048577, + 0x1ed6, 1048577, + 0x1ed8, 1048577, + 0x1eda, 1048577, + 0x1edc, 1048577, + 0x1ede, 1048577, + 0x1ee0, 1048577, + 0x1ee2, 1048577, + 0x1ee4, 1048577, + 0x1ee6, 1048577, + 0x1ee8, 1048577, + 0x1eea, 1048577, + 0x1eec, 1048577, + 0x1eee, 1048577, + 0x1ef0, 1048577, + 0x1ef2, 1048577, + 0x1ef4, 1048577, + 0x1ef6, 1048577, + 0x1ef8, 1048577, + 0x1efa, 1048577, + 0x1efc, 1048577, + 0x1efe, 1048577, + 0x1f59, 1048568, + 0x1f5b, 1048568, + 0x1f5d, 1048568, + 0x1f5f, 1048568, + 0x1fbc, 1048567, + 0x1fcc, 1048567, + 0x1fec, 1048569, + 0x1ffc, 1048567, + 0x2126, 1041059, + 0x212a, 1040193, + 0x212b, 1040314, + 0x2132, 1048604, + 0x2183, 1048577, + 0x2c60, 1048577, + 0x2c62, 1037833, + 0x2c63, 1044762, + 0x2c64, 1037849, + 0x2c67, 1048577, + 0x2c69, 1048577, + 0x2c6b, 1048577, + 0x2c6d, 1037796, + 0x2c6e, 1037827, + 0x2c6f, 1037793, + 0x2c70, 1037794, + 0x2c72, 1048577, + 0x2c75, 1048577, + 0x2c80, 1048577, + 0x2c82, 1048577, + 0x2c84, 1048577, + 0x2c86, 1048577, + 0x2c88, 1048577, + 0x2c8a, 1048577, + 0x2c8c, 1048577, + 0x2c8e, 1048577, + 0x2c90, 1048577, + 0x2c92, 1048577, + 0x2c94, 1048577, + 0x2c96, 1048577, + 0x2c98, 1048577, + 0x2c9a, 1048577, + 0x2c9c, 1048577, + 0x2c9e, 1048577, + 0x2ca0, 1048577, + 0x2ca2, 1048577, + 0x2ca4, 1048577, + 0x2ca6, 1048577, + 0x2ca8, 1048577, + 0x2caa, 1048577, + 0x2cac, 1048577, + 0x2cae, 1048577, + 0x2cb0, 1048577, + 0x2cb2, 1048577, + 0x2cb4, 1048577, + 0x2cb6, 1048577, + 0x2cb8, 1048577, + 0x2cba, 1048577, + 0x2cbc, 1048577, + 0x2cbe, 1048577, + 0x2cc0, 1048577, + 0x2cc2, 1048577, + 0x2cc4, 1048577, + 0x2cc6, 1048577, + 0x2cc8, 1048577, + 0x2cca, 1048577, + 0x2ccc, 1048577, + 0x2cce, 1048577, + 0x2cd0, 1048577, + 0x2cd2, 1048577, + 0x2cd4, 1048577, + 0x2cd6, 1048577, + 0x2cd8, 1048577, + 0x2cda, 1048577, + 0x2cdc, 1048577, + 0x2cde, 1048577, + 0x2ce0, 1048577, + 0x2ce2, 1048577, + 0x2ceb, 1048577, + 0x2ced, 1048577, + 0x2cf2, 1048577, + 0xa640, 1048577, + 0xa642, 1048577, + 0xa644, 1048577, + 0xa646, 1048577, + 0xa648, 1048577, + 0xa64a, 1048577, + 0xa64c, 1048577, + 0xa64e, 1048577, + 0xa650, 1048577, + 0xa652, 1048577, + 0xa654, 1048577, + 0xa656, 1048577, + 0xa658, 1048577, + 0xa65a, 1048577, + 0xa65c, 1048577, + 0xa65e, 1048577, + 0xa660, 1048577, + 0xa662, 1048577, + 0xa664, 1048577, + 0xa666, 1048577, + 0xa668, 1048577, + 0xa66a, 1048577, + 0xa66c, 1048577, + 0xa680, 1048577, + 0xa682, 1048577, + 0xa684, 1048577, + 0xa686, 1048577, + 0xa688, 1048577, + 0xa68a, 1048577, + 0xa68c, 1048577, + 0xa68e, 1048577, + 0xa690, 1048577, + 0xa692, 1048577, + 0xa694, 1048577, + 0xa696, 1048577, + 0xa698, 1048577, + 0xa69a, 1048577, + 0xa722, 1048577, + 0xa724, 1048577, + 0xa726, 1048577, + 0xa728, 1048577, + 0xa72a, 1048577, + 0xa72c, 1048577, + 0xa72e, 1048577, + 0xa732, 1048577, + 0xa734, 1048577, + 0xa736, 1048577, + 0xa738, 1048577, + 0xa73a, 1048577, + 0xa73c, 1048577, + 0xa73e, 1048577, + 0xa740, 1048577, + 0xa742, 1048577, + 0xa744, 1048577, + 0xa746, 1048577, + 0xa748, 1048577, + 0xa74a, 1048577, + 0xa74c, 1048577, + 0xa74e, 1048577, + 0xa750, 1048577, + 0xa752, 1048577, + 0xa754, 1048577, + 0xa756, 1048577, + 0xa758, 1048577, + 0xa75a, 1048577, + 0xa75c, 1048577, + 0xa75e, 1048577, + 0xa760, 1048577, + 0xa762, 1048577, + 0xa764, 1048577, + 0xa766, 1048577, + 0xa768, 1048577, + 0xa76a, 1048577, + 0xa76c, 1048577, + 0xa76e, 1048577, + 0xa779, 1048577, + 0xa77b, 1048577, + 0xa77d, 1013244, + 0xa77e, 1048577, + 0xa780, 1048577, + 0xa782, 1048577, + 0xa784, 1048577, + 0xa786, 1048577, + 0xa78b, 1048577, + 0xa78d, 1006296, + 0xa790, 1048577, + 0xa792, 1048577, + 0xa796, 1048577, + 0xa798, 1048577, + 0xa79a, 1048577, + 0xa79c, 1048577, + 0xa79e, 1048577, + 0xa7a0, 1048577, + 0xa7a2, 1048577, + 0xa7a4, 1048577, + 0xa7a6, 1048577, + 0xa7a8, 1048577, + 0xa7aa, 1006268, + 0xa7ab, 1006257, + 0xa7ac, 1006261, + 0xa7ad, 1006271, + 0xa7b0, 1006318, + 0xa7b1, 1006294, +}; + +} // !namespace + +char32_t tolower(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, tolowerr, nelem (tolowerr)/3, 3); + + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, tolowers, nelem (tolowers)/2, 2); + + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +} + +namespace { + +const char32_t totitler[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, + 0x118c0, 0x118df, 1048544, +}; + +} // !namespace + +namespace { + +const char32_t totitles[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0101, 1048575, + 0x0103, 1048575, + 0x0105, 1048575, + 0x0107, 1048575, + 0x0109, 1048575, + 0x010b, 1048575, + 0x010d, 1048575, + 0x010f, 1048575, + 0x0111, 1048575, + 0x0113, 1048575, + 0x0115, 1048575, + 0x0117, 1048575, + 0x0119, 1048575, + 0x011b, 1048575, + 0x011d, 1048575, + 0x011f, 1048575, + 0x0121, 1048575, + 0x0123, 1048575, + 0x0125, 1048575, + 0x0127, 1048575, + 0x0129, 1048575, + 0x012b, 1048575, + 0x012d, 1048575, + 0x012f, 1048575, + 0x0131, 1048344, + 0x0133, 1048575, + 0x0135, 1048575, + 0x0137, 1048575, + 0x013a, 1048575, + 0x013c, 1048575, + 0x013e, 1048575, + 0x0140, 1048575, + 0x0142, 1048575, + 0x0144, 1048575, + 0x0146, 1048575, + 0x0148, 1048575, + 0x014b, 1048575, + 0x014d, 1048575, + 0x014f, 1048575, + 0x0151, 1048575, + 0x0153, 1048575, + 0x0155, 1048575, + 0x0157, 1048575, + 0x0159, 1048575, + 0x015b, 1048575, + 0x015d, 1048575, + 0x015f, 1048575, + 0x0161, 1048575, + 0x0163, 1048575, + 0x0165, 1048575, + 0x0167, 1048575, + 0x0169, 1048575, + 0x016b, 1048575, + 0x016d, 1048575, + 0x016f, 1048575, + 0x0171, 1048575, + 0x0173, 1048575, + 0x0175, 1048575, + 0x0177, 1048575, + 0x017a, 1048575, + 0x017c, 1048575, + 0x017e, 1048575, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0183, 1048575, + 0x0185, 1048575, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a1, 1048575, + 0x01a3, 1048575, + 0x01a5, 1048575, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b4, 1048575, + 0x01b6, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c4, 1048577, + 0x01c6, 1048575, + 0x01c7, 1048577, + 0x01c9, 1048575, + 0x01ca, 1048577, + 0x01cc, 1048575, + 0x01ce, 1048575, + 0x01d0, 1048575, + 0x01d2, 1048575, + 0x01d4, 1048575, + 0x01d6, 1048575, + 0x01d8, 1048575, + 0x01da, 1048575, + 0x01dc, 1048575, + 0x01dd, 1048497, + 0x01df, 1048575, + 0x01e1, 1048575, + 0x01e3, 1048575, + 0x01e5, 1048575, + 0x01e7, 1048575, + 0x01e9, 1048575, + 0x01eb, 1048575, + 0x01ed, 1048575, + 0x01ef, 1048575, + 0x01f1, 1048577, + 0x01f3, 1048575, + 0x01f5, 1048575, + 0x01f9, 1048575, + 0x01fb, 1048575, + 0x01fd, 1048575, + 0x01ff, 1048575, + 0x0201, 1048575, + 0x0203, 1048575, + 0x0205, 1048575, + 0x0207, 1048575, + 0x0209, 1048575, + 0x020b, 1048575, + 0x020d, 1048575, + 0x020f, 1048575, + 0x0211, 1048575, + 0x0213, 1048575, + 0x0215, 1048575, + 0x0217, 1048575, + 0x0219, 1048575, + 0x021b, 1048575, + 0x021d, 1048575, + 0x021f, 1048575, + 0x0223, 1048575, + 0x0225, 1048575, + 0x0227, 1048575, + 0x0229, 1048575, + 0x022b, 1048575, + 0x022d, 1048575, + 0x022f, 1048575, + 0x0231, 1048575, + 0x0233, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0247, 1048575, + 0x0249, 1048575, + 0x024b, 1048575, + 0x024d, 1048575, + 0x024f, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x025c, 1090895, + 0x0260, 1048371, + 0x0261, 1090891, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026c, 1090881, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0287, 1090858, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x029e, 1090834, + 0x0345, 1048660, + 0x0371, 1048575, + 0x0373, 1048575, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03d9, 1048575, + 0x03db, 1048575, + 0x03dd, 1048575, + 0x03df, 1048575, + 0x03e1, 1048575, + 0x03e3, 1048575, + 0x03e5, 1048575, + 0x03e7, 1048575, + 0x03e9, 1048575, + 0x03eb, 1048575, + 0x03ed, 1048575, + 0x03ef, 1048575, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f3, 1048460, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x0461, 1048575, + 0x0463, 1048575, + 0x0465, 1048575, + 0x0467, 1048575, + 0x0469, 1048575, + 0x046b, 1048575, + 0x046d, 1048575, + 0x046f, 1048575, + 0x0471, 1048575, + 0x0473, 1048575, + 0x0475, 1048575, + 0x0477, 1048575, + 0x0479, 1048575, + 0x047b, 1048575, + 0x047d, 1048575, + 0x047f, 1048575, + 0x0481, 1048575, + 0x048b, 1048575, + 0x048d, 1048575, + 0x048f, 1048575, + 0x0491, 1048575, + 0x0493, 1048575, + 0x0495, 1048575, + 0x0497, 1048575, + 0x0499, 1048575, + 0x049b, 1048575, + 0x049d, 1048575, + 0x049f, 1048575, + 0x04a1, 1048575, + 0x04a3, 1048575, + 0x04a5, 1048575, + 0x04a7, 1048575, + 0x04a9, 1048575, + 0x04ab, 1048575, + 0x04ad, 1048575, + 0x04af, 1048575, + 0x04b1, 1048575, + 0x04b3, 1048575, + 0x04b5, 1048575, + 0x04b7, 1048575, + 0x04b9, 1048575, + 0x04bb, 1048575, + 0x04bd, 1048575, + 0x04bf, 1048575, + 0x04c2, 1048575, + 0x04c4, 1048575, + 0x04c6, 1048575, + 0x04c8, 1048575, + 0x04ca, 1048575, + 0x04cc, 1048575, + 0x04ce, 1048575, + 0x04cf, 1048561, + 0x04d1, 1048575, + 0x04d3, 1048575, + 0x04d5, 1048575, + 0x04d7, 1048575, + 0x04d9, 1048575, + 0x04db, 1048575, + 0x04dd, 1048575, + 0x04df, 1048575, + 0x04e1, 1048575, + 0x04e3, 1048575, + 0x04e5, 1048575, + 0x04e7, 1048575, + 0x04e9, 1048575, + 0x04eb, 1048575, + 0x04ed, 1048575, + 0x04ef, 1048575, + 0x04f1, 1048575, + 0x04f3, 1048575, + 0x04f5, 1048575, + 0x04f7, 1048575, + 0x04f9, 1048575, + 0x04fb, 1048575, + 0x04fd, 1048575, + 0x04ff, 1048575, + 0x0501, 1048575, + 0x0503, 1048575, + 0x0505, 1048575, + 0x0507, 1048575, + 0x0509, 1048575, + 0x050b, 1048575, + 0x050d, 1048575, + 0x050f, 1048575, + 0x0511, 1048575, + 0x0513, 1048575, + 0x0515, 1048575, + 0x0517, 1048575, + 0x0519, 1048575, + 0x051b, 1048575, + 0x051d, 1048575, + 0x051f, 1048575, + 0x0521, 1048575, + 0x0523, 1048575, + 0x0525, 1048575, + 0x0527, 1048575, + 0x0529, 1048575, + 0x052b, 1048575, + 0x052d, 1048575, + 0x052f, 1048575, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e01, 1048575, + 0x1e03, 1048575, + 0x1e05, 1048575, + 0x1e07, 1048575, + 0x1e09, 1048575, + 0x1e0b, 1048575, + 0x1e0d, 1048575, + 0x1e0f, 1048575, + 0x1e11, 1048575, + 0x1e13, 1048575, + 0x1e15, 1048575, + 0x1e17, 1048575, + 0x1e19, 1048575, + 0x1e1b, 1048575, + 0x1e1d, 1048575, + 0x1e1f, 1048575, + 0x1e21, 1048575, + 0x1e23, 1048575, + 0x1e25, 1048575, + 0x1e27, 1048575, + 0x1e29, 1048575, + 0x1e2b, 1048575, + 0x1e2d, 1048575, + 0x1e2f, 1048575, + 0x1e31, 1048575, + 0x1e33, 1048575, + 0x1e35, 1048575, + 0x1e37, 1048575, + 0x1e39, 1048575, + 0x1e3b, 1048575, + 0x1e3d, 1048575, + 0x1e3f, 1048575, + 0x1e41, 1048575, + 0x1e43, 1048575, + 0x1e45, 1048575, + 0x1e47, 1048575, + 0x1e49, 1048575, + 0x1e4b, 1048575, + 0x1e4d, 1048575, + 0x1e4f, 1048575, + 0x1e51, 1048575, + 0x1e53, 1048575, + 0x1e55, 1048575, + 0x1e57, 1048575, + 0x1e59, 1048575, + 0x1e5b, 1048575, + 0x1e5d, 1048575, + 0x1e5f, 1048575, + 0x1e61, 1048575, + 0x1e63, 1048575, + 0x1e65, 1048575, + 0x1e67, 1048575, + 0x1e69, 1048575, + 0x1e6b, 1048575, + 0x1e6d, 1048575, + 0x1e6f, 1048575, + 0x1e71, 1048575, + 0x1e73, 1048575, + 0x1e75, 1048575, + 0x1e77, 1048575, + 0x1e79, 1048575, + 0x1e7b, 1048575, + 0x1e7d, 1048575, + 0x1e7f, 1048575, + 0x1e81, 1048575, + 0x1e83, 1048575, + 0x1e85, 1048575, + 0x1e87, 1048575, + 0x1e89, 1048575, + 0x1e8b, 1048575, + 0x1e8d, 1048575, + 0x1e8f, 1048575, + 0x1e91, 1048575, + 0x1e93, 1048575, + 0x1e95, 1048575, + 0x1e9b, 1048517, + 0x1ea1, 1048575, + 0x1ea3, 1048575, + 0x1ea5, 1048575, + 0x1ea7, 1048575, + 0x1ea9, 1048575, + 0x1eab, 1048575, + 0x1ead, 1048575, + 0x1eaf, 1048575, + 0x1eb1, 1048575, + 0x1eb3, 1048575, + 0x1eb5, 1048575, + 0x1eb7, 1048575, + 0x1eb9, 1048575, + 0x1ebb, 1048575, + 0x1ebd, 1048575, + 0x1ebf, 1048575, + 0x1ec1, 1048575, + 0x1ec3, 1048575, + 0x1ec5, 1048575, + 0x1ec7, 1048575, + 0x1ec9, 1048575, + 0x1ecb, 1048575, + 0x1ecd, 1048575, + 0x1ecf, 1048575, + 0x1ed1, 1048575, + 0x1ed3, 1048575, + 0x1ed5, 1048575, + 0x1ed7, 1048575, + 0x1ed9, 1048575, + 0x1edb, 1048575, + 0x1edd, 1048575, + 0x1edf, 1048575, + 0x1ee1, 1048575, + 0x1ee3, 1048575, + 0x1ee5, 1048575, + 0x1ee7, 1048575, + 0x1ee9, 1048575, + 0x1eeb, 1048575, + 0x1eed, 1048575, + 0x1eef, 1048575, + 0x1ef1, 1048575, + 0x1ef3, 1048575, + 0x1ef5, 1048575, + 0x1ef7, 1048575, + 0x1ef9, 1048575, + 0x1efb, 1048575, + 0x1efd, 1048575, + 0x1eff, 1048575, + 0x1f51, 1048584, + 0x1f53, 1048584, + 0x1f55, 1048584, + 0x1f57, 1048584, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c68, 1048575, + 0x2c6a, 1048575, + 0x2c6c, 1048575, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2c81, 1048575, + 0x2c83, 1048575, + 0x2c85, 1048575, + 0x2c87, 1048575, + 0x2c89, 1048575, + 0x2c8b, 1048575, + 0x2c8d, 1048575, + 0x2c8f, 1048575, + 0x2c91, 1048575, + 0x2c93, 1048575, + 0x2c95, 1048575, + 0x2c97, 1048575, + 0x2c99, 1048575, + 0x2c9b, 1048575, + 0x2c9d, 1048575, + 0x2c9f, 1048575, + 0x2ca1, 1048575, + 0x2ca3, 1048575, + 0x2ca5, 1048575, + 0x2ca7, 1048575, + 0x2ca9, 1048575, + 0x2cab, 1048575, + 0x2cad, 1048575, + 0x2caf, 1048575, + 0x2cb1, 1048575, + 0x2cb3, 1048575, + 0x2cb5, 1048575, + 0x2cb7, 1048575, + 0x2cb9, 1048575, + 0x2cbb, 1048575, + 0x2cbd, 1048575, + 0x2cbf, 1048575, + 0x2cc1, 1048575, + 0x2cc3, 1048575, + 0x2cc5, 1048575, + 0x2cc7, 1048575, + 0x2cc9, 1048575, + 0x2ccb, 1048575, + 0x2ccd, 1048575, + 0x2ccf, 1048575, + 0x2cd1, 1048575, + 0x2cd3, 1048575, + 0x2cd5, 1048575, + 0x2cd7, 1048575, + 0x2cd9, 1048575, + 0x2cdb, 1048575, + 0x2cdd, 1048575, + 0x2cdf, 1048575, + 0x2ce1, 1048575, + 0x2ce3, 1048575, + 0x2cec, 1048575, + 0x2cee, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa641, 1048575, + 0xa643, 1048575, + 0xa645, 1048575, + 0xa647, 1048575, + 0xa649, 1048575, + 0xa64b, 1048575, + 0xa64d, 1048575, + 0xa64f, 1048575, + 0xa651, 1048575, + 0xa653, 1048575, + 0xa655, 1048575, + 0xa657, 1048575, + 0xa659, 1048575, + 0xa65b, 1048575, + 0xa65d, 1048575, + 0xa65f, 1048575, + 0xa661, 1048575, + 0xa663, 1048575, + 0xa665, 1048575, + 0xa667, 1048575, + 0xa669, 1048575, + 0xa66b, 1048575, + 0xa66d, 1048575, + 0xa681, 1048575, + 0xa683, 1048575, + 0xa685, 1048575, + 0xa687, 1048575, + 0xa689, 1048575, + 0xa68b, 1048575, + 0xa68d, 1048575, + 0xa68f, 1048575, + 0xa691, 1048575, + 0xa693, 1048575, + 0xa695, 1048575, + 0xa697, 1048575, + 0xa699, 1048575, + 0xa69b, 1048575, + 0xa723, 1048575, + 0xa725, 1048575, + 0xa727, 1048575, + 0xa729, 1048575, + 0xa72b, 1048575, + 0xa72d, 1048575, + 0xa72f, 1048575, + 0xa733, 1048575, + 0xa735, 1048575, + 0xa737, 1048575, + 0xa739, 1048575, + 0xa73b, 1048575, + 0xa73d, 1048575, + 0xa73f, 1048575, + 0xa741, 1048575, + 0xa743, 1048575, + 0xa745, 1048575, + 0xa747, 1048575, + 0xa749, 1048575, + 0xa74b, 1048575, + 0xa74d, 1048575, + 0xa74f, 1048575, + 0xa751, 1048575, + 0xa753, 1048575, + 0xa755, 1048575, + 0xa757, 1048575, + 0xa759, 1048575, + 0xa75b, 1048575, + 0xa75d, 1048575, + 0xa75f, 1048575, + 0xa761, 1048575, + 0xa763, 1048575, + 0xa765, 1048575, + 0xa767, 1048575, + 0xa769, 1048575, + 0xa76b, 1048575, + 0xa76d, 1048575, + 0xa76f, 1048575, + 0xa77a, 1048575, + 0xa77c, 1048575, + 0xa77f, 1048575, + 0xa781, 1048575, + 0xa783, 1048575, + 0xa785, 1048575, + 0xa787, 1048575, + 0xa78c, 1048575, + 0xa791, 1048575, + 0xa793, 1048575, + 0xa797, 1048575, + 0xa799, 1048575, + 0xa79b, 1048575, + 0xa79d, 1048575, + 0xa79f, 1048575, + 0xa7a1, 1048575, + 0xa7a3, 1048575, + 0xa7a5, 1048575, + 0xa7a7, 1048575, + 0xa7a9, 1048575, +}; + +} // !namespace + +char32_t totitle(char32_t c) noexcept +{ + const char32_t *p; + + p = rbsearch(c, totitler, nelem (totitler)/3, 3); + + if (p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + + p = rbsearch(c, totitles, nelem (totitles)/2, 2); + + if (p && c == p[0]) + return c + p[1] - 1048576; + + return c; +} + +void encode(char32_t c, char res[5]) noexcept +{ + switch (nbytesPoint(c)) { + case 1: + res[0] = static_cast<char>(c); + res[1] = '\0'; + break; + case 2: + res[0] = 0xC0 | ((c >> 6) & 0x1F); + res[1] = 0x80 | (c & 0x3F); + res[2] = '\0'; + break; + case 3: + res[0] = 0xE0 | ((c >> 12) & 0xF ); + res[1] = 0x80 | ((c >> 6) & 0x3F); + res[2] = 0x80 | (c & 0x3F); + res[3] = '\0'; + break; + case 4: + res[0] = 0xF0 | ((c >> 18) & 0x7 ); + res[1] = 0x80 | ((c >> 12) & 0x3F); + res[2] = 0x80 | ((c >> 6) & 0x3F); + res[3] = 0x80 | (c & 0x3F); + res[4] = '\0'; + break; + default: + break; + } +} + +void decode(char32_t &c, const char *res) noexcept +{ + c = 0; + + switch (nbytesUtf8(res[0])) { + case 1: + c = res[0]; + break; + case 2: + c = (res[0] & 0x1f) << 6; + c |= (res[1] & 0x3f); + break; + case 3: + c = (res[0] & 0x0f) << 12; + c |= (res[1] & 0x3f) << 6; + c |= (res[2] & 0x3f); + break; + case 4: + c = (res[0] & 0x07) << 16; + c |= (res[1] & 0x3f) << 12; + c |= (res[2] & 0x3f) << 6; + c |= (res[3] & 0x3f); + default: + break; + } +} + +int nbytesUtf8(char c) noexcept +{ + if (static_cast<unsigned char>(c) <= 127) + return 1; + if ((c & 0xE0) == 0xC0) + return 2; + if ((c & 0xF0) == 0xE0) + return 3; + if ((c & 0xF8) == 0xF0) + return 4; + + return -1; +} + +int nbytesPoint(char32_t c) noexcept +{ + if (c <= 0x7F) + return 1; + if (c <= 0x7FF) + return 2; + if (c <= 0xFFFF) + return 3; + if (c <= 0x1FFFFF) + return 4; + + return -1; +} + +unsigned length(const std::string &str) +{ + unsigned total = 0; + + forEach(str, [&] (char32_t) { + ++ total; + }); + + return total; +} + +std::string toUtf8(const std::u32string &array) +{ + std::string res; + + for (size_t i = 0; i < array.size(); ++i) { + char tmp[5]; + int size = nbytesPoint(array[i]); + + if (size < 0) + throw std::invalid_argument("invalid sequence"); + + encode(array[i], tmp); + res.insert(res.length(), tmp); + } + + return res; +} + +std::u32string toUtf32(const std::string &str) +{ + std::u32string res; + + forEach(str, [&] (char32_t code) { + res.push_back(code); + }); + + return res; +} + +} // !unicode + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/unicode.hpp Wed Oct 05 20:32:27 2016 +0200 @@ -0,0 +1,273 @@ +/* + * unicode.hpp -- UTF-8 to UTF-32 conversions and various operations + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UNICODE_HPP +#define UNICODE_HPP + +/** + * \file unicode.hpp + * \brief UTF-8 to UTF-32 conversions + * \author David Demelier <markand@malikania.fr> + * \warning These files are auto-generated! + */ + +#include <stdexcept> +#include <string> + +namespace irccd { + +/** + * \brief Unicode namespace. + */ +namespace unicode { + +/** + * Encode the unicode code point into multibyte string. + * + * \param point the unicode code point + * \param res the output buffer + */ +void encode(char32_t point, char res[5]) noexcept; + +/** + * Decode the multibyte buffer into an unicode code point. + * + * \param c the code point destination + * \param res the multibyte string. + */ +void decode(char32_t &c, const char *res) noexcept; + +/** + * Get the number of bytes for the first multi byte character from a + * utf-8 string. + * + * This can be used to iterate a valid UTF-8 string to jump to the next + * real character. + * + * \param c the first multi byte character + * \return the number of bytes [1-4] or -1 if invalid + */ +int nbytesUtf8(char c) noexcept; + +/** + * Get the number of bytes for the unicode point. + * + * \param point the unicode point + * \return the number of bytes [1-4] or -1 if invalid + */ +int nbytesPoint(char32_t point) noexcept; + +/** + * Get real number of character in a string. + * + * \param str the string + * \return the length + * \throw std::invalid_argument on invalid sequence + */ +unsigned length(const std::string &str); + +/** + * Iterate over all real characters in the UTF-8 string. + * + * The function must have the following signature: + * void f(char ch) + * + * \param str the UTF-8 string + * \param function the function callback + * \throw std::invalid_argument on invalid sequence + */ +template <typename Func> +void forEach(const std::string &str, Func function) +{ + for (size_t i = 0; i < str.size(); ) { + char32_t point = 0; + int size = nbytesUtf8(str[i]); + + if (size < 0) + throw std::invalid_argument("invalid sequence"); + + decode(point, str.data() + i); + function(point); + + i += size; + } +} + +/** + * Convert a UTF-32 string to UTF-8 string. + * + * \param array the UTF-32 string + * \return the UTF-8 string + * \throw std::invalid_argument on invalid sequence + */ +std::string toUtf8(const std::u32string &array); + +/** + * Convert a UTF-8 string to UTF-32 string. + * + * \param str the UTF-8 string + * \return the UTF-32 string + * \throw std::invalid_argument on invalid sequence + */ +std::u32string toUtf32(const std::string &str); + +/** + * Check if the unicode character is space. + * + * \param c the character + * \return true if space + */ +bool isspace(char32_t c) noexcept; + +/** + * Check if the unicode character is digit. + * + * \param c the character + * \return true if digit + */ +bool isdigit(char32_t c) noexcept; + +/** + * Check if the unicode character is alpha category. + * + * \param c the character + * \return true if alpha + */ +bool isalpha(char32_t c) noexcept; + +/** + * Check if the unicode character is upper case. + * + * \param c the character + * \return true if upper case + */ +bool isupper(char32_t c) noexcept; + +/** + * Check if the unicode character is lower case. + * + * \param c the character + * \return true if lower case + */ +bool islower(char32_t c) noexcept; + +/** + * Check if the unicode character is title case. + * + * \param c the character + * \return true if title case + */ +bool istitle(char32_t c) noexcept; + +/** + * Convert to upper case. + * + * \param c the character + * \return the upper case character + */ +char32_t toupper(char32_t c) noexcept; + +/** + * Convert to lower case. + * + * \param c the character + * \return the lower case character + */ +char32_t tolower(char32_t c) noexcept; + +/** + * Convert to title case. + * + * \param c the character + * \return the title case character + */ +char32_t totitle(char32_t c) noexcept; + +/** + * Convert the UTF-32 string to upper case. + * + * \param str the str + * \return the upper case string + */ +inline std::u32string toupper(std::u32string str) +{ + for (size_t i = 0; i < str.size(); ++i) + str[i] = toupper(str[i]); + + return str; +} + +/** + * Convert the UTF-8 string to upper case. + * + * \param str the str + * \return the upper case string + * \warning very slow at the moment + */ +inline std::string toupper(const std::string &str) +{ + std::string result; + char buffer[5]; + + forEach(str, [&] (char32_t code) { + encode(toupper(code), buffer); + result += buffer; + }); + + return result; +} + +/** + * Convert the UTF-32 string to lower case. + * + * \param str the str + * \return the lower case string + */ +inline std::u32string tolower(std::u32string str) +{ + for (size_t i = 0; i < str.size(); ++i) + str[i] = tolower(str[i]); + + return str; +} + +/** + * Convert the UTF-8 string to lower case. + * + * \param str the str + * \return the lower case string + * \warning very slow at the moment + */ +inline std::string tolower(const std::string &str) +{ + std::string result; + char buffer[5]; + + forEach(str, [&] (char32_t code) { + encode(tolower(code), buffer); + result += buffer; + }); + + return result; +} + +} // !unicode + +} // !irccd + +#endif // !UNICODE_HPP