all repos — litestore @ 3a9d41ade414b4b634ba524b9e51cc3bd3d13c9a

A minimalist nosql document store.

Merge branch 'custom-resources' of github.com:h3rald/litestore into custom-resources
h3rald h3rald@h3rald.com
Sat, 08 Feb 2020 13:09:26 +0100
commit

3a9d41ade414b4b634ba524b9e51cc3bd3d13c9a

parent

a218928a04d832bf5aa8542cffaaf328ef56dfbc

M src/litestore.nimsrc/litestore.nim

@@ -1,5 +1,6 @@

import strutils, + strtabs, os, uri, httpcore,
A src/litestorepkg/examples/test.js

@@ -0,0 +1,1 @@

+LiteStore.response.content = LiteStore.api.get('docs').content;
M src/litestorepkg/lib/api_v5.nimsrc/litestorepkg/lib/api_v5.nim

@@ -46,9 +46,9 @@ var field = matches[1]

if field[0] == '$': field = "json_extract(documents.data, '$1')" % matches[1] if matches[0] == "-": - clauses.add("$1 DESC" % field) + clauses.add("$1 COLLATE NOCASE DESC" % field) else: - clauses.add("$1 ASC" % field) + clauses.add("$1 COLLATE NOCASE ASC" % field) return clauses.join(", ") proc selectClause*(str: string, options: var QueryOptions) =
M src/litestorepkg/lib/api_v6.nimsrc/litestorepkg/lib/api_v6.nim

@@ -7,14 +7,15 @@ strtabs,

pegs, json, os, - times, - duktape + uri, + times import types, contenttypes, core, utils, - logger + logger, + duktape # Helper procs

@@ -47,9 +48,9 @@ var field = matches[1]

if field[0] == '$': field = "json_extract(documents.data, '$1')" % matches[1] if matches[0] == "-": - clauses.add("$1 DESC" % field) + clauses.add("$1 COLLATE NOCASE DESC" % field) else: - clauses.add("$1 ASC" % field) + clauses.add("$1 COLLATE NOCASE ASC" % field) return clauses.join(", ") proc selectClause*(str: string, options: var QueryOptions) =

@@ -824,7 +825,6 @@ if id != "":

if resource == "indexes": var field = "" try: - echo req.body field = parseJson(req.body.strip)["field"].getStr except: return resError(Http400, "Bad Request - Invalid JSON body - $1" % getCurrentExceptionMsg())

@@ -909,12 +909,178 @@ return validate(req, LS, resource, id, patch)

else: return resError(Http405, "Method not allowed: $1" % $req.reqMethod) - # Custom Resources support +# Custom Resources support -proc execute*(req: LSRequest, LS:LiteStore, id: string): LSResponse = - var ctx = duk_create_heap_default() - var ctx_idx = ctx.duk_push_object() +proc createRequest(LS: LiteStore, ctx: DTContext, obj: duk_idx_t, req: LSRequest) = var req_idx = ctx.duk_push_object() + discard ctx.duk_push_string($req.reqMethod) + discard ctx.duk_put_prop_string(req_idx, "method") + # url + var uri_idx = ctx.duk_push_object() + discard ctx.duk_push_string(LS.address) + discard ctx.duk_put_prop_string(uri_idx, "hostname") + ctx.duk_push_int(cast[cint](LS.port)) + discard ctx.duk_put_prop_string(uri_idx, "port") + discard ctx.duk_push_string(req.url.query) + discard ctx.duk_put_prop_string(uri_idx, "search") + discard ctx.duk_push_string(req.url.path) + discard ctx.duk_put_prop_string(uri_idx, "path") + discard ctx.duk_put_prop_string(req_idx, "url") + discard ctx.duk_push_string(req.body) + discard ctx.duk_put_prop_string(req_idx, "body") + var hd_idx = ctx.duk_push_object() + for k, v in pairs(req.headers): + discard ctx.duk_push_string(v) + discard ctx.duk_put_prop_string(hd_idx, k) + discard ctx.duk_put_prop_string(req_idx, "headers") + discard ctx.duk_put_prop_string(obj, "request") + +proc createResponse(LS: LiteStore, ctx: DTContext, obj: duk_idx_t) = + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(200) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string("") + discard ctx.duk_put_prop_string(res_idx, "content") + var hd_idx = ctx.duk_push_object() + discard ctx.duk_push_string("*") + discard ctx.duk_put_prop_string(hd_idx, "Access-Control-Allow-Origin") + discard ctx.duk_push_string("Authorization, Content-Type") + discard ctx.duk_put_prop_string(hd_idx, "Access-Control-Allow-Headers") + discard ctx.duk_push_string(LS.appname & "/" & LS.appversion) + discard ctx.duk_put_prop_string(hd_idx, "Server") + discard ctx.duk_push_string("application/json") + discard ctx.duk_put_prop_string(hd_idx, "Content-Type") + discard ctx.duk_put_prop_string(res_idx, "headers") + discard ctx.duk_put_prop_string(obj, "response") + +proc newSimpleLSRequest(meth: HttpMethod, resource, id, body = "", params = "", headers = newHttpHeaders()): LSRequest = + result.reqMethod = meth + result.body = body + result.headers = headers + result.url = parseUri("$1://$2:$3/$4/$5?$6" % @["http", "localhost", "9500", resource, id, params]) + + +proc get(resource, id: string, params = ""): LSResponse = + return newSimpleLSRequest(HttpGet, resource, id, "", params).get(LS, resource, id) + +proc post(resource, folder, body: string, ct = ""): LSResponse = + var headers = newHttpHeaders() + if ct != "": + headers["Content-Type"] = ct + return newSimpleLSRequest(HttpPost, resource, "", body, "", headers).post(LS, resource, folder & "/") + +proc put(resource, id, body: string, ct = ""): LSResponse = + var headers = newHttpHeaders() + if ct != "": + headers["Content-Type"] = ct + return newSimpleLSRequest(HttpPut, resource, id, body, "", headers).put(LS, resource, id) + +proc patch(resource, id, body: string): LSResponse = + var headers = newHttpHeaders() + headers["Content-Type"] = "application/json" + return newSimpleLSRequest(HttpPatch, resource, id, body, "", headers).patch(LS, resource, id) + +proc delete(resource, id: string): LSResponse = + return newSimpleLSRequest(HttpPatch, resource, id).delete(LS, resource, id) + +proc head(resource, id: string): LSResponse = + return newSimpleLSRequest(HttpHead, resource, id).head(LS, resource, id) + +proc registerApi(LS: LiteStore, ctx: DTContext, obj: duk_idx_t) = + var api_idx = ctx.duk_push_object() + # GET + var get: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let id = duk_get_string(ctx, 1) + let params = duk_get_string(ctx, 2) + let resp = get($resource, $id, $params) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, get, 3) + discard ctx.duk_put_prop_string(api_idx, "get") + # POST + var post: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let folder = duk_get_string(ctx, 1) + let body = duk_get_string(ctx, 2) + let ct = duk_get_string(ctx, 3) + let resp = post($resource, $folder, $body, $ct) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, post, 4) + discard ctx.duk_put_prop_string(api_idx, "post") + # PUT + var put: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let id = duk_get_string(ctx, 1) + let body = duk_get_string(ctx, 2) + let ct = duk_get_string(ctx, 3) + let resp = put($resource, $id, $body, $ct) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, put, 4) + discard ctx.duk_put_prop_string(api_idx, "put") + # PATCH + var patch: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let id = duk_get_string(ctx, 1) + let body = duk_get_string(ctx, 2) + let resp = patch($resource, $id, $body) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, patch, 3) + discard ctx.duk_put_prop_string(api_idx, "patch") + # DELETE + var delete: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let id = duk_get_string(ctx, 1) + let resp = delete($resource, $id) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, delete, 2) + discard ctx.duk_put_prop_string(api_idx, "delete") + # HEAD + var head: DTCFunction = (proc (ctx: DTContext): cint{.stdcall.} = + let resource = duk_get_string(ctx, 0) + let id = duk_get_string(ctx, 1) + let resp = head($resource, $id) + var res_idx = ctx.duk_push_object() + ctx.duk_push_int(cast[cint](resp.code)) + discard ctx.duk_put_prop_string(res_idx, "code") + discard ctx.duk_push_string(resp.content) + discard ctx.duk_put_prop_string(res_idx, "content") + return 1 + ) + discard duk_push_c_function(ctx, head, 2) + discard ctx.duk_put_prop_string(api_idx, "head") + discard ctx.duk_put_prop_string(obj, "api") + +proc execute*(req: LSRequest, LS:LiteStore, id: string): LSResponse = var resource: string if not LS.customResources.hasKey(id): # Attempt to retrieve resource from system documents

@@ -926,24 +1092,26 @@ resource = doc.data

else: resource = LS.customResources[id] # Create execution context - ctx.duk_push_int(200) - discard ctx.duk_put_prop_string(req_idx, "code") - discard ctx.duk_push_string("") - discard ctx.duk_put_prop_string(req_idx, "content") - # Todo: Add default headers - discard ctx.duk_put_prop_string(ctx_idx, "response") - # Todo: Add request object - discard ctx.duk_put_global_string("ctx") + var ctx = duk_create_heap_default() + duk_console_init(ctx) + duk_print_alert_init(ctx) + var ctx_idx = ctx.duk_push_object() + LS.registerApi(ctx, ctx_idx) + LS.createRequest(ctx, ctx_idx, req) + LS.createResponse(ctx, ctx_idx) + discard ctx.duk_put_global_string("LiteStore") # Evaluate custom resource - try: - ctx.duk_eval_string(LS.customResources[id]) - except: - return resError(Http500, "An error occurred when executing custom resource code.") + if ctx.duk_peval_string(LS.customResources[id]) != 0: + return resError(Http500, $ctx.duk_safe_to_string(-1)) # Retrieve response - ctx.duk_eval_string("JSON.stringify(ctx.response)") - echo "TEST!" + discard + if ctx.duk_peval_string("JSON.stringify(LiteStore.response);") != 0: + return resError(Http500, $ctx.duk_safe_to_string(-1)) let jResponse = parseJson($(ctx.duk_get_string(-1))) - echo jResponse ctx.duk_destroy_heap(); result.code = HttpCode(jResponse["code"].getInt) result.content = jResponse["content"].getStr + result.headers = newHttpHeaders() + for k, v in pairs(jResponse["headers"]): + result.headers[k] = v.getStr + result.headers["Content-Length"] = $result.content.len
M src/litestorepkg/lib/duktape.nimsrc/litestorepkg/lib/duktape.nim

@@ -29,7 +29,11 @@ import strutils

const sourcePath = currentSourcePath().split({'\\', '/'})[0..^2].join("/") {.passC: "-I\"" & sourcePath & "/../vendor/duktape\"".} const headerduktape = sourcePath & "/../vendor/duktape/duktape.h" +const headerconsole = sourcePath & "/../vendor/duktape/extras/console/duk_console.h" +const headerprintalert = sourcePath & "/../vendor/duktape/extras/print-alert/duk_print_alert.h" {.compile: "../vendor/duktape/duktape.c".} +{.compile: "../vendor/duktape/extras/console/duk_console.c".} +{.compile: "../vendor/duktape/extras/print-alert/duk_print_alert.c".} const DUK_VERSION* = 20201 DUK_DEBUG_PROTOCOL_VERSION* = 2

@@ -702,6 +706,15 @@ proc duk_eval_string*(ctx: DTContext, s: cstring) {.header: headerduktape.}

proc duk_pcompile_string*(ctx: DTContext, flags: duk_uint_t, s: cstring): duk_int_t {.header: headerduktape.} proc duk_safe_to_string*(ctx: DTContext, idx: duk_idx_t): cstring {.header: headerduktape.} # proc duk_to_string*(ctx: DTContext, index: cint): cstring {.header: headerduktape.} + +proc duk_peval_string*(ctx: DTContext, s: cstring): duk_int_t {.header: headerduktape.} + +## Extras + +proc duk_console_init*(ctx: DTContext, flags: duk_uint_t = 0) {.stdcall, +importc: "duk_console_init", header: headerconsole.} +proc duk_print_alert_init*(ctx: DTContext, flags: duk_uint_t = 0) {.stdcall, +importc: "duk_print_alert_init", header: headerprintalert.} type DTCFunction* = duk_c_function
A src/litestorepkg/vendor/duktape/extras/console/duk_console.c

@@ -0,0 +1,185 @@

+/* + * Minimal 'console' binding. + * + * https://github.com/DeveloperToolsWG/console-object/blob/master/api.md + * https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference + * https://developer.mozilla.org/en/docs/Web/API/console + */ + +#include <stdio.h> +#include <stdarg.h> +#include "duktape.h" +#include "duk_console.h" + +/* XXX: Add some form of log level filtering. */ + +/* XXX: Should all output be written via e.g. console.write(formattedMsg)? + * This would make it easier for user code to redirect all console output + * to a custom backend. + */ + +/* XXX: Init console object using duk_def_prop() when that call is available. */ + +static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) { + duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx); + FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr; + duk_idx_t n = duk_get_top(ctx); + duk_idx_t i; + + duk_get_global_string(ctx, "console"); + duk_get_prop_string(ctx, -1, "format"); + + for (i = 0; i < n; i++) { + if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) { + /* Slow path formatting. */ + duk_dup(ctx, -1); /* console.format */ + duk_dup(ctx, i); + duk_call(ctx, 1); + duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */ + } + } + + duk_pop_2(ctx); + + duk_push_string(ctx, " "); + duk_insert(ctx, 0); + duk_join(ctx, n); + + if (error_name) { + duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1)); + duk_push_string(ctx, "name"); + duk_push_string(ctx, error_name); + duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */ + duk_get_prop_string(ctx, -1, "stack"); + } + + fprintf(output, "%s\n", duk_to_string(ctx, -1)); + if (flags & DUK_CONSOLE_FLUSH) { + fflush(output); + } + return 0; +} + +static duk_ret_t duk__console_assert(duk_context *ctx) { + if (duk_to_boolean(ctx, 0)) { + return 0; + } + duk_remove(ctx, 0); + + return duk__console_log_helper(ctx, "AssertionError"); +} + +static duk_ret_t duk__console_log(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_trace(duk_context *ctx) { + return duk__console_log_helper(ctx, "Trace"); +} + +static duk_ret_t duk__console_info(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_warn(duk_context *ctx) { + return duk__console_log_helper(ctx, NULL); +} + +static duk_ret_t duk__console_error(duk_context *ctx) { + return duk__console_log_helper(ctx, "Error"); +} + +static duk_ret_t duk__console_dir(duk_context *ctx) { + /* For now, just share the formatting of .log() */ + return duk__console_log_helper(ctx, 0); +} + +static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) { + duk_push_c_function(ctx, func, DUK_VARARGS); + duk_push_string(ctx, "name"); + duk_push_string(ctx, name); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */ + duk_set_magic(ctx, -1, (duk_int_t) flags); + duk_put_prop_string(ctx, -2, name); +} + +void duk_console_init(duk_context *ctx, duk_uint_t flags) { + duk_uint_t flags_orig; + + /* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified, + * just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY. + */ + if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) { + flags &= ~DUK_CONSOLE_STDOUT_ONLY; + } + /* Remember the (possibly corrected) flags we received. */ + flags_orig = flags; + + duk_push_object(ctx); + + /* Custom function to format objects; user can replace. + * For now, try JX-formatting and if that fails, fall back + * to ToString(v). + */ + duk_eval_string(ctx, + "(function (E) {" + "return function format(v){" + "try{" + "return E('jx',v);" + "}catch(e){" + "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */ + "}" + "};" + "})(Duktape.enc)"); + duk_put_prop_string(ctx, -2, "format"); + + flags = flags_orig; + if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { + /* No output indicators were specified; these levels go to stdout. */ + flags |= DUK_CONSOLE_STDOUT_ONLY; + } + duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags); + duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags); + duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */ + duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags); + duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags); + + flags = flags_orig; + if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { + /* No output indicators were specified; these levels go to stderr. */ + flags |= DUK_CONSOLE_STDERR_ONLY; + } + duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags); + duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags); + duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */ + duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags); + + duk_put_global_string(ctx, "console"); + + /* Proxy wrapping: ensures any undefined console method calls are + * ignored silently. This was required specifically by the + * DeveloperToolsWG proposal (and was implemented also by Firefox: + * https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is + * apparently no longer the preferred way of implementing console. + * When Proxy is enabled, whitelist at least .toJSON() to avoid + * confusing JX serialization of the console object. + */ + + if (flags & DUK_CONSOLE_PROXY_WRAPPER) { + /* Tolerate failure to initialize Proxy wrapper in case + * Proxy support is disabled. + */ + (void) duk_peval_string_noresult(ctx, + "(function(){" + "var D=function(){};" + "var W={toJSON:true};" /* whitelisted */ + "console=new Proxy(console,{" + "get:function(t,k){" + "var v=t[k];" + "return typeof v==='function'||W[k]?v:D;" + "}" + "});" + "})();" + ); + } +}
A src/litestorepkg/vendor/duktape/extras/console/duk_console.h

@@ -0,0 +1,29 @@

+#if !defined(DUK_CONSOLE_H_INCLUDED) +#define DUK_CONSOLE_H_INCLUDED + +#include "duktape.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */ +#define DUK_CONSOLE_PROXY_WRAPPER (1U << 0) + +/* Flush output after every call. */ +#define DUK_CONSOLE_FLUSH (1U << 1) + +/* Send output to stdout only (default is mixed stdout/stderr). */ +#define DUK_CONSOLE_STDOUT_ONLY (1U << 2) + +/* Send output to stderr only (default is mixed stdout/stderr). */ +#define DUK_CONSOLE_STDERR_ONLY (1U << 3) + +/* Initialize the console system */ +extern void duk_console_init(duk_context *ctx, duk_uint_t flags); + +#if defined(__cplusplus) +} +#endif /* end 'extern "C"' wrapper */ + +#endif /* DUK_CONSOLE_H_INCLUDED */
A src/litestorepkg/vendor/duktape/extras/print-alert/duk_print_alert.c

@@ -0,0 +1,127 @@

+/* + * Duktape 1.x compatible print() and alert() bindings. + */ + +#include <stdio.h> +#include <string.h> +#include "duktape.h" +#include "duk_print_alert.h" + +#define DUK_PRINT_ALERT_FLUSH /* Flush after stdout/stderr write (Duktape 1.x: yes) */ +#undef DUK_PRINT_ALERT_SMALL /* Prefer smaller footprint (but slower and more memory churn) */ + +#if defined(DUK_PRINT_ALERT_SMALL) +static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { + duk_idx_t nargs; + + nargs = duk_get_top(ctx); + + /* If argument count is 1 and first argument is a buffer, write the buffer + * as raw data into the file without a newline; this allows exact control + * over stdout/stderr without an additional entrypoint (useful for now). + * Otherwise current print/alert semantics are to ToString() coerce + * arguments, join them with a single space, and append a newline. + */ + + if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); + fwrite((const void *) buf, 1, (size_t) sz_buf, fh); + } else { + duk_push_string(ctx, " "); + duk_insert(ctx, 0); + duk_concat(ctx, nargs); + fprintf(fh, "%s\n", duk_require_string(ctx, -1)); + } + +#if defined(DUK_PRINT_ALERT_FLUSH) + fflush(fh); +#endif + + return 0; +} +#else +/* Faster, less churn, higher footprint option. */ +static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { + duk_idx_t nargs; + const duk_uint8_t *buf; + duk_size_t sz_buf; + const char nl = '\n'; + duk_uint8_t buf_stack[256]; + + nargs = duk_get_top(ctx); + + /* If argument count is 1 and first argument is a buffer, write the buffer + * as raw data into the file without a newline; this allows exact control + * over stdout/stderr without an additional entrypoint (useful for now). + * Otherwise current print/alert semantics are to ToString() coerce + * arguments, join them with a single space, and append a newline. + */ + + if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); + } else if (nargs > 0) { + duk_idx_t i; + duk_size_t sz_str; + const duk_uint8_t *p_str; + duk_uint8_t *p; + + sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */ + for (i = 0; i < nargs; i++) { + (void) duk_to_lstring(ctx, i, &sz_str); + sz_buf += sz_str; + } + + if (sz_buf <= sizeof(buf_stack)) { + p = (duk_uint8_t *) buf_stack; + } else { + p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf); + } + + buf = (const duk_uint8_t *) p; + for (i = 0; i < nargs; i++) { + p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str); + memcpy((void *) p, (const void *) p_str, sz_str); + p += sz_str; + *p++ = (duk_uint8_t) (i == nargs - 1 ? '\n' : ' '); + } + } else { + buf = (const duk_uint8_t *) &nl; + sz_buf = 1; + } + + /* 'buf' contains the string to write, 'sz_buf' contains the length + * (which may be zero). + */ + + if (sz_buf > 0) { + fwrite((const void *) buf, 1, (size_t) sz_buf, fh); +#if defined(DUK_PRINT_ALERT_FLUSH) + fflush(fh); +#endif + } + + return 0; +} +#endif + +static duk_ret_t duk__print(duk_context *ctx) { + return duk__print_alert_helper(ctx, stdout); +} + +static duk_ret_t duk__alert(duk_context *ctx) { + return duk__print_alert_helper(ctx, stderr); +} + +void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) { + (void) flags; /* unused at the moment */ + + /* XXX: use duk_def_prop_list(). */ + duk_push_global_object(ctx); + duk_push_string(ctx, "print"); + duk_push_c_function(ctx, duk__print, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_push_string(ctx, "alert"); + duk_push_c_function(ctx, duk__alert, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_pop(ctx); +}
A src/litestorepkg/vendor/duktape/extras/print-alert/duk_print_alert.h

@@ -0,0 +1,18 @@

+#if !defined(DUK_PRINT_ALERT_H_INCLUDED) +#define DUK_PRINT_ALERT_H_INCLUDED + +#include "duktape.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* No flags at the moment. */ + +extern void duk_print_alert_init(duk_context *ctx, duk_uint_t flags); + +#if defined(__cplusplus) +} +#endif /* end 'extern "C"' wrapper */ + +#endif /* DUK_PRINT_ALERT_H_INCLUDED */