/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    ql_misc.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/ql_misc.h"
#include "qore/intern/QoreSignal.h"
#include "qore/intern/QoreNamespaceIntern.h"
#include "qore/intern/QC_Program.h"
#include "qore/intern/ModuleInfo.h"
#include "qore/intern/qore_program_private.h"
#include "qore/intern/QoreHashNodeIntern.h"

#include <cerrno>
#include <cstring>
#include <ctime>

#ifndef WARN_MODULES
// needed so that the Qore default argument value in sinatures below will match a C++ value
#define WARN_MODULES QP_WARN_MODULES
#endif

static const QoreFunction* get_builtin_func(const QoreStringNode* str, ExceptionSink* xsink) {
   const qore_ns_private* ns;
   const QoreFunction* f = qore_root_ns_private::runtimeFindFunction(*(getRootNS()), str->c_str(), ns);
   if (f)
      return f;

   xsink->raiseException("NO-FUNCTION", "cannot find any builtin function '%s()'", str->c_str());
   return nullptr;
}

static const char* tlist[] = { "OPTION", "ALGORITHM", "FUNCTION" };

// signal constants - if they are not defined define them here
#ifndef SIGABRT
#define SIGABRT 0
#endif
#ifndef SIGALRM
#define SIGALRM 0
#endif
#ifndef SIGBUS
#define SIGBUS 0
#endif
#ifndef SIGCANCEL
#define SIGCANCEL 0
#endif
#ifndef SIGCHLD
#define SIGCHLD 0
#endif
#ifndef SIGCLD
#define SIGCLD 0
#endif
#ifndef SIGCONT
#define SIGCONT 0
#endif
#ifndef SIGEMT
#define SIGEMT 0
#endif
#ifndef SIGFPE
#define SIGFPE 0
#endif
#ifndef SIGFREEZE
#define SIGFREEZE 0
#endif
#ifndef SIGHUP
#define SIGHUP 0
#endif
#ifndef SIGILL
#define SIGILL 0
#endif
#ifndef SIGINFO
#define SIGINFO 0
#endif
#ifndef SIGINT
#define SIGINT 0
#endif
#ifndef SIGIO
#define SIGIO 0
#endif
#ifndef SIGIOT
#define SIGIOT 0
#endif
#ifndef SIGJVM1
#define SIGJVM1 0
#endif
#ifndef SIGJVM2
#define SIGJVM2 0
#endif
#ifndef SIGKILL
#define SIGKILL 0
#endif
#ifndef SIGLOST
#define SIGLOST 0
#endif
#ifndef SIGLWP
#define SIGLWP 0
#endif
#ifndef SIGPIPE
#define SIGPIPE 0
#endif
#ifndef SIGPOLL
#define SIGPOLL 0
#endif
#ifndef SIGPROF
#define SIGPROF 0
#endif
#ifndef SIGPWR
#define SIGPWR 0
#endif
#ifndef SIGQUIT
#define SIGQUIT 0
#endif
#ifndef SIGSEGV
#define SIGSEGV 0
#endif
#ifndef SIGSTKFLT
#define SIGSTKFLT 0
#endif
#ifndef SIGSTOP
#define SIGSTOP 0
#endif
#ifndef SIGSYS
#define SIGSYS 0
#endif
#ifndef SIGTERM
#define SIGTERM 0
#endif
#ifndef SIGTHAW
#define SIGTHAW 0
#endif
#ifndef SIGTRAP
#define SIGTRAP 0
#endif
#ifndef SIGTSTP
#define SIGTSTP 0
#endif
#ifndef SIGTTIN
#define SIGTTIN 0
#endif
#ifndef SIGTTOU
#define SIGTTOU 0
#endif
#ifndef SIGURG
#define SIGURG 0
#endif
#ifndef SIGUSR1
#define SIGUSR1 0
#endif
#ifndef SIGUSR2
#define SIGUSR2 0
#endif
#ifndef SIGVTALRM
#define SIGVTALRM 0
#endif
#ifndef SIGWAITING
#define SIGWAITING 0
#endif
#ifndef SIGWINCH
#define SIGWINCH 0
#endif
#ifndef SIGXCPU
#define SIGXCPU 0
#endif
#ifndef SIGXFSZ
#define SIGXFSZ 0
#endif
#ifndef SIGXRES
#define SIGXRES 0
#endif

/** @defgroup parse_url_options URL Parsing Options

    @since %Qore 1.0.10
 */
///@{
//! If the hostname or address is enclosed in square brackets, the brackets will be included in the \c "host" key
/** Square brackets are used by some %Qore methods to denote IPv6 addresses; for example see
    @ref Qore::Socket::connect() "Socket::connect()"
*/
const QURL_KEEP_BRACKETS = QURL_KEEP_BRACKETS;

//! Perform percent decoding on the \c "host", \c "username", and \c "password" fields
const QURL_DECODE = QURL_DECODE;

//! Decodes all fields like @ref QURL_DECODE plus also performs percent decoding on \c "path" and \c "query" fields
const QURL_DECODE_PATH = QURL_DECODE_PATH;
///@}

//! a hash describing a parsed URL
/** @since %Qore 0.9.3
*/
hashdecl UrlInfo {
    //! the scheme or protocol of the URL, if present
    string protocol;

    //! the path given in the URL string, if present
    string path;

    //! the query part of the URL (i.e. text after any \c '?' char), if present
    string query;

    //! the username of the URL, if present
    string username;

    //! the password given in the URL, if any
    string password;

    //! the hostname given in the URL string, if any
    string host;

    //! the port number given in the URL string, if any
    int port;
}

/** @defgroup StringConcatEncoding String Concatenation Encoding Codes

    @see <string>::getEncoded()

    @since %Qore 0.8.12
 */
///@{
//! code for encoding HTML 5 symbols as named character references
/** encodes all HTML 5 symbols as <a href="http://www.w3.org/TR/html5/syntax.html#named-character-references">named character references</a>

    @note to encode all non-ascii symbols as numeric character references as well, include @ref Qore::CE_NONASCII in the bitfield

    @see @ref Qore::CD_HTML
 */
const CE_HTML = CE_HTML;
//! code for encoding XML entities
/** The following symbols are encoded:
    - \c '\"': as \c "&quot;"
    - \c "&": as \c "&amp;"
    - \c "'": as \c "&apos;"
    - \c "<": as \c "&lt;"
    - \c ">": as \c "&gt;"

    @note \c "'" / \c "&apos;" is the only character not included in @ref Qore::CE_HTML

    @see @ref Qore::CD_XML
 */
const CE_XML = CE_XML;
//! code for encoding all non-ASCII symbols as numeric character references
/** using this code ensures that the resulting string has no non-ASCII characters

    @see @ref Qore::CD_NUM_REF
 */
const CE_NONASCII = CE_NONASCII;
//! code for encoding XHTML entities
/** This code is a combination of @ref Qore::CE_HTML and @ref Qore::CE_XML

    @see @ref Qore::CD_XHTML
 */
const CE_XHTML = CE_XHTML;
//! code for encoding everything
/** @see @ref Qore::CD_ALL
 */
const CE_ALL = CE_ALL;
///@}

/** @defgroup StringConcatDecoding String Concatenation Decoding Codes

    @see <string>::getDecoded()

    @since %Qore 0.8.12
 */
///@{
//! code for decoding HTML 5 named character references to their native symbols
/** decodes all HTML 5 <a href="http://www.w3.org/TR/html5/syntax.html#named-character-references">named character references</a> to their native symbols

    @note to decode all numeric character references as well, include @ref Qore::CD_NUM_REF in the bitfield

    @see @ref Qore::CE_HTML
 */
const CD_HTML = CD_HTML;
//! code for decoding XML entities
/** The following symbols are decoded:
    - \c '\"': as \c "&quot;"
    - \c "&": as \c "&amp;"
    - \c "'": as \c "&apos;"
    - \c "<": as \c "&lt;"
    - \c ">": as \c "&gt;"

    @note \c "'" / \c "&apos;" is the only character not included in @ref Qore::CD_HTML

    @see @ref Qore::CE_XML
 */
const CD_XML = CD_XML;
//! code for decoding numeric character references to symbols
/** @see @ref CE_NONASCII
 */
const CD_NUM_REF = CD_NUM_REF;
//! code for decoding XHTML named character references to symbols
/** This code is a combination of @ref Qore::CD_HTML and @ref Qore::CD_XML

    @see @ref Qore::CE_XHTML
 */
const CD_XHTML = CD_XHTML;
//! code for decoding everything
/** @see @ref Qore::CE_ALL
 */
const CD_ALL = CD_ALL;
///@}

/** @defgroup signal_constants Signal Constants
    Signal constants - if any of the constants in this section are not defined on the host; the constant's value will be 0
*/
///@{
//! SIGABRT
const SIGABRT = SIGABRT;
//! SIGALRM
const SIGALRM = SIGALRM;
//! SIGBUS
const SIGBUS = SIGBUS;
//! SIGCANCEL
const SIGCANCEL = SIGCANCEL;
//! SIGCHLD
const SIGCHLD = SIGCHLD;
//! SIGCLD
const SIGCLD = SIGCLD;
//! SIGCONT
const SIGCONT = SIGCONT;
//! SIGEMT
const SIGEMT = SIGEMT;
//! SIGFPE
const SIGFPE = SIGFPE;
//! SIGFREEZE
const SIGFREEZE = SIGFREEZE;
//! SIGHUP
const SIGHUP = SIGHUP;
//! SIGILL
const SIGILL = SIGILL;
//! SIGINFO
const SIGINFO = SIGINFO;
//! SIGINT
const SIGINT = SIGINT;
//! SIGIO
const SIGIO = SIGIO;
//! SIGIOT
const SIGIOT = SIGIOT;
//! SIGJVM1
const SIGJVM1 = SIGJVM1;
//! SIGJVM2
const SIGJVM2 = SIGJVM2;
//! SIGKILL
const SIGKILL = SIGKILL;
//! SIGLOST
const SIGLOST = SIGLOST;
//! SIGLWP
const SIGLWP = SIGLWP;
//! SIGPIPE
const SIGPIPE = SIGPIPE;
//! SIGPOLL
const SIGPOLL = SIGPOLL;
//! SIGPROF
const SIGPROF = SIGPROF;
//! SIGPWR
const SIGPWR = SIGPWR;
//! SIGQUIT
const SIGQUIT = SIGQUIT;
//! SIGSEGV
const SIGSEGV = SIGSEGV;
//! SIGSTKFLT
const SIGSTKFLT = SIGSTKFLT;
//! SIGSTOP
const SIGSTOP = SIGSTOP;
//! SIGSYS
const SIGSYS = SIGSYS;
//! SIGTERM
const SIGTERM = SIGTERM;
//! SIGTHAW
const SIGTHAW = SIGTHAW;
//! SIGTRAP
const SIGTRAP = SIGTRAP;
//! SIGTSTP
const SIGTSTP = SIGTSTP;
//! SIGTTIN
const SIGTTIN = SIGTTIN;
//! SIGTTOU
const SIGTTOU = SIGTTOU;
//! SIGURG
const SIGURG = SIGURG;
//! SIGUSR1
const SIGUSR1 = SIGUSR1;
//! SIGUSR2
const SIGUSR2 = SIGUSR2;
//! SIGVTALRM
const SIGVTALRM = SIGVTALRM;
//! SIGWAITING
const SIGWAITING = SIGWAITING;
//! SIGWINCH
const SIGWINCH = SIGWINCH;
//! SIGXCPU
const SIGXCPU = SIGXCPU;
//! SIGXFSZ
const SIGXFSZ = SIGXFSZ;
//! SIGXRES
const SIGXRES = SIGXRES;

//! maps signal names to signal values
const NameToSignal = (
   "SIGABRT": SIGABRT,
   "SIGALRM": SIGALRM,
   "SIGBUS": SIGBUS,
   "SIGCANCEL": SIGCANCEL,
   "SIGCHLD": SIGCHLD,
   "SIGCLD": SIGCLD,
   "SIGCONT": SIGCONT,
   "SIGEMT": SIGEMT,
   "SIGFPE": SIGFPE,
   "SIGFREEZE": SIGFREEZE,
   "SIGHUP": SIGHUP,
   "SIGILL": SIGILL,
   "SIGINFO": SIGINFO,
   "SIGINT": SIGINT,
   "SIGIO": SIGIO,
   "SIGIOT": SIGIOT,
   "SIGJVM1": SIGJVM1,
   "SIGJVM2": SIGJVM2,
   "SIGKILL": SIGKILL,
   "SIGLOST": SIGLOST,
   "SIGLWP": SIGLWP,
   "SIGPIPE": SIGPIPE,
   "SIGPOLL": SIGPOLL,
   "SIGPROF": SIGPROF,
   "SIGPWR": SIGPWR,
   "SIGQUIT": SIGQUIT,
   "SIGSEGV": SIGSEGV,
   "SIGSTKFLT": SIGSTKFLT,
   "SIGSTOP": SIGSTOP,
   "SIGSYS": SIGSYS,
   "SIGTERM": SIGTERM,
   "SIGTHAW": SIGTHAW,
   "SIGTRAP": SIGTRAP,
   "SIGTSTP": SIGTSTP,
   "SIGTTIN": SIGTTIN,
   "SIGTTOU": SIGTTOU,
   "SIGURG": SIGURG,
   "SIGUSR1": SIGUSR1,
   "SIGUSR2": SIGUSR2,
   "SIGVTALRM": SIGVTALRM,
   "SIGWAITING": SIGWAITING,
   "SIGWINCH": SIGWINCH,
   "SIGXCPU": SIGXCPU,
   "SIGXFSZ": SIGXFSZ,
   "SIGXRES": SIGXRES,
   );

//! maps signal numbers (as a string key) to the symbolic name for the signal
const SignalToName = (
   SIGABRT: "SIGABRT",
   SIGALRM: "SIGALRM",
   SIGBUS: "SIGBUS",
   SIGCANCEL: "SIGCANCEL",
   SIGCHLD: "SIGCHLD",
   SIGCONT: "SIGCONT",
   SIGEMT: "SIGEMT",
   SIGFPE: "SIGFPE",
   SIGFREEZE: "SIGFREEZE",
   SIGHUP: "SIGHUP",
   SIGILL: "SIGILL",
   SIGINFO: "SIGINFO",
   SIGINT: "SIGINT",
   SIGIO: "SIGIO",
   SIGIOT: "SIGIOT",
   SIGJVM1: "SIGJVM1",
   SIGJVM2: "SIGJVM2",
   SIGKILL: "SIGKILL",
   SIGLOST: "SIGLOST",
   SIGLWP: "SIGLWP",
   SIGPIPE: "SIGPIPE",
   SIGPOLL: "SIGPOLL",
   SIGPROF: "SIGPROF",
   SIGPWR: "SIGPWR",
   SIGQUIT: "SIGQUIT",
   SIGSEGV: "SIGSEGV",
   SIGSTKSZ: "SIGSTKSZ",
   SIGSTOP: "SIGSTOP",
   SIGSYS: "SIGSYS",
   SIGTERM: "SIGTERM",
   SIGTHAW: "SIGTHAW",
   SIGTRAP: "SIGTRAP",
   SIGTSTP: "SIGTSTP",
   SIGTTIN: "SIGTTIN",
   SIGTTOU: "SIGTTOU",
   SIGURG: "SIGURG",
   SIGUSR1: "SIGUSR1",
   SIGUSR2: "SIGUSR2",
   SIGVTALRM: "SIGVTALRM",
   SIGWAITING: "SIGWAITING",
   SIGWINCH: "SIGWINCH",
   SIGXCPU: "SIGXCPU",
   SIGXFSZ: "SIGXFSZ",
   SIGXRES: "SIGXRES",
);
///@}

/** @defgroup signal_handling_functions Signal Handing Functions
    Signal handing functions
*/
///@{
//! Sets or replaces a signal handler according to the signal number and closure or call reference (function or object method reference) passed
/** @par Platform Availability:
    @ref Qore::Option::HAVE_SIGNAL_HANDLING

    By the time this function returns, changes to the signal handling thread have already been effected.

    When a signal is raised and the signal handler code is called, the signal number is passed as an integer argument to the signal handling code.

    @param signal The signal number to process, see @ref signal_constants for possible values
    @param f The code to execute when the signal is caught; this should accept an integer argument giving the signal number

    @par Example:
    @code{.py}
set_signal_handler(SIGINT, \signal_handler());
    @endcode

    @see @ref signal_handling for more information
*/
nothing set_signal_handler(softint signal, code f) [dom=PROCESS] {
#ifdef HAVE_SIGNAL_HANDLING
    if (signal <= 0 || signal > QORE_SIGNAL_MAX)
        return xsink->raiseException("SET-SIGNAL-HANDLER-ERROR", "%d is not a valid signal", signal);

    QSM.setHandler(signal, f, xsink);
#else
    return xsink->raiseException("MISSING-FEATURE-ERROR", "this platform does not support signal handling, therefore "
        "the set_signal_handler() and remove_signal_handler() functions are not available in Qore; for maximum "
        "portability, use the constant Option::HAVE_SIGNAL_HANDLING to check if this function is implemented before "
        "calling");
#endif
}

//! Removes a signal handler and returns the signal handling state to the default
/**
    @par Platform Availability:
    @ref Qore::Option::HAVE_SIGNAL_HANDLING

    By the time this function returns, changes to the signal handling thread have already been effected.

    @param signal The signal number to process, see @ref signal_constants for possible values

    @par Example:
    @code{.py}
remove_signal_handler(SIGINT);
    @endcode

    @see @ref signal_handling for more information
*/
nothing remove_signal_handler(softint signal) [dom=PROCESS] {
#ifdef HAVE_SIGNAL_HANDLING
    if (signal <= 0 || signal > QORE_SIGNAL_MAX) {
        return xsink->raiseException("REMOVE-SIGNAL-HANDLER-ERROR", "%d is not a valid signal", signal);
    }
    QSM.removeHandler(signal, xsink);
#else
    return xsink->raiseException("MISSING-FEATURE-ERROR", "this platform does not support signal handling, therefore "
        "the set_signal_handler() and remove_signal_handler() functions are not available in Qore; for maximum "
        "portability, use the constant Option::HAVE_SIGNAL_HANDLING to check if this function is implemented before "
        "calling");
#endif
}
///@}

/** @defgroup misc_functions Miscellaneous Functions
    Miscellaneous functions
 */
///@{
//! Calls a function and returns the return value, passing the remaining arguments after the function name to the function
/**
    @param name The name of the function to call
    @param ... Any optional arguments to the function

    @return The value returned by the called function

    @par Example:
    @code{.py}
auto result = call_function("func_name", arg1, arg2);
    @endcode

    @throw INVALID-FUNCTION-ACCESS Parse options do not allow access to the function
    @throw NO-FUNCTION The function does not exist

    @note The function called could also throw other exceptions

    @see
    - call_function_args()
    - call_static_method()
    - call_static_method_args()
    - call_object_method()
    - call_object_method_args()
*/
auto call_function(string name, ...) {
    ReferenceHolder<QoreListNode> vargs(xsink);
    // if there are arguments to pass, create argument list by copying current list
    if (num_args(args) > 1) {
        vargs = args->copyListFrom(1);
    }

    return getProgram()->callFunction(name->c_str(), *vargs, xsink);
}

//! Calls the given @ref call_reference "call reference" or @ref closure "closure" and returns the result, passing the remaining arguments to the @ref call_reference "call reference" or @ref closure "closure"
/**
    @param f The @ref call_reference "call reference" or @ref closure "closure"
    @param ... Any optional arguments to the @ref call_reference "call reference" or @ref closure "closure"

    @par Example:
    @code{.py}
auto result = call_function(call_ref, arg1, arg2);
    @endcode

    @note The @ref call_reference "call reference" or @ref closure "closure" called could also throw other exceptions

    @see
    - call_function_args()
    - call_static_method()
    - call_static_method_args()
    - call_object_method()
    - call_object_method_args()
*/
auto call_function(code f, ...) {
    ReferenceHolder<QoreListNode> vargs(xsink);
    // if there are arguments to pass, create argument list by copying current list
    if (num_args(args) > 1) {
        vargs = args->copyListFrom(1);
    }

    return f->execValue(*vargs, xsink);
}

//! Calls a function and returns the return value, using the optional second argument as a list of arguments for the function
/**
    @param name The name of the function to call
    @param vargs Optionally a single argument to the function or a list of arguments to the function

    @return The value returned by the called function

    @par Example:
    @code{.py}
auto result = call_function_args("func_name", (arg1, arg2));
    @endcode

    @throw INVALID-FUNCTION-ACCESS Parse options do not allow access to the function
    @throw NO-FUNCTION The function does not exist

    @note The function called could also throw other exceptions

    @see
    - call_function()
    - call_static_method()
    - call_static_method_args()
    - call_object_method()
    - call_object_method_args()
*/
auto call_function_args(string name, *softlist<auto> vargs) {
    return getProgram()->callFunction(name->c_str(), vargs, xsink);
}

//! Calls the given @ref call_reference "call reference" or @ref closure "closure" and returns the result, using the optional second argument as a list of arguments to the @ref call_reference "call reference" or @ref closure "closure"
/**
    @param f The @ref call_reference "call reference" or @ref closure "closure"
    @param vargs Optionally a single argument to the @ref call_reference "call reference" or @ref closure "closure" or a list of arguments to the @ref call_reference "call reference" or @ref closure "closure"

    @par Example:
    @code{.py}
auto result = call_function_args(call_ref, (arg1, arg2));
    @endcode

    @note The @ref call_reference "call reference" or @ref closure "closure" called could also throw other exceptions

    @see
    - call_function()
    - call_static_method()
    - call_static_method_args()
    - call_object_method()
    - call_object_method_args()
*/
auto call_function_args(code f, *softlist<auto> vargs) {
    return f->execValue(vargs, xsink);
}

//! Calls a function and returns the return value, passing the remaining arguments after the function name to the builtin function
/**
    @param name The name of the builtin function to call
    @param ... Any optional arguments to the function

    @return The value returned by the called function

    @par Example:
    @code{.py}
auto result = call_builtin_function("func_name", arg1, arg2);
    @endcode

    @throw INVALID-FUNCTION-ACCESS Parse options do not allow access to the function
    @throw NO-FUNCTION The function does not exist

    @note The function called could also throw other exceptions

    @since 0.8.4 this function no longer restricts its search to builtin functions as as of Qore 0.8.4 builtin and user functions are stored identically internally; there is only one function implementation which can contain both builtin and user variants
*/
auto call_builtin_function(string name, ...) {
    const QoreFunction* f = get_builtin_func(name, xsink);
    if (!f)
        return QoreValue();

    ReferenceHolder<QoreListNode> vargs(xsink);
    // if there are arguments to pass, create argument list by copying current list
    if (num_args(args) > 1) {
        vargs = args->copyListFrom(1);
    }

    return f->evalDynamic(*vargs, xsink);
}

//! Calls a function and returns the return value, using the optional second argument as a list of arguments for the function
/**
    @param name The name of the builtin function to call
    @param vargs Optionally a single argument to the function or a list of arguments to the function

    @return The value returned by the called function

    @par Example:
    @code{.py}
auto result = call_builtin_function_args("func_name", (arg1, arg2));
    @endcode

    @throw INVALID-FUNCTION-ACCESS Parse options do not allow access to the function
    @throw NO-FUNCTION The function does not exist

    @note The function called could also throw other exceptions

    @since 0.8.4 this function no longer restricts its search to builtin functions as as of Qore 0.8.4 builtin and user functions are stored identically internally; there is only one function implementation which can contain both builtin and user variants
*/
auto call_builtin_function_args(string name, *softlist<auto> vargs) {
    const QoreFunction* f = get_builtin_func(name, xsink);
    if (!f)
        return QoreValue();

    return f->evalDynamic(vargs, xsink);
}

//! A function performing the same role as the @ref exists "exists operator"
/** @param ... if only a single argument is passed, then this function returns @ref True if the single argument exists, @ref False if not; otherwise is multiple arguments are passed to the function, it always returns @ref True; this is to emulate the behavior of the @ref exists "exists operator"

    @return if only a single argument is passed, then this function returns @ref True if the single argument exists, @ref False if not; otherwise is multiple arguments are passed to the function, it always returns @ref True; this is to emulate the behavior of the @ref exists "exists operator"

    @par Example:
    @code{.py}
bool b = exists(val);
    @endcode
*/
bool exists(...) [flags=CONSTANT] {
    // to emulate the exists operator, we must return True if more than one argument is passed
    // as this will appear to be a list to the exists operator, which is different from NOTHING
    return (num_args(args) <= 1) ? !get_param_value(args, 0).isNothing() : true;
}

//! Returns @ref True if the function exists in the current program's function name space
/** @param name the name of the function to check

    @return @ref True if the function exists in the current program's function name space, @ref False if not

    @deprecated use exists_function(); camel-case function names were deprecated in %Qore 0.8.12
*/
bool existsFunction(string name) [flags=CONSTANT,DEPRECATED] {
    return getProgram()->existsFunction(name->c_str());
}

//! Always returns @ref True
/** This function variant is included for backwards-compatibility

    @param c a @ref call_reference "call reference" or @ref closure "closure":

    @return always returns @ref True

    @deprecated use exists_function(); camel-case function names were deprecated in %Qore 0.8.12
*/
bool existsFunction(code[doc] c) [flags=NOOP,DEPRECATED] {
    return true;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing existsFunction() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns @ref True if the function exists in the current program's function name space
/** @param name the name of the function to check

    @return @ref True if the function exists in the current program's function name space, @ref False if not

    @par Example:
    @code{.py}
bool b = exists_function("func_name");
    @endcode

    @since %Qore 0.8.12 as a replacement for deprecated camel-case existsFunction()
*/
bool exists_function(string name) [flags=CONSTANT] {
    return getProgram()->existsFunction(name->c_str());
}

//! Always returns @ref True
/** This function variant is included for backwards-compatibility

    @param c a @ref call_reference "call reference" or @ref closure "closure":

    @return always returns @ref True

    @since %Qore 0.8.12 as a replacement for deprecated camel-case existsFunction()
*/
bool exists_function(code[doc] c) [flags=NOOP] {
   return true;
}

//! Returns \c "builtin" (for a builtin function), \c "user" (for a user function), or @ref nothing (if the function cannot be found) according to the function name passed
/**
    @param name The function name to check
    @return \c "builtin" (for a builtin function), \c "user" (for a user function), or @ref nothing (if the function cannot be found) according to the function name passed

    @deprecated use function_type(); camel-case function names were deprecated in %Qore 0.8.12
*/
*string functionType(string name) [flags=CONSTANT,DEPRECATED] {
    const qore_ns_private* ns;
    const QoreFunction* f = qore_root_ns_private::runtimeFindFunction(*(getRootNS()), name->c_str(), ns);
    if (!f)
        return QoreValue();

    return new QoreStringNode(f->hasBuiltin() ? "builtin" : "user");
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing functionType() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns \c "builtin" (for a builtin function), \c "user" (for a user function), or @ref nothing (if the function cannot be found) according to the function name passed
/**
    @param name The function name to check
    @return \c "builtin" (for a builtin function), \c "user" (for a user function), or @ref nothing (if the function cannot be found) according to the function name passed

    @par Example:
    @code{.py}
*string type = function_type("print");
    @endcode

    @since %Qore 0.8.12 as a replacement for deprecated camel-case functionType()
*/
*string function_type(string name) {
    const qore_ns_private* ns;
    const QoreFunction* f = qore_root_ns_private::runtimeFindFunction(*(getRootNS()), name->c_str(), ns);
    if (!f)
        return QoreValue();

    return new QoreStringNode(f->hasBuiltin() ? "builtin" : "user");
}

//! Returns a string with characters needing HTML escaping translated to HTML escape codes
/** @param str the argument to process

    @return the string passed as an argument with characters needing HTML escaping translated to HTML escape codes

    @par Example:
    @code{.py}
string str = html_encode("<hello>"); # returns "&lt;hello&gt;"
    @endcode

    @note equivalent to <string>::getEncoded() with @ref Qore::CE_HTML

    @see <string>::getEncoded()
*/
string html_encode(string str) [flags=CONSTANT] {
    QoreStringNodeHolder rv(new QoreStringNode(str->getEncoding()));
    return rv->concatEncode(xsink, *str, CE_HTML) ? QoreValue() : rv.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing html_encode() [flags=RUNTIME_NOOP] {
}

//! Returns a string with any HTML escape codes translated to the original characters
/** @param str the argument to decode

    @return the string passed as an argument with any HTML escape codes translated to the original characters

    @par Example:
    @code{.py}
string str = html_decode("&lt;hello&gt;"); # returns "<hello>"
    @endcode

    @note equivalent to <string>::getDecoded() with @ref Qore::CD_HTML

    @see <string>::getDecoded()
*/
string html_decode(string str) [flags=CONSTANT] {
    QoreStringNodeHolder rv(new QoreStringNode(str->getEncoding()));
    return rv->concatDecode(xsink, *str, CD_HTML) ? QoreValue() : rv.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing html_decode() [flags=RUNTIME_NOOP] {
}

//! Returns the name of the @ref default_encoding "default character encoding"
/** @return the name of the @ref default_encoding "default character encoding"

    @par Example:
    @code{.py}
string encoding = get_default_encoding();
    @endcode
*/
string get_default_encoding() {
    return new QoreStringNode(QCS_DEFAULT->getCode());
}

//! Adds the text passed to the current program's code, tagged with the given label
/**
    @param code the string of %Qore source code to parse; the parsed code will be added to the current program
    @param label a label identifying the code or the code's source
    @param warning_mask An optional warning mask; see @ref warning_constants for values to combine by binary-or; if this arguments is 0 or not given then no warnings will be checked or issued and the return value will always be @ref nothing
    @param source An optional source file name for the code being parsed; this is useful if sections of a file are parsed
    @param offset An optional line offset for use with the \a source parameter; this gives the line offset in the file to the code being parsed
    @param format_label %Qore 0.9 is obsolete / ignored

    @par Example:
    @code{.py}
parse(code, filename);
    @endcode

    @note This function could throw many parse exceptions which are not enumerated here; any parse errors will result in an appropriate exception.

    @see
    - Qore::Program::parse()
    - Qore::Program::parsePending()

    @since %Qore 0.8.7 the optional \a warning_mask, \a source, \a offset, and \a format_label arguments were added; %Qore 0.9 the \a format_label is obsolete / ignored;
    these changes were made to align the functionality of this function with the @ref Qore::Program::parse() "Program::parse()"
    and @ref Qore::Program::parsePending() "Program::parsePending()" methods
*/
*hash<auto> parse(string code, string label, *softint warning_mask, *string source, *softint offset, softbool[doc] format_label = True) [dom=EMBEDDED_LOGIC,IN_MODULE] {
    if (warning_mask) {
        ExceptionSink wsink;
        getProgram()->parse(code, label, xsink, &wsink, warning_mask, source, offset);
        if (!wsink.isException())
            return QoreValue();

        QoreException* e = wsink.catchException();
        return e->makeExceptionObjectAndDelete(xsink);
    }

    getProgram()->parse(code, label, xsink, 0, 0, source, offset);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing parse() [flags=RUNTIME_NOOP;dom=EMBEDDED_LOGIC,IN_MODULE] {
}

//! Returns the class name of the object passed
/**
    @param obj the object to get the class name of
    @return the class name of the object passed

    @deprecated use get_class_name(); camel-case function names were deprecated in %Qore 0.8.12
*/
string getClassName(object obj) [flags=CONSTANT,DEPRECATED] {
    return new QoreStringNode(obj->getClass()->getName());
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getClassName() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns the class name of the object passed
/**
    @param obj the object to get the class name of
    @return the class name of the object passed

    @par Example:
    @code{.py}
string name = get_class_name(obj);
    @endcode

    @note equivalent to <object>::className()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getClassName()
*/
string get_class_name(object obj) [flags=CONSTANT] {
    return new QoreStringNode(obj->getClass()->getName());
}

//! Parses a URL string and returns a hash of the components
/** Throws an exception if the string cannot be parsed as a URL; does not perform percent decoding

    @param url the URL to parse (ex: \c "https://user:pass@host:8080/path"); either a hostname or path is required at
    a minimum or a \c PARSE-URL-ERROR exception is raised
    @param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then
    the brackets will be included in the \c "host" key output as well; square brackets are used by some %Qore methods
    to denote IPv6 addresses; for example see @ref Qore::Socket::connect() "Socket::connect()"

    @return a hash of the components of the URL with the following keys (if data in the URL is present; note that at
    least either the \c "host" or the \c "path" keys will always be returned if no \c PARSE-URL-ERROR is raised):
    - \c protocol: the scheme in the URL (ex: \c "http")
    - \c path: any path given in the URL; the path will be prefixed by \c "/" if a hostname is found in the URL
      argument string, otherwise it will not if it was not given as such in the argument string - not subjected to
      percent decoding (see note below)
    - \c username: any username given in the URL - not subjected to percent decoding (see note below)
    - \c password: any password given in the URL - not subjected to percent decoding (see note below)
    - \c host: any hostname given in the URL; note that this key will be given if no other information can be found in
      the URL argument and the URL argument string has no \c "/" characters; depending on the usage context for this
      function, this may actually be a filename
    - \c port: any port number given in the URL

    @par Example:
    @code{.py}
hash<UrlInfo> h = parse_url(url_string, True);
    @endcode

    @throw PARSE-URL-ERROR The URL string given could not be parsed

    @note
    - URLs with UNIX sockets are generally supported in Qore with the following syntax:
      <tt><b>scheme://socket=</b></tt><i>url_encoded_path</i><tt><b>/path</b></tt>, where <i>url_encoded_path</i> is a
      path with URL-encoding as performed by @ref encode_url() "encode_url(string, True)"; for example:
      \c "http://socket=%2ftmp%socket-dir%2fsocket-file-1/url/path"; this allows a filesystem path to be used in the
      host portion of the URL and for the URL to include a URL path as well.
    - none of the string fields returned here are subjected to percent decoding; to decode percent encoding, call
      @ref Qore::decode_url() "decode_url()" on the strings manually or use the @ref parse_url(string, __7_ int) variant
      with the @ref QURL_DECODE or @ref QURL_DECODE_PATH option

    @see parseURL() for a version of this function that does not throw exceptions if the URL cannot be parsed
*/
hash<UrlInfo> parse_url(string url, bool keep_brackets) {
    QoreURL qurl(url, keep_brackets, xsink);
    if (*xsink)
        return QoreValue();
    assert(qurl.isValid());
    return qurl.getHash();
}

//! Parses a URL string and returns a hash of the components
/** Throws an exception if the string cannot be parsed as a URL

    @param url the URL to parse (ex: \c "https://user:pass@host:8080/path"); either a hostname or path is required at
    a minimum or a \c PARSE-URL-ERROR exception is raised
    @param options a bitfield of @ref parse_url_options

    @return a hash of the components of the URL with the following keys (if data in the URL is present; note that at
    least either the \c "host" or the \c "path" keys will always be returned if no \c PARSE-URL-ERROR is raised):
    - \c protocol: the scheme in the URL (ex: \c "http")
    - \c path: any path given in the URL; the path will be prefixed by \c "/" if a hostname is found in the URL
      argument string, otherwise it will not if it was not given as such in the argument string; subject to percent
      decoding if @ref QURL_DECODE passed in \a options
    - \c username: any username given in the URL; subject to percent decoding if @ref QURL_DECODE or
      @ref QURL_DECODE_PATH passed in \a options
    - \c password: any password given in the URL; subject to percent decoding if @ref QURL_DECODE or
      @ref QURL_DECODE_PATH passed in \a options
    - \c host: any hostname given in the URL; note that this key will be given if no other information can be found in
      the URL argument and the URL argument string has no \c "/" characters; depending on the usage context for this
      function, this may actually be a filename; subject to percent decoding if @ref QURL_DECODE or
      @ref QURL_DECODE_PATH passed in \a options
    - \c port: any port number given in the URL

    @par Example:
    @code{.py}
hash<UrlInfo> h = parse_url(url_string, QURL_DECODE);
    @endcode

    @throw PARSE-URL-ERROR The URL string given could not be parsed

    @note
    - URLs with UNIX sockets are generally supported in Qore with the following syntax:
      <tt><b>scheme://socket=</b></tt><i>url_encoded_path</i><tt><b>/path</b></tt>, where <i>url_encoded_path</i> is a
      path with URL-encoding as performed by @ref encode_url() "encode_url(string, True)"; for example:
      \c "http://socket=%2ftmp%socket-dir%2fsocket-file-1/url/path"; this allows a filesystem path to be used in the
      host portion of the URL and for the URL to include a URL path as well.

    @see
    - parseURL() for a version of this function that does not throw exceptions if the URL cannot be parsed
*/
hash<UrlInfo> parse_url(string url, *int options) {
    QoreURL qurl(xsink, url, options);
    return qurl.isValid() ? qurl.getHash() : QoreValue();
}

//! Parses a URL string and returns a hash of the components; if the URL cannot be parsed then @ref nothing is returned
/** @param url the URL to parse (ex: \c "https://user:pass@host:8080/path"); either a hostname or path is required at
    a minimum or the function will return @ref nothing
    @param keep_brackets if this argument is true then if the hostname or address is enclosed in square brackets, then
    the brackets will be included in the \c "host" key output as well; square brackets are used by some %Qore methods
    to denote IPv6 addresses; for example see @ref Qore::Socket::connect() "Socket::connect()"

    @return a hash of the components of the URL with the following keys (if data in the URL is present; note that at
    least either the \c "host" or the \c "path" keys will always be returned if a hash is returned):
    - \c protocol: the scheme in the URL (ex: \c "http")
    - \c path: any path given in the URL; the path will be prefixed by \c "/" if a hostname is found in the URL
      argument string, otherwise it will not if it was not given as such in the argument string - not subjected to
      percent decoding (see note below)
    - \c username: any username given in the URL - not subjected to percent decoding (see note below)
    - \c password: any password given in the URL - not subjected to percent decoding (see note below)
    - \c host: any hostname given in the URL; note that this key will be given if no other information can be found in
      the URL argument and the URL argument string has no \c "/" characters; depending on the usage context for this
      function, this may actually be a filename - not subjected to percent decoding (see note below)

    - \c port: any port number given in the URL

    @par Example:
    @code{.py}
*hash<UrlInfo> h = parseURL(url_string);
    @endcode

    @note
    - URLs with UNIX sockets are generally supported in Qore with the following syntax:
      <tt><b>scheme://socket=</b></tt><i>url_encoded_path</i><tt><b>/path</b></tt>, where <i>url_encoded_path</i> is a
      path with URL-encoding as performed by @ref encode_url() "encode_url(string, True)"; for example:
      \c "http://socket=%2ftmp%socket-dir%2fsocket-file-1/url/path"; this allows a filesystem path to be used in the
      host portion of the URL and for the URL to include a URL path as well.
    - none of the string fields returned here are subjected to percent decoding; to decode percent encoding, call
      @ref Qore::decode_url() "decode_url()" on the strings manually or use the @ref parse_url(string, __7_ int) variant
      with the @ref QURL_DECODE or @ref QURL_DECODE_PATH options

    @see parse_url() for a version of this function that throws exceptions if the URL cannot be parsed
*/
*hash<UrlInfo> parseURL(string url, bool keep_brackets = False) [flags=CONSTANT] {
    QoreURL qurl(url, keep_brackets);
    return qurl.isValid() ? qurl.getHash() : QoreValue();
}

//! Returns the URL string passed without any password information
/** @param url the URL to process

    @return the URL string passed without any password information

    @since %Qore 1.13
*/
string get_safe_url(string url) {
    QoreURL qurl(xsink, url, QURL_KEEP_BRACKETS);
    if (!qurl.isValid()) {
        assert(*xsink);
        return QoreValue();
    }
    if (!qurl.getPassword()) {
        url->ref();
        return url;
    }

    SimpleRefHolder<QoreStringNode> rv(new QoreStringNode(QCS_UTF8));

    const QoreString* str = qurl.getProtocol();
    if (str && !str->empty()) {
        rv->concat(str, xsink);
        rv->concat("://");
    }

    str = qurl.getUserName();
    bool has_user_or_pass = false;
    if (str && !str->empty()) {
        rv->concat(str, xsink);
        has_user_or_pass = true;
    }

    str = qurl.getPassword();
    if (str && !str->empty()) {
        rv->concat(":<masked>");
        if (!has_user_or_pass) {
            has_user_or_pass = true;
        }
    }
    if (has_user_or_pass) {
        rv->concat('@');
    }

    str = qurl.getHost();
    if (str && !str->empty()) {
        rv->concat(str, xsink);
    }

    if (qurl.getPort()) {
        rv->sprintf(":%d", qurl.getPort());
    }

    str = qurl.getPath();
    if (str && !str->empty()) {
        rv->concat(str, xsink);
    }

    return rv.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing parseURL() [flags=RUNTIME_NOOP] {
}

//! Executes a process and returns a string of the output (stdout only)
/**
    @param cmd The shell command to executed as a subprocess
    @param rc an optional reference to an integer to return the return code of the process

    @return The stdout of the shell command that was executed

    @par Example:
    @code{.py}
int rc;
string files = backquote("ls", \rc);
    @endcode

    @throw BACKQUOTE-ERROR An error occurred with the fork() or opening the stdout pipe

    @see
    - system()
    - @ref backquote_operator "the backquote operator"

    @since %Qore 0.8.8 the \a rc argument was added
*/
string backquote(string cmd, *reference<int> rc) [dom=EXTERNAL_PROCESS] {
    int prc;
    SimpleRefHolder<QoreStringNode> str(backquoteEval(cmd->c_str(), prc, xsink));
    if (!*xsink && rc) {
        QoreTypeSafeReferenceHelper rh(rc, xsink);
        if (!rh)
            return QoreValue();

        rh.assign((int64)prc);
    }
    return str.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing backquote() [flags=RUNTIME_NOOP;dom=EXTERNAL_PROCESS] {
}

//! Returns a base64-encoded representation of a string
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the string to encode
    @param maxlinelen the maximum length of a line in the resulting output string in bytes; if this value is > 0 then output lines will be separated by CRLF characters

    @return the base64-encoded string of the data passed

    @since the maxlinelen parameter was added in %Qore 0.8.4

    @see
    - <string>::toBase64()
    - make_hex_string(string)

    @deprecated use make_base64_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string makeBase64String(string str, softint maxlinelen = -1) [flags=CONSTANT,DEPRECATED] {
   QoreStringNode* rv = new QoreStringNode;
   rv->concatBase64(str, maxlinelen);
   return rv;
}

//! Returns a base64-encoded representation of a binary object
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param bin the data to encode
    @param maxlinelen the maximum length of a line in the resulting output string in bytes; if this value is > 0 then output lines will be separated by CRLF characters

    @return the base64-encoded string of the data passed

    @since the maxlinelen parameter was added in %Qore 0.8.4

    @see
    - <binary>::toBase64()
    - make_hex_string(binary)

    @deprecated use make_base64_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string makeBase64String(binary bin, softint maxlinelen = -1) [flags=CONSTANT,DEPRECATED] {
   return new QoreStringNode(bin, maxlinelen);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing makeBase64String() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns a base64-encoded representation of a string
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the string to encode
    @param maxlinelen the maximum length of a line in the resulting output string in bytes; if this value is > 0 then output lines will be separated by CRLF characters

    @return the base64-encoded string of the data passed

    @par Example:
    @code{.py}
string base64 = make_base64_string(data, 64);
    @endcode

    @see
    - <string>::toBase64()
    - make_hex_string(string)

    @since %Qore 0.8.12 as a replacement for deprecated camel-case makeBase64String()
*/
string make_base64_string(string str, softint maxlinelen = -1) [flags=CONSTANT] {
   QoreStringNode* rv = new QoreStringNode;
   rv->concatBase64(str, maxlinelen);
   return rv;
}

//! Returns a base64-encoded representation of a binary object
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param bin the data to encode
    @param maxlinelen the maximum length of a line in the resulting output string in bytes; if this value is > 0 then output lines will be separated by CRLF characters

    @return the base64-encoded string of the data passed

    @par Example:
    @code{.py}
string base64 = make_base64_string(data, 64);
    @endcode

    @see
    - <binary>::toBase64()
    - make_hex_string(binary)

    @since %Qore 0.8.12 as a replacement for deprecated camel-case makeBase64String()
*/
string make_base64_string(binary bin, softint maxlinelen = -1) [flags=CONSTANT] {
   return new QoreStringNode(bin, maxlinelen);
}

//! Returns a base64-url-encoded representation of a string
/** Just like @ref make_base64_string() but with \c "+" and \c "/" subject to URL percent encoding

    @param str the string to encode

    @return the base64-url-encoded string of the data passed

    @par Example:
    @code{.py}
string base64 = make_base64_url_string(data);
    @endcode

    @see
    - <string>::toBase64Url()

    @since %Qore 1.19.0
*/
string make_base64_url_string(string str) [flags=CONSTANT] {
    QoreStringNode* rv = new QoreStringNode;
    rv->concatBase64Url(*str);
    return rv;
}

//! Returns a base64-url-encoded representation of a binary object
/** Just like @ref make_base64_string() but with \c "+" and \c "/" subject to URL percent encoding

    @param bin the data to encode

    @return the base64-encoded string of the data passed

    @par Example:
    @code{.py}
string base64 = make_base64_url_string(data);
    @endcode

    @see
    - <binary>::toBase64Url()

    @since %Qore 1.19.0
*/
string make_base64_url_string(binary bin) [flags=CONSTANT] {
    QoreStringNode* rv = new QoreStringNode;
    rv->concatBase64Url(*bin);
    return rv;
}

//! Parses a base64 encoded string and returns a binary object of the decoded data
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the base64-encoded input data to decode

    @return a binary object of the decoded data

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @see
    - parse_hex_string()
    - parse_base64_string_to_string()

    @deprecated use parse_base64_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
binary parseBase64String(string str) [flags=RET_VALUE_ONLY,DEPRECATED] {
   return str->parseBase64(xsink);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing parseBase64String() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Parses a base64 encoded string and returns a binary object of the decoded data
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the base64-encoded input data to decode

    @return a binary object of the decoded data

    @par Example:
    @code{.py}
binary bin = parse_base64_string(base64_string);
    @endcode

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @see
    - parse_hex_string()
    - parse_base64_string_to_string()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case parseBase64String()
*/
binary parse_base64_string(string str) [flags=RET_VALUE_ONLY] {
   return str->parseBase64(xsink);
}

//! Parses a base64 encoded string and returns a string of the decoded data
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the base64-encoded input data to decode
    @param encoding the character encoding tag for the string return value; if not present, the @ref default_encoding "default character encoding" is assumed.

    @return a string of the decoded data tagged with the given encoding

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @since the encoding parameter was added in %Qore 0.8.4

    @see
    - parse_hex_string()
    - parse_base64_string()

    @deprecated use parse_base64_string_to_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string parseBase64StringToString(string str, *string encoding) [flags=RET_VALUE_ONLY,DEPRECATED] {
   const QoreEncoding *qe = encoding ? QEM.findCreate(encoding) : QCS_DEFAULT;
   return str->parseBase64ToString(qe, xsink);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing parseBase64StringToString() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Parses a base64 encoded string and returns a string of the decoded data
/** Implementation based on <a href="http://www.ietf.org/rfc/rfc1421.txt">RFC-1421</a> and <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC-2045</a>

    @param str the base64-encoded input data to decode
    @param encoding the character encoding tag for the string return value; if not present, the @ref default_encoding "default character encoding" is assumed.

    @return a string of the decoded data tagged with the given encoding

    @par Example:
    @code{.py}
string str = parse_base64_string_to_string(base64_string);
    @endcode

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @see
    - parse_hex_string()
    - parse_base64_string_to_string()

    @since
    - the encoding parameter was added in %Qore 0.8.4
    - %Qore 0.8.12 as a replacement for deprecated camel-case parseBase64StringToString()
*/
string parse_base64_string_to_string(string str, *string encoding) [flags=RET_VALUE_ONLY] {
   const QoreEncoding *qe = encoding ? QEM.findCreate(encoding) : QCS_DEFAULT;
   return str->parseBase64ToString(qe, xsink);
}

//! Parses a base64-url-encoded string and returns a binary object of the decoded data
/** Base64-URL encoding is defined by <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC-4648</a>

    @param str the base64-url-encoded input data to decode

    @return a binary object of the decoded data

    @par Example:
    @code{.py}
binary bin = parse_base64_url_string(base64_string);
    @endcode

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @see
    - parse_base64_string()

    @since %Qore 2.0
*/
binary parse_base64_url_string(string str) [flags=RET_VALUE_ONLY] {
   return str->parseBase64Url(xsink);
}

//! Parses a base64-url-encoded string and returns a string of the decoded data
/** Base64-URL encoding is defined by <a href="https://datatracker.ietf.org/doc/html/rfc4648">RFC-4648</a>

    @param str the base64-url-encoded input data to decode
    @param encoding the character encoding tag for the string return value; if not present, the @ref default_encoding "default character encoding" is assumed.

    @return a string of the decoded data tagged with the given encoding

    @par Example:
    @code{.py}
string str = parse_base64_url_string_to_string(base64_string);
    @endcode

    @throw BASE64-PARSE-ERROR A syntax error occurred parsing the base64 string (invalid character, etc)

    @see
    - parse_base64_string_to_string()

    @since %Qore 2.0
*/
string parse_base64_url_string_to_string(string str, *string encoding) [flags=RET_VALUE_ONLY] {
   const QoreEncoding *qe = encoding ? QEM.findCreate(encoding) : QCS_DEFAULT;
   return str->parseBase64UrlToString(qe, xsink);
}

//! Returns a list of hashes describing the currently-loaded %Qore modules
/** @return a list of hashes describing the currently-loaded %Qore modules; each element in the list is a hash with the following keys:
    - \c filename: the path to the module
    - \c name: the name of the module
    - \c desc: the description of the module
    - \c version: the version of the module
    - \c author: the author of the module
    - \c api_major: the major number of the %Qore module API version the module support
    - \c api_minor: the minor number of the %Qore module API version the module support
    - \c url: the module's URL
    - \c license: the module's license

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @see get_module_hash()

    @deprecated use get_module_list(); camel-case function names were deprecated in %Qore 0.8.12
*/
list<hash<auto>> getModuleList() [flags=DEPRECATED] {
   return MM.getModuleList();
}

//! Returns a list of hashes describing the currently-loaded %Qore modules
/** @return a list of hashes describing the currently-loaded %Qore modules; each element in the list is a hash with the following keys:
    - \c filename: the path to the module
    - \c name: the name of the module
    - \c desc: the description of the module
    - \c version: the version of the module
    - \c author: the author of the module
    - \c api_major: the major number of the %Qore module API version the module support
    - \c api_minor: the minor number of the %Qore module API version the module support
    - \c url: the module's URL
    - \c license: the module's license

    @par Example:
    @code{.py}
list<hash<auto>> l = get_module_list();
    @endcode

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @see get_module_hash()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getModuleList()
*/
list<hash<auto>> get_module_list() {
   return MM.getModuleList();
}

//! Returns a hash of hashes describing the currently-loaded %Qore modules; the top-level hash keys are the module names
/** @return a hash of hashes describing the currently-loaded %Qore modules; the top-level hash keys are the module names; the values are hashes with the following keys:
    - \c filename: the path to the module
    - \c name: the name of the module
    - \c desc: the description of the module
    - \c version: the version of the module
    - \c author: the author of the module
    - \c api_major: the major number of the %Qore module API version the module support
    - \c api_minor: the minor number of the %Qore module API version the module support
    - \c url: the module's URL
    - \c license: the module's license

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @since %Qore 0.8.1

    @deprecated use get_module_hash(); camel-case function names were deprecated in %Qore 0.8.12
*/
hash<string, hash<auto>> getModuleHash() [flags=DEPRECATED] {
   return MM.getModuleHash();
}

//! Returns a hash of hashes describing the currently-loaded %Qore modules; the top-level hash keys are the module names
/** @return a hash of hashes describing the currently-loaded %Qore modules; the top-level hash keys are the module names; the values are hashes with the following keys:
    - \c filename: the path to the module
    - \c name: the name of the module
    - \c desc: the description of the module
    - \c version: the version of the module
    - \c author: the author of the module
    - \c api_major: the major number of the %Qore module API version the module support
    - \c api_minor: the minor number of the %Qore module API version the module support
    - \c url: the module's URL
    - \c license: the module's license

    @par Example:
    @code{.py}
hash<string, hash<auto>> mh = get_module_hash();
    @endcode

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getModuleHash()
*/
hash<string, hash<auto>> get_module_hash() {
   return MM.getModuleHash();
}

//! Returns a list of strings of the builtin and module-supplied features of Qore
/** @return a list of strings of the builtin and module-supplied features of Qore

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @deprecated use get_feature_list(); camel-case function names were deprecated in %Qore 0.8.12
*/
list<string> getFeatureList() [flags=DEPRECATED] {
   return getProgram()->getFeatureList();
}

//! Returns a list of strings of the builtin and module-supplied features of Qore
/** @return a list of strings of the builtin and module-supplied features of Qore

    @par Example:
    @code{.py}
list<string> l = get_feature_list();
    @endcode

    @note this function is not flagged with @ref CONSTANT since its value could change at runtime

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getFeatureList()
*/
list<string> get_feature_list() {
   return getProgram()->getFeatureList();
}

//! Returns a list of all the values in the hash argument passed
/** @param h a hash to get all the values of

    @return a list of all the values in the hash argument passed

    @par Example:
    @code{.py}
list<auto> vl = hash_values(vl);
    @endcode

    @note equivalent to <hash>::values()

    @see <hash>::keys()
*/
list<auto> hash_values(hash<auto> h) [flags=CONSTANT] {
    return qore_hash_private::get(*h)->getValues();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing hash_values() [flags=RUNTIME_NOOP] {
}

//! Returns @ref True if the given key exists in the hash (does not necessarily have to have a value assigned); exceptions are only raised if string encoding errors are encountered
/**
    @param h the hash to check
    @param key the key name to check; this value will be converted to the @ref default_encoding "default character encoding" to check the hash

    @return @ref True if the given key exists in the hash (does not necessarily have to have a value assigned), @ref False if the key is not present at all

    @par Example:
    @code{.py}
bool b = has_key(hash, "key");
    @endcode

    @note no exception is thrown when checking an invalid key in a @ref hashdecl "hashdecl"-derived hash

    @throw ENCODING-CONVERSION-ERROR this error is thrown if the given key cannot be converted to the @ref default_encoding "default character encoding"
*/
bool has_key(hash<auto> h, string key) {
    TempEncodingHelper tmp(key, QCS_DEFAULT, xsink);
    if (*xsink)
        return QoreValue();
    bool exists;
    h->getKeyValueExistence(tmp->c_str(), exists);
    return exists;
}

//! Returns @ref True if the given key exists in the object (does not necessarily have to have a value assigned); exceptions are only raised if string encoding errors are encountered or in case of object access errors.
/**
    @par Example:
    @code{.py}
bool b = has_key(obj, "key");
    @endcode

    @throw ENCODING-CONVERSION-ERROR this error is thrown if the given key cannot be converted to the @ref default_encoding "default character encoding"

    @note object access exceptions could also be raised
*/
bool has_key(object obj, string key) {
    TempEncodingHelper tmp(key, QCS_DEFAULT, xsink);
    if (*xsink)
        return QoreValue();
    return obj->hasMember(tmp->c_str(), xsink);
}

//! Returns the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/**
    @param str the string data to process
    @param offset the byte offset of the data to retrieve (the first value is at offset 0)

    @return the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int byte = get_byte(str, 2); # returns the third byte of the string
    @endcode

    @note that for strings this function is not equivalent to the @ref list_element_operator "[] operator" for multi-byte character encodings, as this function works purely on bytes and the @ref list_element_operator "[] operator" operates on characters\n\n
    Furthermore this function is identical to getByte(), except this function has no @ref RUNTIME_NOOP variant
*/
*int get_byte(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= size || offset < 0)
      return QoreValue();

   return ptr[offset];
}

//! Returns the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/**
    @param b the data to process
    @param offset the byte offset of the data to retrieve (the first value is at offset 0)

    @return the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int byte = get_byte(bin, 2); # returns the third byte of the binary object
    @endcode

    @note this function is equivalent to the more efficient @ref list_element_operator "[] operator" when used with a binary argument\n\n
    Furthermore this function is identical to getByte(), except this function has no @ref RUNTIME_NOOP variant
*/
*int get_byte(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= size || offset < 0)
      return QoreValue();

   return ptr[offset];
}

//! Returns the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/**
    @param str the string data to process
    @param offset the byte offset of the data to retrieve (the first value is at offset 0)

    @return the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @note that for strings this function is not equivalent to the @ref list_element_operator "[] operator" for multi-byte character encodings, as this function works purely on bytes and the @ref list_element_operator "[] operator" operates on characters\n\n
    Furthermore this function is identical to get_byte(), except this function has a @ref RUNTIME_NOOP variant

    @deprecated since %Qore 0.8.4 marked as deprecated; use the get_byte() function instead
*/
*int getByte(string str, softint offset = 0) [flags=CONSTANT,DEPRECATED] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= size || offset < 0)
      return QoreValue();

   return ptr[offset];
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getByte() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/**
    @param b the data to process
    @param offset the byte offset of the data to retrieve (the first value is at offset 0)

    @return the byte value at the given byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @note this function is equivalent to the more efficient @ref list_element_operator "[] operator" when used with a binary argument\n\n
    Furthermore this function is identical to get_byte(), except this function has a @ref RUNTIME_NOOP variant

    @deprecated %Qore 0.8.4 marked as deprecated; use the get_byte() function instead
*/
*int getByte(binary b, softint offset = 0) [flags=CONSTANT,DEPRECATED] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= size || offset < 0)
      return QoreValue();

   return ptr[offset];
}

//! Returns the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 2-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_16(str, 2); # returns the third 2-byte (16-bit) value in the string
    @endcode

    @see get_word_16_lsb()
*/
*int get_word_16(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 1) || offset < 0)
      return QoreValue();

   return ntohs(*((unsigned short*)&ptr[offset]));
}

//! Returns the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 2-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_16(bin, 2); # returns the third 2-byte (16-bit) value in the binary object
    @endcode

    @see get_word_16_lsb()
*/
*int get_word_16(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 1) || offset < 0)
      return QoreValue();

   return ntohs(*((unsigned short*)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_32(str, 4); # returns the third 4-byte (32-bit) value in the string
    @endcode

    @note This function is identical to getWord32(), except this function has no @ref RUNTIME_NOOP variant

    @see get_word_32_lsb()
*/
*int get_word_32(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return ntohl(*((unsigned int*)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_32(bin, 2); # returns the third 4-byte (32-bit) value in the binary object
    @endcode

    @note This function is identical to getWord32(), except this function has no @ref RUNTIME_NOOP variant

    @see get_word_32_lsb()
*/
*int get_word_32(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return ntohl(*((unsigned int*)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @note This function is identical to get_word_32(), except this function has a @ref RUNTIME_NOOP variant

    @deprecated %Qore 0.8.4 marked as deprecated; use the get_word_32() function instead
*/
*int getWord32(string str, softint offset = 0) [flags=CONSTANT,DEPRECATED] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return ntohl(*((unsigned int *)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @note This function is identical to get_word_32(), except this function has a @ref RUNTIME_NOOP variant

    @deprecated %Qore 0.8.4 marked as deprecated; use the get_word_32() function instead
*/
*int getWord32(binary b, softint offset = 0) [flags=CONSTANT,DEPRECATED] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return (int64)ntohl(*((unsigned int *)&ptr[offset]));
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getWord32() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 8-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_64(str, 4); # returns the third 8-byte (64-bit) value in the string
    @endcode

    @see get_word_64_lsb()
*/
*int get_word_64(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 7) || offset < 0)
      return QoreValue();

   return (int64)MSBi8(*((int64 *)&ptr[offset]));
}

//! Returns the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Most_significant_bit">MSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 8-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_64(bin, 2); # returns the third 8-byte (64-bit) value in the binary object
    @endcode

    @see get_word_64_lsb()
*/
*int get_word_64(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 7) || offset < 0)
      return QoreValue();

   return (int64)MSBi8(*((int64 *)&ptr[offset]));
}

//! Returns the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 2-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_16_lsb(str, 2); # returns the third 2-byte (16-bit) value in the string
    @endcode

    @see get_word_16()
*/
*int get_word_16_lsb(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 1) || offset < 0)
      return QoreValue();

   return (int64)LSBi2(*((unsigned short *)&ptr[offset]));
}

//! Returns the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 2-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 16-bit integer value at the given 2-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_16_lsb(bin, 2); # returns the third 2-byte (16-bit) value in the binary object
    @endcode

    @see get_word_16()
*/
*int get_word_16_lsb(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 1) || offset < 0)
      return QoreValue();

   return (int64)LSBi2(*((unsigned short *)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_32_lsb(str, 4); # returns the third 4-byte (32-bit) value in the string
    @endcode

    @note This function is identical to getWord32(), except this function has no @ref RUNTIME_NOOP variant

    @see get_word_32()
*/
*int get_word_32_lsb(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return (int64)LSBi4(*((unsigned int *)&ptr[offset]));
}

//! Returns the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 4-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 32-bit integer value at the given 4-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_32_lsb(bin, 2); # returns the third 4-byte (32-bit) value in the binary object
    @endcode

    @see get_word_32()
*/
*int get_word_32_lsb(binary b, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )b->getPtr();
   int size = b->size();

   if (offset >= (size - 3) || offset < 0)
      return QoreValue();

   return (int64)LSBi4(*((unsigned int *)&ptr[offset]));
}

//! Returns the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param str the string data to process
    @param offset the 8-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_64_lsb(str, 4); # returns the third 8-byte (64-bit) value in the string
    @endcode

    @see get_word_64()
*/
*int get_word_64_lsb(string str, softint offset = 0) [flags=CONSTANT] {
   unsigned char* ptr = (unsigned char* )str->c_str();
   int size = str->strlen();

   if (offset >= (size - 7) || offset < 0)
      return QoreValue();

   return (int64)LSBi8(*((int64 *)&ptr[offset]));
}

//! Returns the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data
/** This function assumes <a href="http://en.wikipedia.org/wiki/Least_significant_bit">LSB byte order</a> when retrieving the value from the data

    @param b the data to process
    @param offset the 8-byte offset of the data to retrieve (the first value is at offset 0)

    @return the 64-bit integer value at the given 8-byte offset (the first value is at offset 0) or @ref nothing if the offset is not legal for the given data

    @par Example:
    @code{.py}
*int val = get_word_64_lsb(bin, 2); # returns the third 8-byte (64-bit) value in the binary object
    @endcode

    @see get_word_64()
*/
*int get_word_64_lsb(binary b, softint offset = 0) [flags=CONSTANT] {
    unsigned char* ptr = (unsigned char* )b->getPtr();
    int size = b->size();

    if (offset >= (size - 7) || offset < 0)
        return QoreValue();

    return (int64)LSBi8(*((int64 *)&ptr[offset]));
}

//! This function always returns an empty string \c ""
/** This function variant is included for backwards-compatibility

    @param str no action is taken on the argument

    @return always returns an empty string \c ""
*/
string splice(string[doc] str) [flags=NOOP] {
    return new QoreStringNode;
}

//! Returns a string based on the argument string but with characters removed from a certain character index
/** An exception can only be thrown here if an invalid multi-byte encoding is found

    @param str the string to process
    @param start the character index (where the first character is 0) to start removing characters; if this value is negative, it gives the offset from the end of the string

    @return the processed string

    @par Example:
    @code{.py}
string str = splice(str2, 5);
    @endcode

    @see the @ref splice "splice operator"
*/
string splice(string str, softint start) {
    QoreStringNodeHolder pstr(str->copy());
    pstr->splice(start, xsink);
    return *xsink ? QoreValue() : pstr.release();
}

//! Returns a string based on the argument string but optionally with characters removed and/or added from a certain character index
/** An exception can only be thrown here if an invalid multi-byte encoding is found

    @param str the string to process
    @param start the character index (where the first character is 0) to start removing (and/or adding) characters; if this value is negative, it gives the offset from the end of the string
    @param len the number of characters to remove; if this argument is 0 then no characters are removed; if this value is negative, then it gives an offset from the end of the string (ie -2 means remove all characters up to but not including the two last characters)
    @param nstr the optional string for inserting new characters

    @return the processed string with characters removed and/or added according to the arguments

    @par Example:
    @code{.py}
string str = splice(str2, 5, 7, "hello");
    @endcode

    @see the @ref splice "splice operator"
*/
string splice(string str, softint start, softint len, *string nstr) {
    QoreStringNodeHolder pstr(str->copy());
    pstr->splice(start, len, QoreValue(nstr), xsink);
    return *xsink ? QoreValue() : pstr.release();
}

//! Returns a list based on the argument list but with elements removed from the given index to the end of the list
/** Exceptions can only be thrown here if objects are removed from the list and this causes them to go out of scope and an exception is thrown in one of the destructors

    @param l the list to process
    @param start the starting element (where the first element is 0) to start removing elements; if this value is negative, it gives the offset from the end of the list

    @par Example:
    @code{.py}
list<auto> l = splice(l2, 5);
    @endcode

    @see the @ref splice "splice operator"
*/
list<auto> splice(list<auto> l, softint start) {
    ReferenceHolder<QoreListNode> lst(l->copy(), xsink);
    ReferenceHolder<> holder(lst->splice(start), xsink);
    return *xsink ? QoreValue() : lst.release();
}

//! Returns a list based on the argument list but optionally with elements removed and/or added from a certain index
/** Exceptions can only be thrown here if objects are removed from the list and this causes them to go out of scope and an exception is thrown in one of the destructors

    @param l the list to process
    @param start the starting element (where the first element is 0) to start removing (and/or adding) elements; if this value is negative, it gives the offset from the end of the list
    @param len the number of list elements to remove; if this argument is 0 then no elements are removed; if this value is negative, then it gives an offset from the end of the list (ie -2 means remove all elements up to but not including the two last elements)

    @return the processed list with elements removed and/or added according to the arguments

    @par Example:
    @code{.py}
list<auto> l = splice(l2, 5, 7);
    @endcode

    @see the @ref splice "splice operator"
*/
list<auto> splice(list<auto> l, softint start, softint len) {
    ReferenceHolder<QoreListNode> lst(l->copy(), xsink);
    ReferenceHolder<> holder(lst->splice(start, len), xsink);
    return *xsink ? QoreValue() : lst.release();
}

//! Returns a list based on the argument list but optionally with elements removed and/or added from a certain index
/** Exceptions can only be thrown here if objects are removed from the list and this causes them to go out of scope and an exception is thrown in one of the destructors

    @param l the list to process
    @param start the starting element (where the first element is 0) to start removing (and/or adding) elements; if this value is negative, it gives the offset from the end of the list
    @param len the number of list elements to remove; if this argument is 0 then no elements are removed; if this value is negative, then it gives an offset from the end of the list (ie -2 means remove all elements up to but not including the two last elements)
    @param nlist the new  list for inserting new elements

    @return the processed list with elements removed and/or added according to the arguments

    @par Example:
    @code{.py}
list<auto> l = splice(l2, 5, 7, "hello");
    @endcode

    @see the @ref splice "splice operator"
*/
list<auto> splice(list<auto> l, softint start, softint len, softlist<auto> nlist) {
    ReferenceHolder<QoreListNode> lst(l->copy(), xsink);
    ReferenceHolder<> holder(lst->splice(start, len, nlist, xsink), xsink);
    return *xsink ? QoreValue() : lst.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing splice() [flags=RUNTIME_NOOP] {
}

//! Returns a hex-encoded representation of a string
/** @param str the string to encode

    @return a string of hex digits repsenting the bytes of the argument passed

    @see
    - <string>::toHex()
    - make_base64_string(string, softint)
    - parse_hex_string()

    @deprecated use make_hex_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string makeHexString(string str) [flags=CONSTANT,DEPRECATED] {
    QoreStringNode* rv = new QoreStringNode;
    rv->concatHex(str);
    return rv;
}

//! Returns a hex-encoded representation of a binary object
/** @param bin the binary object to encode

    @return a string of hex digits repsenting the bytes of the argument passed

    @see
    - <binary>::toHex()
    - make_base64_string(string, softint)
    - parse_hex_string()

    @deprecated use make_hex_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string makeHexString(binary bin) [flags=CONSTANT,DEPRECATED] {
    QoreStringNode* str = new QoreStringNode;
    str->concatHex(bin);
    return str;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing makeHexString() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns a hex-encoded representation of a string
/** @param str the string to encode

    @return a string of hex digits repsenting the bytes of the argument passed

    @par Example:
    @code{.py}
string str = make_hex_string(str2);
    @endcode

    @see
    - <binary>::toHex()
    - make_base64_string(binary, softint)
    - parse_hex_string()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case makeHexString()
*/
string make_hex_string(string str) [flags=CONSTANT] {
    QoreStringNode* rv = new QoreStringNode;
    rv->concatHex(str);
    return rv;
}

//! Returns a hex-encoded representation of a binary object
/** @param bin the binary object to encode

    @return a string of hex digits repsenting the bytes of the argument passed

    @par Example:
    @code{.py}
string str = make_hex_string(bin);
    @endcode

    @see
    - <binary>::toHex()
    - make_base64_string(binary, softint)
    - parse_hex_string()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case makeHexString()
*/
string make_hex_string(binary bin) [flags=CONSTANT] {
    QoreStringNode* str = new QoreStringNode;
    str->concatHex(bin);
    return str;
}

//! Parses a hex-encoded string and returns the binary object
/** @param hexstr a string of an even-number of only hexadecimal digits

    @return a binary object of the decoded data

    @throw PARSE-HEX-ERROR A syntax error occurred parsing the hex string (odd number of digits, invalid hex character, etc)

    @see
    - parse_base64_string()
    - make_hex_string()

    @deprecated use parse_hex_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
binary parseHexString(string hexstr) [flags=RET_VALUE_ONLY,DEPRECATED] {
   return hexstr->parseHex(xsink);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing parseHexString() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Parses a hex-encoded string and returns the binary object
/** @param hexstr a string of an even-number of only hexadecimal digits

    @return a binary object of the decoded data

    @par Example:
    @code{.py}
binary bin = parseHexString(hex_string);
    @endcode

    @throw PARSE-HEX-ERROR A syntax error occurred parsing the hex string (odd number of digits, invalid hex character, etc)

    @see
    - parse_base64_string()
    - make_hex_string()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case parseHexString()
*/
binary parse_hex_string(string hexstr) [flags=RET_VALUE_ONLY] {
   return hexstr->parseHex(xsink);
}

//! Returns an integer for a hexadecimal string value; throws an exception if non-hex digits are found
/** @param str a string of hexadecimal digits (like \c "6d4f84e0"; with or without leading \c "x" or \c "0x")

    @return the base-10 integer corresponding to the argument

    @par Example:
    @code{.py}
int i = hextoint("ab3d4e0f12");
    @endcode

    @throw PARSE-HEX-ERROR invalid hex digit found
*/
int hextoint(string str) {
   if (!str->strlen())
      return 0;

   int64 rc = 0;
   int64 pow = 0;
   const char* buf = str->c_str();
   size_t len = str->size();
   if (*buf == '0' && *(buf + 1) == 'x') {
      buf += 2;
      len -= 2;
   }
   else if (*buf == 'x') {
      buf++;
      --len;
   }
   for (const char* p = buf + len - 1; p >= buf; p--) {
      int n = get_nibble(*p, xsink);
      if (xsink->isException())
	 return 0;
      if (!pow) {
	 rc = n;
	 pow = 16;
      }
      else {
	 rc += n * pow;
	 pow *= 16;
      }
   }
   return rc;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing hextoint() [flags=RUNTIME_NOOP] {
}

//! parses a string representing a number in a configurable base and returns the integer
/** @param num a string representing a number
    @param base the base of the number

    @return the integer represented by the string and the base

    @par Example:
    @code{.py}
int i = strtoint("41", 8);
    @endcode

    @throw STRTOINT-ERROR cannot parse string; unsupported base, etc
*/
int strtoint(string num, softint base = 10) {
   errno = 0;
   int64 rv = strtoll(num->c_str(), 0, base);
   if (errno == EINVAL || errno == ERANGE)
      xsink->raiseException("STRTOINT-ERROR", "could not parse '%s' to an integer", num->c_str());
   return rv;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing strtoint() [flags=RUNTIME_NOOP] {
}

//! Loads in a %Qore module at run-time
/** If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded with the @ref requires "%requires directive" instead, unless all references to the objects provided by the module will be made in code embedded in child Program objects.

    @param name either a feature name (a module will be searched with this feature name) or a path to a module to load
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors

    @par Example:
    @code{.py}
load_module("mysql");
    @endcode

    @throw LOAD-MODULE-ERROR module cannot be loaded: API incompatibility, module defines symbols already defined in the target object, etc

    @see
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.8.7 this function can also be used in user module code
    - %Qore 0.9 added the \c warning_mask parameter
*/
nothing load_module(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), getProgram(), nullptr, QMLO_NONE, warning_mask);
}

//! Loads in a %Qore module at run-time
/** If a feature with the same name already exists, then this feature's code is imported into the current Program object if necessary and no further action is taken.

    Note that modules providing objects resolved at parse time (classes, constants, functions, etc) must be loaded with the @ref requires "%requires directive" instead, unless all references to the objects provided by the module will be made in code embedded in child Program objects.

    @param name either a feature name (a module will be searched with this feature name) or a path to a module to load
    @param warning_mask the warning mask to use when loading the module; note that warnings are treated as errors

    @par Example:
    @code{.py}
load_module("mysql");
    @endcode

    @throw LOAD-MODULE-ERROR module cannot be loaded: API incompatibility, module defines symbols already defined in the target object, etc

    @see
    - get_module_hash()
    - get_feature_list()

    @since
    - %Qore 0.8.7 this function can also be used in user module code
    - %Qore 0.9 added the \c warning_mask parameter
*/
*hash<ExceptionInfo> load_module_warn(string name, int warning_mask = WARN_MODULES) [dom=MODULES] {
    ExceptionSink wsink;
    QMM.runTimeLoadModule(*xsink, wsink, name->c_str(), getProgram(), nullptr, QMLO_NONE, warning_mask);
    if (!*xsink && wsink) {
        return wsink.catchException()->makeExceptionObjectAndDelete(xsink);
    }
    return QoreValue();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing load_module() [flags=RUNTIME_NOOP;dom=MODULES] {
}

//! Loads in a %Qore user module at run-time with using the given @ref Qore::Program "Program" object as the container for the user module code
/** This function allows a user module to be loaded with a custom API already present in the user module's @ref Qore::Program "Program" container

    @par Example:
    @code{.py}
load_user_module_with_program("MyModule", p);
    @endcode

    @param name the name or path of the user module to load
    @param pgm the @ref Qore::Program "Program" object to use as a container for the new user module, presumably this has a custom API that the user module can use; note that after this call the @ref Qore::Program "Program" object will be owned by the user module, therefore the object itself will no longer be valid and any accesses to the object after this call will result in an exception

    @throw LOAD-MODULE-ERROR module cannot be loaded: binary modules cannot be loaded in @ref Qore::Program "Program" containers, module defines symbols already defined in the target object, etc

    @since %Qore 0.8.12
 */
nothing load_user_module_with_program(string name, !Qore::Program[QoreProgram] pgm) [dom=MODULES] {
    QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), getProgram(), pgm);
}

//! Reloads an already-loaded %Qore module subject to code injection at run-time into %Qore; the module's code is not imported into the current Program object
/**
    @param name a feature name for an already-loaded module

    @par Example:
    @code{.py}
reload_module("FixedLengthUtil");
    @endcode

    @throw LOAD-MODULE-ERROR module cannot be loaded: API incompatibility, etc

    @note
    - unlike load_module(), this function does not cause the given module's code to be loaded in the current Program object
    - in case the given feature has not been loaded or has already been loaded and was not subject to code injection, this call is silently ignored

    @see
    - load_module()

    @since %Qore 0.8.12
*/
nothing reload_module(string name) [dom=MODULES,INJECTION] {
    QMM.runTimeLoadModule(*xsink, *xsink, name->c_str(), 0, 0, QMLO_RELOAD);
}

//! Decodes percent numeric codes in a URL string and returns the decoded string in UTF-8 encoding
/** @param url a URL string with percent-encodings to decode

    @return the URL string in UTF-8 encoding with all percent-encodings decoded

    @par Example:
    @code{.py}
string decoded_url = decode_url(encoded_url);
    @endcode

    @note percent decoding is made according to <a href="http://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 2.1</a>

    @see
    - encode_url()
    - encode_uri_request()
    - decode_uri_request() for a similar function that provides AJAX-compatible URI decoding for the URI query component
*/
string decode_url(string url) [flags=CONSTANT] {
   QoreStringNodeHolder str(new QoreStringNode(QCS_UTF8));
   str->concatDecodeUrl(*url, xsink);
   return *xsink ? QoreValue() : str.release();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing decode_url() [flags=RUNTIME_NOOP] {
}

//! Encodes URLs by substituting \c '%%' characters with \c '%25', spaces (\c ' ') with \c '%20', and non-ascii characters by percent-encoded representations
/** @param url a URL string to encode
    @param encode_all if @ref True "True", then in addition to \c '%%', spaces (\c ' '), and non-ascii characters the following reserved characters (according to <a href="http://tools.ietf.org/html/rfc3986">RFC 3986</a> are also encoded: \c '!', \c '*', \c '\'', \c '(', \c ')', \c ';', \c ':', \c '@', \c '&', \c '=', \c '+', \c '$', \c ',', \c '/', \c '?', \c '#', \c '[', \c ']'

    @return the URL string with encoded according to the arguments

    @par Example:
    @code{.py}
string encoded_url = encode_url(url);
    @endcode

    @see
    - decode_url()
    - encode_uri_request() for a similar function that provides AJAX-compatible URI encoding for the URI query component
    - decode_uri_request()

    @since %Qore 0.8.9
*/
string encode_url(string url, softbool encode_all = False) [flags=CONSTANT] {
   QoreStringNodeHolder str(new QoreStringNode(url->getEncoding()));
   str->concatEncodeUrl(xsink, url, encode_all);
   return *xsink ? QoreValue() : str.release();
}

//! Decodes percent-encoded codes in a URI path and converts \c "+" signs in the query component to spaces and returns the decoded string in UTF-8 encoding
/** @par Example:
    @code{.py}
string decoded_uri = decode_uri_request("path/to/something%20else?query=1%2b1+%2b2");
# results in: "path/to/something else?query=1+1 +2"
    @endcode

    @param uri a URI request string to decode

    @return the decoded URI request string in UTF-8 encoding

    The URI string is divided into three parts:
    - the path, which is the first part of the URI string up until any \c "?" or \c "#" character
    - the query, which is any string following a \c "?" character terminating the path and up to a possible \c "#" character
    - the fragment, which is any string following a \c "#" character

    For all parts, any percent-encoded codes (<a href="http://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 2.1</a>) are decoded to the corresponinding character.  The path component is identified and distinguished by a \c "?" or a \c "#" character.  The path component is subjected to standard percent encoding.  The query component is any part of the URI string following a \c "?" character up to the end of the URI string or until a \c "#" characer is found.  The query component is subjected to special decoding for AJAX compatibility; \c "+" characters are decoded to ASCII space (therefore any \c "+" charaters in the query component of the URI must be encoded with percent encoding, such is the case with @ref Qore::encode_uri_request() for example).  The fragment component is identified as any text following a \c "#" character, and this is decoded with the same rules as in the path component.

    @note the difference between this function and @ref Qore::decode_url() is that this function employs special logic in the decoding of the different components of the URI string for compatibility with common AJAX libraries that encode ASCII spaces as \c "+" in the query component of the URI string.

    @see
    - encode_uri_request()
    - encode_url()
    - decode_url()

    @since %Qore 0.8.12
*/
string decode_uri_request(string uri) [flags=CONSTANT] {
   QoreStringNodeHolder str(new QoreStringNode(QCS_UTF8));
   str->concatDecodeUriRequest(*uri, xsink);
   return *xsink ? QoreValue() : str.release();
}

//! Encodes URI requests by substituting special characters in the path with percent-encoded equivalents and substituting spaces with \c "+" and \c "+" with the percent-encoded equivalent in the URI query component
/** @par Example:
    @code{.py}
string encoded_uri = encode_uri_request("path/to/something else?query=1+1 +2")'
# results in: "path/to/something%20else?query=1%2b1+%2b2"
    @endcode

    @param url a URL string to encode

    @return the URI request string with encoded according to the arguments

    The URI string is divided into three parts:
    - the path, which is the first part of the URI string up until any \c "?" or \c "#" character
    - the query, which is any string following a \c "?" character terminating the path and up to a possible \c "#" character
    - the fragment, which is any string following a \c "#" character

    For all parts, any non-ASCII characters and the \c "%" character are subjected to percent encoding (<a href="http://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 2.1</a>).  The path component is identified and distinguished by a \c "?" or a \c "#" character.  Any spaces in the path component are also subjected to percent encoding.  The query component is any part of the URI string following a \c "?" character up to the end of the URI string or until a \c "#" characer is found.  Spaces in the query component are encoded with \c "+" for compatibility with AJAX libraries, and \c "+" signs are subjected to percent encoding.  The fragment component is identified as any text following a \c "#" character, and this is encoded with the same rules as in the path component.

    @note the difference between this function and @ref Qore::encode_url() is that this function employs special logic in the encoding of the different components of the URI string for compatibility with common AJAX libraries that encode ASCII spaces as \c "+" in the query component of the URI string

    @see
    - decode_uri_request()
    - decode_url()
    - encode_url()
    - <a href="http://tools.ietf.org/html/rfc3986">RFC 3986</a>
    - <a href="http://tools.ietf.org/html/rfc6874">RFC 6874</a>

    @since %Qore 0.8.12
*/
string encode_uri_request(string url) [flags=CONSTANT] {
   QoreStringNodeHolder str(new QoreStringNode(url->getEncoding()));
   str->concatEncodeUriRequest(xsink, url);
   return *xsink ? QoreValue() : str.release();
}

//! Returns the path (directory and filename) of the current script or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)
/** @return the path (directory and filename) of the current script or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)

    @par Example:
    @code{.py}
*string str = get_script_path();
    @endcode
*/
*string get_script_path() {
   return getProgram()->getScriptPath();
}

//! Returns the name of the directory from which the current script was executed or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)
/** @return the name of the directory from which the current script was executed or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)

    @par Example:
    @code{.py}
*string str = get_script_dir();
    @endcode
*/
*string get_script_dir() {
   return getProgram()->getScriptDir();
}

//! Returns the filename of the current script if known or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)
/** @return the filename of the current script if known or @ref nothing if unknown (i.e. no parent script, script read from stdin, etc)

    @par Example:
    @code{.py}
*string str = get_script_name();
    @endcode
*/
*string get_script_name() {
   return getProgram()->getScriptName();
}

//! Returns a list of hashes giving information about %Qore library options for the current build
/** @return a list of hashes giving information about %Qore library options for the current build; each hash will have the following keys:
    - \c option: The string description of the option
    - \c constant: A string giving the name of the constant that has the boolean value for this option
    - \c type: The type of option
    - \c value: The boolean value of the option

    @par Example:
    @code{.py}
list<hash<auto>> l = get_qore_option_list();
    @endcode

    @see get_qore_option_hash()
*/
list<hash<auto>> get_qore_option_list() [flags=CONSTANT] {
    QoreListNode* l = new QoreListNode(autoHashTypeInfo);

    for (unsigned j = 0; j < qore_option_list_size; ++j) {
        QoreHashNode* h = new QoreHashNode(autoTypeInfo);
        qore_hash_private* hh = qore_hash_private::get(*h);
        hh->setKeyValueIntern("option", new QoreStringNode(qore_option_list[j].option));
        hh->setKeyValueIntern("constant", new QoreStringNode(qore_option_list[j].constant));
        hh->setKeyValueIntern("type", new QoreStringNode(tlist[qore_option_list[j].type]));
        hh->setKeyValueIntern("value", qore_option_list[j].value);
        l->push(h, xsink);
    }
    return l;
}

//! Returns a hash of hashes giving information about %Qore library options for the current build
/** @return a hash of hashes giving information about %Qore library options for the current build; the hash keys are the constant names and the values are hashes with the following keys:
    - \c option: The string description of the option
    - \c constant: A string giving the name of the constant that has the boolean value for this option (equal to the hash key name)
    - \c type: The type of option
    - \c value: The boolean value of the option

    @par Example:
    @code{.py}
hash<string, hash<auto>> h = get_qore_option_hash();
    @endcode

    @see get_qore_option_list()

    @since %Qore 0.8.4
*/
hash<string, hash<auto>> get_qore_option_hash() [flags=CONSTANT] {
    QoreHashNode* rv = new QoreHashNode(autoHashTypeInfo);

    for (unsigned j = 0; j < qore_option_list_size; ++j) {
        QoreHashNode* h = new QoreHashNode(autoTypeInfo);
        h->setKeyValue("option", new QoreStringNode(qore_option_list[j].option), xsink);
        h->setKeyValue("constant", new QoreStringNode(qore_option_list[j].constant), xsink);
        h->setKeyValue("type", new QoreStringNode(tlist[qore_option_list[j].type]), xsink);
        h->setKeyValue("value", qore_option_list[j].value, xsink);

        rv->setKeyValue(qore_option_list[j].constant, h, 0);
    }
    return rv;
}

//! Returns a hash of library build and version info
/** @return a hash of library build and version info with the following keys:
    - \c PlatformOS: The operating system used to build the %Qore library
    - \c PlatformCPU: The CPU used as a target for the %Qore library build
    - \c VersionString: The full version string for this version of the %Qore library
    - \c VersionMajor: An integer giving the %Qore library's major version number
    - \c VersionMinor: An integer giving the %Qore library's minor version number
    - \c VersionSub: An integer giving the %Qore library's release version number
    - \c Build: An integer giving the %Qore library's subversion revision number
    - \c BuildHost: A string giving information about the host used to compile the %Qore library
    - \c Compiler: The compiler used to build the %Qore library
    - \c ModuleDir: The module directory assumed by default in the %Qore library
    - \c CFLAGS: The compiler flags used to compile the %Qore library
    - \c LDFLAGS: The linker flags used to link the %Qore library

    @par Example:
    @code{.py}
hash<auto> h = get_qore_library_info();
    @endcode
*/
hash<auto> get_qore_library_info() [flags=CONSTANT] {
   QoreHashNode* h = new QoreHashNode(autoTypeInfo);

   h->setKeyValue("PlatformOS", new QoreStringNode(qore_target_os), xsink);
   h->setKeyValue("PlatformCPU", new QoreStringNode(qore_target_arch), xsink);
   h->setKeyValue("VersionString", new QoreStringNode(qore_version_string), xsink);
   h->setKeyValue("VersionMajor", qore_version_major, xsink);
   h->setKeyValue("VersionMinor", qore_version_minor, xsink);
   h->setKeyValue("VersionSub", qore_version_sub, xsink);
   h->setKeyValue("Build", qore_build_number, xsink);

   h->setKeyValue("UserModuleVerDir", new QoreStringNode(qore_user_module_ver_dir), xsink);
   h->setKeyValue("UserModuleDir", new QoreStringNode(qore_user_module_dir), xsink);
   h->setKeyValue("BinaryModuleVerDir", new QoreStringNode(qore_module_ver_dir), xsink);
   h->setKeyValue("BinaryModuleDir", new QoreStringNode(qore_module_dir), xsink);
   h->setKeyValue("ModulePath", new QoreStringNodeMaker("%s:%s:%s:%s", qore_user_module_ver_dir, qore_user_module_dir, qore_module_ver_dir, qore_module_dir), xsink);

   h->setKeyValue("BuildHost", new QoreStringNode(qore_build_host), xsink);
   h->setKeyValue("Compiler", new QoreStringNode(qore_cplusplus_compiler), xsink);
   h->setKeyValue("CFLAGS", new QoreStringNode(qore_cflags), xsink);
   h->setKeyValue("LDFLAGS", new QoreStringNode(qore_ldflags), xsink);

   return h;
}

//! returns the current @ref parse_options "parse options" for the current @ref Qore::Program "Program" object
/** @par Example:
    @code{.py}
int i = get_parse_options();
    @endcode

    @return the current @ref parse_options "parse options" for the current @ref Qore::Program "Program" object

    @since %Qore 0.8.7
 */
int get_parse_options() [flags=CONSTANT] {
   return runtime_get_parse_options();
}

//! sets the return value for a Program object when running with @ref exec-class "%exec-class"
/** @par Example:
    @code{.py}
set_return_value(1);
    @endcode

    @param val the return value for the Program

    @throw SETRETURNVALUE-ERROR this exception is thrown if the Program is not currently running in @ref exec-class "%exec-class" mode

    @since %Qore 0.8.12
 */
set_return_value(auto val) [dom=PROCESS] {
    qore_program_private::setReturnValue(*getProgram(), val.refSelf(), xsink);
}

//! returns a descriptive string for an exception location; the \c source and \c offset information will also be included in the string returned if present in the @ref Qore::ExceptionInfo "ExceptionInfo" hash argument
/** @par Example:
    @code{.py}
try {
    throw "oops", sprintf("arg %y is invalid", arg);
} catch (hash<ExceptionSink> ex) {
    log("%s: %s: %s", get_ex_pos(ex), ex.err, ex.desc);
}
    @endcode

    @param ex a hash which should be an @ref Qore::ExceptionInfo "ExceptionInfo" hash

    @return a string describing the exception location (ex: \c "sftp-poller.q:140")

    @since %Qore 0.8.7
 */
string get_ex_pos(hash<auto> ex) [flags=CONSTANT] {
    QoreValue n = ex->getKeyValue("file");
    QoreStringNode* str = new QoreStringNode(n.getType() == NT_STRING
        ? n.get<const QoreStringNode>()->c_str()
        : "<unknown>");
    int ln = (int)ex->getKeyValue("line").getAsBigInt();
    str->sprintf(":%d", ln);
    bool openparen = false;
    n = ex->getKeyValue("lang");
    if (n.getType() == NT_STRING) {
        str->sprintf(" (%s", n.get<const QoreStringNode>()->c_str());
        openparen = true;
    }
    n = ex->getKeyValue("source");
    if (n.getType() == NT_STRING) {
        const QoreStringNode* sstr = n.get<const QoreStringNode>();
        if (!sstr->empty()) {
            int offset = (int)ex->getKeyValue("offset").getAsBigInt();
            str->concat(" ");
            if (!openparen) {
                str->concat('(');
                openparen = true;
            }
            str->sprintf("source \"%s\":%d", sstr->c_str(), ln + offset);
        }
    }
    if (openparen) {
        str->concat(')');
    }
    return str;
}

//! retrieves a hash of local variables for the given stack frame
/** @par Example:
    @code
hash<auto> h = get_local_vars(0);
    @endcode

    @param frame the stack frame where 0 is the current stack frame

    @return a hash of local variables for the given stack frame; if the stack frame is invalid, an empty hash is returned; hash keys are variable names; hash values are hashes with the following keys:
    - \c "type": can be either "local" or "closure" for closure-bound local variables
    - \c "value": the value of the variable

    @throw CIRCULAR-REFERENCE-ERROR circular reference found when retrieving local variable values

    @since %Qore 0.8.13
 */
hash<auto> get_local_vars(int frame) [dom=DEBUGGER;flags=RET_VALUE_ONLY] {
    return thread_get_local_vars(frame, xsink);
}

//! sets the value of the given local variable; if the variable cannot be found an exception is raised
/** @par Example:
    @code
set_local_var_value(0, "var", 1);
    @endcode

    @param frame the stack frame where 0 is the current or highest stack frame
    @param var the name of the local variable
    @param value the value to assign

    @throw UNKNOWN-VARIABLE the given local variable is not present in the current stack frame

    @note
    - other exceptions could be raised when making the assignment such as incompatible type exceptions
    - pure local variables (i.e. not closure bound and not subject to the reference operator) are not stored with type information at runtime; type information is only enforced at parse / compile time, therefore it's possible to set local variables with invalid values that contradict their declarations with this function

    @since %Qore 0.8.13
*/
nothing set_local_var_value(int frame, string var, auto value) [dom=DEBUGGER] {
   if (thread_set_local_var_value(frame, var->c_str(), value, xsink) == 1 && thread_set_closure_var_value(frame, var->c_str(), value, xsink) == 1 && !xsink->isEvent())
      xsink->raiseException("UNKNOWN-VARIABLE", "cannot find local variable '%s' in stack frame %d", var->c_str(), (int)frame);
}

//! returns a hash of global variables
/** @par Example:
    @code{.py}
hash<auto> h = get_global_vars();
    @endcode

    @return a hash of global variables and their values

    @see @ref Qore::Program::getGlobalVars()

    @since %Qore 0.8.13
 */
hash<auto> get_global_vars() [flags=CONSTANT] {
   return getProgram()->getGlobalVars();
}

//! set the value of a global variable
/** @par Example:
    @code
set_global_var_value("a", 1);
    @endcode

    @param name the name of the variable
    @param value the value to assign

    @throw UNKNOWN-VARIABLE the variable is not a global variable

    @note other exceptions could be thrown if the value cannot be assigned to the given variable

    @see @ref Qore::Program::setGlobalVarValue()

    @since %Qore 0.8.13
 */
nothing set_global_var_value(string name, auto value) {
   getProgram()->setGlobalVarValue(name->c_str(), value.refSelf(), xsink);
}

//! returns the given global module option
/** @par Example:
    @code
auto val = get_module_option(mod, opt);
    @endcode

    @param module the name of the module
    @param option the name of the option

    @return the value of the given module option

    @see set_module_option()

    @since %Qore 0.9
*/
auto get_module_option(string module, string option) [flags=CONSTANT;dom=PROCESS] {
    TempEncodingHelper mod_str(module, QCS_DEFAULT, xsink);
    if (*xsink) {
        return QoreValue();
    }
    TempEncodingHelper opt_str(option, QCS_DEFAULT, xsink);
    if (*xsink) {
        return QoreValue();
    }

    return qore_get_module_option(mod_str->c_str(), opt_str->c_str());
}

//! set the given module option
/** @par Example:
    @code
set_module_option(mod, opt, val);
    @endcode

    @param module the name of the module
    @param option the name of the option
    @param value the value to assign; setting to @ref nothing removes the module option

    @see get_module_option()

    @since %Qore 0.9
 */
nothing set_module_option(string module, string option, auto value) [dom=PROCESS] {
    TempEncodingHelper mod_str(module, QCS_DEFAULT, xsink);
    if (*xsink) {
        return QoreValue();
    }
    TempEncodingHelper opt_str(option, QCS_DEFAULT, xsink);
    if (*xsink) {
        return QoreValue();
    }

    qore_set_module_option(mod_str->c_str(), opt_str->c_str(), value.refSelf());
}

//! resolve the string as a call reference
/** @param identifier the string to resolve; function or static class method, can include namespace path

    @return the call reference to the given function or static method; the string is resolved as follows:
    - if in a class method, a non-static method lookup is made
    - if not found, then a static method lookup is made
    - if not found, then a function lookup is made
    - if not found, an \c CALL-REFERENCE-ERROR exception is thrown

    @throw CALL-REFERENCE-ERROR cannot resolve call reference; method not accessible in the calling context

    @note
    - use <object>::getCallReference() to get call references to non-public class methods; this builtin function
      will always run without any class context, however pseudo-methods run with the class context of the caller, which
      enables it to access any accessible private method
    - call references to non-static methods contain a weak reference to the object; the lifetime of the object is not
      extended by the call reference

    @see
    - <object>::getCallReference()
    - @ref Qore::Program::getCallReference()

    @since %Qore 0.9.4
 */
code get_call_reference(string identifier) [flags=RET_VALUE_ONLY] {
    return get_call_reference_intern(runtime_get_stack_object(), identifier, xsink);
}
///@}
