diff --git a/site/doc/examples/sample_unit.uce b/site/doc/examples/sample_unit.uce new file mode 100644 index 0000000..04d5511 --- /dev/null +++ b/site/doc/examples/sample_unit.uce @@ -0,0 +1,16 @@ +// Small target unit used by the unit_*/component documentation examples. +RENDER(Request& context) +{ + print("hello from the sample unit"); +} + +COMPONENT(Request& context) +{ + print("hello from the sample component"); +} + +EXPORT DValue* doc_greet(DValue* call_param) +{ + print("doc_greet() was called"); + return(0); +} diff --git a/site/doc/pages/2_DValue_assign.txt b/site/doc/pages/2_DValue_assign.txt index 840ee2b..99f224a 100644 --- a/site/doc/pages/2_DValue_assign.txt +++ b/site/doc/pages/2_DValue_assign.txt @@ -22,4 +22,7 @@ Assignment overloads forward to the matching `set(...)` overloads. They are the :example -print("DValue::assign example\n"); +DValue row; +row["name"] = "Ada"; +row["role"] = "engineer"; +print(row["name"].to_string(), " / ", row["role"].to_string(), "\n"); diff --git a/site/doc/pages/2_DValue_clear.txt b/site/doc/pages/2_DValue_clear.txt index eaf7c45..e8c1347 100644 --- a/site/doc/pages/2_DValue_clear.txt +++ b/site/doc/pages/2_DValue_clear.txt @@ -13,4 +13,7 @@ Clears the value into an empty map-shaped value and resets its array index. Unli :example -print("DValue::clear example\n"); +DValue u; +u["a"] = "1"; u["b"] = "2"; +u.clear(); +print("after clear, has a: ", u.has("a") ? "yes" : "no", "\n"); diff --git a/site/doc/pages/2_DValue_deref.txt b/site/doc/pages/2_DValue_deref.txt index 7bb927b..c378033 100644 --- a/site/doc/pages/2_DValue_deref.txt +++ b/site/doc/pages/2_DValue_deref.txt @@ -14,4 +14,8 @@ Returns the value after following internal reference links. Most public accessor :example -print("DValue::deref example\n"); +DValue target; +target.set("value"); +DValue ref; +ref.set_reference(&target); +print("through ref: ", ref.deref().to_string(), "\n"); diff --git a/site/doc/pages/2_DValue_get_or_create.txt b/site/doc/pages/2_DValue_get_or_create.txt index a557003..8a80381 100644 --- a/site/doc/pages/2_DValue_get_or_create.txt +++ b/site/doc/pages/2_DValue_get_or_create.txt @@ -15,4 +15,6 @@ Ensures the value is map-shaped and returns the named child. If the key does not :example -print("DValue::get_or_create example\n"); +DValue config; +config.get_or_create("host")->set("localhost"); +print(config.has("host") ? config["host"].to_string() : "missing", "\n"); diff --git a/site/doc/pages/2_DValue_get_type_name.txt b/site/doc/pages/2_DValue_get_type_name.txt index a6a5baa..d13af0f 100644 --- a/site/doc/pages/2_DValue_get_type_name.txt +++ b/site/doc/pages/2_DValue_get_type_name.txt @@ -12,4 +12,6 @@ Returns a human-readable name for the dereferenced value's type tag. :example -print("DValue::get_type_name example\n"); +DValue s; s.set("text"); +DValue m; m["k"] = "v"; +print(s.get_type_name(), " / ", m.get_type_name(), "\n"); diff --git a/site/doc/pages/2_DValue_has.txt b/site/doc/pages/2_DValue_has.txt index 1e4d94d..be66eb9 100644 --- a/site/doc/pages/2_DValue_has.txt +++ b/site/doc/pages/2_DValue_has.txt @@ -15,6 +15,6 @@ return value : true when the dereferenced value is map-shaped and contains the k Checks for a child key without creating it. Returns false for scalars and missing keys. :example -DValue user; -user["name"] = "Ada"; -print(user.has("name") ? "yes\n" : "no\n"); +DValue u; +u["name"] = "Ada"; +print(u.has("name") ? "has name" : "no name", " / ", u.has("age") ? "has age" : "no age", "\n"); diff --git a/site/doc/pages/2_DValue_is_reference.txt b/site/doc/pages/2_DValue_is_reference.txt index 4512649..0340dc5 100644 --- a/site/doc/pages/2_DValue_is_reference.txt +++ b/site/doc/pages/2_DValue_is_reference.txt @@ -14,4 +14,7 @@ Reports whether the current node's direct type tag is the internal reference tag :example -print("DValue::is_reference example\n"); +DValue target; target.set("x"); +DValue ref; ref.set_reference(&target); +DValue plain; plain.set("y"); +print(ref.is_reference() ? "ref" : "plain", " / ", plain.is_reference() ? "ref" : "plain", "\n"); diff --git a/site/doc/pages/2_DValue_key.txt b/site/doc/pages/2_DValue_key.txt index 092bd46..787f142 100644 --- a/site/doc/pages/2_DValue_key.txt +++ b/site/doc/pages/2_DValue_key.txt @@ -16,4 +16,8 @@ Looks up one child without creating it. The non-const overload forwards through :example -print("DValue::key example\n"); +DValue user; +user["email"] = "ada@example.test"; +const DValue* found = user.key("email"); +const DValue* missing = user.key("phone"); +print(found ? found->to_string() : "none", " / ", missing ? "present" : "absent", "\n"); diff --git a/site/doc/pages/2_DValue_operator_index.txt b/site/doc/pages/2_DValue_operator_index.txt index d7e48fb..9da6c57 100644 --- a/site/doc/pages/2_DValue_operator_index.txt +++ b/site/doc/pages/2_DValue_operator_index.txt @@ -19,4 +19,6 @@ Returns a mutable reference to a child, creating the child when it does not alre :example -print("DValue::operator_index example\n"); +DValue m; +m["a"]["b"] = "nested"; +print(m["a"]["b"].to_string(), "\n"); diff --git a/site/doc/pages/2_DValue_pop.txt b/site/doc/pages/2_DValue_pop.txt index b384a15..4400f9f 100644 --- a/site/doc/pages/2_DValue_pop.txt +++ b/site/doc/pages/2_DValue_pop.txt @@ -13,4 +13,9 @@ Ensures the value is map-shaped, removes the last entry according to `std::map` :example -print("DValue::pop example\n"); +DValue list; +list.set_array(); +DValue a; a.set("first"); list.push(a); +DValue b; b.set("last"); list.push(b); +DValue popped = list.pop(); +print("popped ", popped.to_string(), ", remaining ", list.keys().size(), "\n"); diff --git a/site/doc/pages/2_DValue_push.txt b/site/doc/pages/2_DValue_push.txt index 4eb7b93..f46d016 100644 --- a/site/doc/pages/2_DValue_push.txt +++ b/site/doc/pages/2_DValue_push.txt @@ -15,4 +15,8 @@ Appends a child under the next numeric key. Empty values become list-shaped. Exi :example -print("DValue::push example\n"); +DValue list; +list.set_array(); +DValue a; a.set("x"); list.push(a); +DValue b; b.set("y"); list.push(b); +print("count: ", list.keys().size(), "\n"); diff --git a/site/doc/pages/2_DValue_reference_target.txt b/site/doc/pages/2_DValue_reference_target.txt index 871ea52..349279c 100644 --- a/site/doc/pages/2_DValue_reference_target.txt +++ b/site/doc/pages/2_DValue_reference_target.txt @@ -15,4 +15,8 @@ If this node is an internal reference, follows reference links up to the runtime :example -print("DValue::reference_target example\n"); +DValue target; +target.set("data"); +DValue ref; +ref.set_reference(&target); +print(ref.reference_target() == &target ? "points to target" : "elsewhere", "\n"); diff --git a/site/doc/pages/2_DValue_remove.txt b/site/doc/pages/2_DValue_remove.txt index 91cc2e9..a39c44b 100644 --- a/site/doc/pages/2_DValue_remove.txt +++ b/site/doc/pages/2_DValue_remove.txt @@ -15,4 +15,7 @@ Ensures the value is map-shaped and erases the named child. Removing the last ch :example -print("DValue::remove example\n"); +DValue u; +u["a"] = "1"; u["b"] = "2"; +u.remove("a"); +print("has a: ", u.has("a") ? "yes" : "no", ", has b: ", u.has("b") ? "yes" : "no", "\n"); diff --git a/site/doc/pages/2_DValue_set.txt b/site/doc/pages/2_DValue_set.txt index d74838c..4a6f724 100644 --- a/site/doc/pages/2_DValue_set.txt +++ b/site/doc/pages/2_DValue_set.txt @@ -22,4 +22,6 @@ Assigns a new value, forwarding through references when possible. String, pointe :example -print("DValue::set example\n"); +DValue v; +v.set("hello"); +print(v.to_string(), "\n"); diff --git a/site/doc/pages/2_DValue_set_array.txt b/site/doc/pages/2_DValue_set_array.txt index 391c8c4..a7cf181 100644 --- a/site/doc/pages/2_DValue_set_array.txt +++ b/site/doc/pages/2_DValue_set_array.txt @@ -14,4 +14,8 @@ Clears the value and makes it an empty list-shaped map. Subsequent `push()` call :example -print("DValue::set_array example\n"); +DValue list; +list.set_array(); +DValue item; item.set("first"); +list.push(item); +print("is_list=", list.is_list() ? "yes" : "no", " count=", list.keys().size(), "\n"); diff --git a/site/doc/pages/2_DValue_set_bool.txt b/site/doc/pages/2_DValue_set_bool.txt index 8e054f3..c73d0b1 100644 --- a/site/doc/pages/2_DValue_set_bool.txt +++ b/site/doc/pages/2_DValue_set_bool.txt @@ -14,4 +14,6 @@ Stores a boolean value, forwarding through references when possible. :example -print("DValue::set_bool example\n"); +DValue flag; +flag.set_bool(true); +print(flag.to_bool() ? "true" : "false", "\n"); diff --git a/site/doc/pages/2_DValue_set_reference.txt b/site/doc/pages/2_DValue_set_reference.txt index 14a93a8..ccb41b0 100644 --- a/site/doc/pages/2_DValue_set_reference.txt +++ b/site/doc/pages/2_DValue_set_reference.txt @@ -14,4 +14,8 @@ Stores an internal reference to another `DValue`. Most unit code should not need :example -print("DValue::set_reference example\n"); +DValue target; +target.set("original"); +DValue ref; +ref.set_reference(&target); +print(ref.deref().to_string(), "\n"); diff --git a/site/doc/pages/2_DValue_set_type.txt b/site/doc/pages/2_DValue_set_type.txt index a41ab95..7398afe 100644 --- a/site/doc/pages/2_DValue_set_type.txt +++ b/site/doc/pages/2_DValue_set_type.txt @@ -17,4 +17,7 @@ Prefer the typed setters (`set`, `set_bool`, `set_array`) in normal unit code. :example -print("DValue::set_type example\n"); +DValue v; +v.set_array(); +DValue item; item.set("x"); v.push(item); +print("type after set_array: ", v.get_type_name(), "\n"); diff --git a/site/doc/pages/2_Request_ob_start.txt b/site/doc/pages/2_Request_ob_start.txt index 877ac04..eca8fce 100644 --- a/site/doc/pages/2_Request_ob_start.txt +++ b/site/doc/pages/2_Request_ob_start.txt @@ -14,4 +14,7 @@ Starts a new output buffer for the request and makes it the active `context.ob`. :example -print("Request::ob_start example\n"); +context.ob_start(); +print("fragment"); +String captured = ob_get_close(); +print("captured: ", captured, "\n"); diff --git a/site/doc/pages/3_Blocked functions.txt b/site/doc/pages/3_Blocked functions.txt index e203926..5c042f3 100644 --- a/site/doc/pages/3_Blocked functions.txt +++ b/site/doc/pages/3_Blocked functions.txt @@ -55,6 +55,3 @@ listed, so a deployment cannot be bricked by an over-broad blocklist: - Pure-compute library functions that are NOT hostcalls (string ops, `DValue` methods, hashing helpers like `gen_noise`, etc.) are not OS capabilities and cannot be blocked this way — only `uce_host_*` membrane calls are gateable. - -:example -print("Blocked functions documents a UCE concept.\n"); diff --git a/site/doc/pages/3_C++ Preprocessor.txt b/site/doc/pages/3_C++ Preprocessor.txt index 23d4dd8..c9ef628 100644 --- a/site/doc/pages/3_C++ Preprocessor.txt +++ b/site/doc/pages/3_C++ Preprocessor.txt @@ -119,4 +119,8 @@ The page template can then render `context.call["fragments"]["head"]` inside `"; }); +print(html, "\n"); diff --git a/site/doc/pages/3_Coming from React.txt b/site/doc/pages/3_Coming from React.txt index 2b3a722..f9723bb 100644 --- a/site/doc/pages/3_Coming from React.txt +++ b/site/doc/pages/3_Coming from React.txt @@ -64,4 +64,5 @@ When a unit fails to compile, UCE reports the source path, generated C++ path, c - Component children/slot syntax is not part of UCE yet; use explicit props and component calls for now. :example -print("Coming from React documents a UCE concept.\n"); +// Components are UCE's reusable, props-driven building blocks (like React components). +print(component("examples/sample_unit"), "\n"); diff --git a/site/doc/pages/ascii_safe_name.txt b/site/doc/pages/ascii_safe_name.txt index 4b86ec9..98ff0ef 100644 --- a/site/doc/pages/ascii_safe_name.txt +++ b/site/doc/pages/ascii_safe_name.txt @@ -2,17 +2,15 @@ String ascii_safe_name(String raw) :params -raw : input string to normalize -return value : ASCII-safe identifier made from letters, digits, and underscores +raw : arbitrary input string +return value : an ASCII-only sanitized name + +:content +Like `safe_name()`, but also restricts the result to ASCII, transliterating or dropping non-ASCII characters. Use it when the consumer (a legacy filesystem, header, or protocol) requires plain ASCII. + +:example +print(ascii_safe_name("Cafe Munchen 2024"), "\n"); :see >string - -:content -Builds a conservative identifier by keeping ASCII letters, digits, and underscores and dropping other characters. - -This is useful when turning user- or config-provided names into handler suffixes, DOM-safe variable stems, or CSS and JS hook names. - - -:example -print(ascii_safe_name("Hello, UCE!"), "\n"); +safe_name diff --git a/site/doc/pages/backtrace_capture.txt b/site/doc/pages/backtrace_capture.txt index 3a7ef1c..5b3fbe8 100644 --- a/site/doc/pages/backtrace_capture.txt +++ b/site/doc/pages/backtrace_capture.txt @@ -20,4 +20,5 @@ Example: :example -print("backtrace_capture example\n"); +String trace = backtrace_capture(); +print(trace == "" ? "(empty in the wasm sandbox; populated by the native crash handler)" : trace, "\n"); diff --git a/site/doc/pages/backtrace_get_frames.txt b/site/doc/pages/backtrace_get_frames.txt index b55c4e4..b09cbdb 100644 --- a/site/doc/pages/backtrace_get_frames.txt +++ b/site/doc/pages/backtrace_get_frames.txt @@ -18,4 +18,4 @@ Formats a captured native backtrace frame array. Most page code should use `backtrace_capture()` instead. Use this helper when you already have raw frame pointers from lower-level diagnostic code. :example -print("backtrace_get_frames example\n"); +print(backtrace_get_frames(0, 0) == "" ? "(no frames available in the wasm sandbox)" : "frames captured", "\n"); diff --git a/site/doc/pages/base64_decode.txt b/site/doc/pages/base64_decode.txt index 296d1a1..7afcdf2 100644 --- a/site/doc/pages/base64_decode.txt +++ b/site/doc/pages/base64_decode.txt @@ -1,24 +1,16 @@ :sig -String base64_decode(String raw, bool& ok) +String base64_decode(String raw) :params -raw : Base64 encoded string -ok : set to `true` when decoding succeeds; set to `false` for invalid input -return value : decoded binary-safe string, or an empty string when decoding fails - -:see ->string -base64_encode +raw : Base64-encoded text +return value : the decoded bytes (empty on invalid input) :content -Decodes a Base64 string. - -Pass a `bool` variable for `ok` so callers can distinguish invalid input from a valid empty decoded value. - -Example: - - +Decodes Base64 text back to bytes. Pairs with `base64_encode()` for round-tripping binary data through text channels. :example -bool ok = false; -print(base64_decode("aGVsbG8=", ok), " ", ok ? "ok" : "bad", "\n"); +print(base64_decode("aGVsbG8="), "\n"); + +:see +>uri +base64_encode diff --git a/site/doc/pages/base64_encode.txt b/site/doc/pages/base64_encode.txt index 7119072..d85378c 100644 --- a/site/doc/pages/base64_encode.txt +++ b/site/doc/pages/base64_encode.txt @@ -2,21 +2,16 @@ String base64_encode(String raw) :params -raw : binary-safe source string -return value : Base64 encoded string - -:see ->string -base64_decode +raw : binary-safe source bytes +return value : the Base64-encoded text :content -Encodes a string with Base64. - -UCE strings can contain binary data, so `raw` may include NUL bytes and non-text bytes. - -Example: - - +Encodes bytes as Base64 text. UCE strings are binary-safe, so `raw` may contain NUL and other non-text bytes — handy for embedding `random_bytes()` or a `sha256()` digest in headers, cookies, or JSON. :example print(base64_encode("hello"), "\n"); + +:see +>uri +base64_decode +random_bytes diff --git a/site/doc/pages/basename.txt b/site/doc/pages/basename.txt index 84cc579..8e0da07 100644 --- a/site/doc/pages/basename.txt +++ b/site/doc/pages/basename.txt @@ -2,14 +2,16 @@ String basename(String fn) :params -fn : raw filename -return value : the file's name +fn : a path +return value : the final path component + +:content +Returns the last component of a path — the file or directory name without its leading directories. + +:example +print(basename("/var/www/site/index.uce"), "\n"); :see >sys - -:content -Isolates the file name component from a path or file name. - -:example -print("basename example\n"); +dirname +path_join diff --git a/site/doc/pages/cli_arg.txt b/site/doc/pages/cli_arg.txt index 0059fe6..8d96c63 100644 --- a/site/doc/pages/cli_arg.txt +++ b/site/doc/pages/cli_arg.txt @@ -15,4 +15,5 @@ If the key is missing, or the resolved value is empty, `default_value` is return For commands that need multiple values or typed reads, prefer calling `cli_input(context)` once and reading the returned `DValue` directly. :example -print("cli_arg example\n"); +String name = cli_arg(context, "name", "world"); +print("hello ", name, "\n"); diff --git a/site/doc/pages/cli_input.txt b/site/doc/pages/cli_input.txt index 3d6b1cf..62078e0 100644 --- a/site/doc/pages/cli_input.txt +++ b/site/doc/pages/cli_input.txt @@ -25,4 +25,5 @@ Convenience script usage: :example -print("cli_input example\n"); +DValue input = cli_input(context); +print("parsed ", input.keys().size(), " CLI arguments\n"); diff --git a/site/doc/pages/component.txt b/site/doc/pages/component.txt index 10865c3..f8e7ff5 100644 --- a/site/doc/pages/component.txt +++ b/site/doc/pages/component.txt @@ -58,4 +58,4 @@ Because `` escapes HTML, use `` when inserting the returned - `component()` then calls either `COMPONENT(Request& context)` or the selected `COMPONENT:NAME(Request& context)` handler. :example -print("component example\n"); +print(component("examples/sample_unit"), "\n"); diff --git a/site/doc/pages/component_exists.txt b/site/doc/pages/component_exists.txt index 4dd8e40..9755872 100644 --- a/site/doc/pages/component_exists.txt +++ b/site/doc/pages/component_exists.txt @@ -18,4 +18,4 @@ If `name` contains a colon, only the file portion is used for existence checks. This is useful when a page wants to render an optional component if it is present without hard-failing when it is missing. :example -print("component_exists example\n"); +print(component_exists("examples/sample_unit") ? "exists" : "not found", " / ", component_exists("no_such_component") ? "exists" : "not found", "\n"); diff --git a/site/doc/pages/component_render.txt b/site/doc/pages/component_render.txt index 9ea1ba9..81c8c55 100644 --- a/site/doc/pages/component_render.txt +++ b/site/doc/pages/component_render.txt @@ -25,4 +25,5 @@ Use `component_render()` when you want to write component output directly from C :example -print("component_render example\n"); +component_render("examples/sample_unit"); +print("\n"); diff --git a/site/doc/pages/component_resolve.txt b/site/doc/pages/component_resolve.txt index cf94beb..d86a45b 100644 --- a/site/doc/pages/component_resolve.txt +++ b/site/doc/pages/component_resolve.txt @@ -18,4 +18,4 @@ If `name` contains a colon, only the file portion is used for resolution. This is primarily a debugging helper so you can see which concrete file a shorthand component name maps to. :example -print("component_resolve example\n"); +print("resolved path: ", component_resolve("examples/sample_unit"), "\n"); diff --git a/site/doc/pages/crypto_equal.txt b/site/doc/pages/crypto_equal.txt index 533205f..92283cb 100644 --- a/site/doc/pages/crypto_equal.txt +++ b/site/doc/pages/crypto_equal.txt @@ -1,10 +1,20 @@ -# crypto_equal +:sig +bool crypto_equal(String a, String b) +:params +a : first value +b : second value +return value : true if the byte strings are equal -Constant-time byte comparison for secrets such as MACs and tokens. +:content +Compares two byte strings in constant time, so the comparison takes the same time whether they differ in the first byte or the last. Always use it to check secrets such as HMACs, tokens, and password hashes — `==` can leak how much of a secret matched via timing. :example -print(crypto_equal("abc", "abc") ? "same\n" : "different\n"); +String expected = hmac_sha256_hex("key", "payload"); +print(crypto_equal(expected, hmac_sha256_hex("key", "payload")) ? "valid" : "invalid", " / "); +print(crypto_equal(expected, "tampered") ? "valid" : "invalid", "\n"); :see ->noise +>sys +hmac_sha256_hex +sha256_hex diff --git a/site/doc/pages/cwd_get.txt b/site/doc/pages/cwd_get.txt index 5e97d45..cc9a18a 100644 --- a/site/doc/pages/cwd_get.txt +++ b/site/doc/pages/cwd_get.txt @@ -11,4 +11,4 @@ return value : the current working directory Returns the current working directory. :example -print("cwd_get example\n"); +print(cwd_get() != "" ? "have a working directory" : "none", "\n"); diff --git a/site/doc/pages/cwd_set.txt b/site/doc/pages/cwd_set.txt index ca86a35..a370369 100644 --- a/site/doc/pages/cwd_set.txt +++ b/site/doc/pages/cwd_set.txt @@ -11,4 +11,7 @@ path : the new working directory Sets the host worker process current directory. In wasm this is a hostcall, so restore the previous directory when using it inside request code. :example -print("cwd_set example\n"); +String old = cwd_get(); +cwd_set("/tmp"); +print("cwd is now ", cwd_get(), "\n"); +cwd_set(old); diff --git a/site/doc/pages/dir_list.txt b/site/doc/pages/dir_list.txt index 195e338..708fd1a 100644 --- a/site/doc/pages/dir_list.txt +++ b/site/doc/pages/dir_list.txt @@ -1,9 +1,26 @@ +:sig DValue dir_list(String path) -Returns a list of `{ name, type, size, mtime }` entries for a policy-gated directory. Names are sorted and exclude `.`/`..`. +:params +path : directory to list +return value : a list of entry maps, each with name, type, size, mtime + +:content +Lists a directory's entries as a `DValue` list. Each entry is a map with `name`, `type` ("file", "dir", "symlink", or "other"), `size`, and `mtime`. Iterate with `.each()` and read fields with `item.key("name")->to_string()`. + +:example +mkdir("/tmp/doc-dirlist"); +file_put_contents("/tmp/doc-dirlist/note.txt", "hi"); +DValue entries = dir_list("/tmp/doc-dirlist"); +String found = "no"; +entries.each([&](const DValue& item, String key) { + if(item.key("name") && item.key("name")->to_string() == "note.txt") + found = "yes"; +}); +print("note.txt listed: ", found, "\n"); :see >sys - -:example -print("dir_list example\n"); +file_stat +mkdir +ls diff --git a/site/doc/pages/dir_remove.txt b/site/doc/pages/dir_remove.txt index 8be3671..9539d53 100644 --- a/site/doc/pages/dir_remove.txt +++ b/site/doc/pages/dir_remove.txt @@ -1,9 +1,21 @@ -dir_remove +:sig +bool dir_remove(String path, bool recursive = false) -Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error. +:params +path : directory to remove +recursive : when true, remove the directory and everything inside it +return value : true on success + +:content +Removes a directory. By default it must be empty; pass `recursive = true` to delete the directory and all of its contents. + +:example +mkdir("/tmp/doc-rmdir"); +file_put_contents("/tmp/doc-rmdir/child.txt", "x"); +dir_remove("/tmp/doc-rmdir", true); +print(file_stat("/tmp/doc-rmdir")["exists"].to_bool() ? "still there" : "removed", "\n"); :see >sys - -:example -print("dir_remove example\n"); +mkdir +file_unlink diff --git a/site/doc/pages/dirname.txt b/site/doc/pages/dirname.txt index 469d20e..896aee2 100644 --- a/site/doc/pages/dirname.txt +++ b/site/doc/pages/dirname.txt @@ -2,14 +2,16 @@ String dirname(String fn) :params -fn : raw filename -return value : the directory's name +fn : a path +return value : the directory portion of the path + +:content +Returns everything in a path except the final component — i.e. the containing directory. + +:example +print(dirname("/var/www/site/index.uce"), "\n"); :see >sys - -:content -Isolates the directory component from a path or file name. - -:example -print("dirname example\n"); +basename +path_join diff --git a/site/doc/pages/error_pages.txt b/site/doc/pages/error_pages.txt index fe476fc..6287d94 100644 --- a/site/doc/pages/error_pages.txt +++ b/site/doc/pages/error_pages.txt @@ -51,6 +51,3 @@ While an error page renders, error-page handling is disabled: a compiling, broke ## Ready-made examples `site/errors/compiling.uce`, `site/errors/compiler-error.uce`, and `site/errors/runtime-error.uce` in this repository are working examples — point the config keys at them or copy them into your site. - -:example -print("error_pages example\n"); diff --git a/site/doc/pages/expand_path.txt b/site/doc/pages/expand_path.txt index 253a556..6107872 100644 --- a/site/doc/pages/expand_path.txt +++ b/site/doc/pages/expand_path.txt @@ -2,15 +2,17 @@ String expand_path(String path, String relative_to_path = "") :params -path : a relative path -relative_to_path : optional, expand relative to this path (if not given, the current path is used) -return value : expanded version of the 'path' +path : path to expand +relative_to_path : base directory a relative `path` is resolved against +return value : the expanded path + +:content +Expands a path: a relative `path` is resolved against `relative_to_path` (or the current unit's directory when that is empty), producing a normalized path you can hand to the file APIs. + +:example +print(expand_path("sub/page.uce", "/var/www/site"), "\n"); :see >sys - -:content -Converts a relative path name into an absolute path, using the current working directory as a base when `relative_to_path` is not provided. - -:example -print("expand_path example\n"); +path_join +path_real diff --git a/site/doc/pages/file_append.txt b/site/doc/pages/file_append.txt index 24dac5f..44ba8ef 100644 --- a/site/doc/pages/file_append.txt +++ b/site/doc/pages/file_append.txt @@ -2,16 +2,19 @@ void file_append(String file_name, ...val) :params -file_name : file name of file that should be written to -...val : one or more values that should be written into the file +file_name : file to append to, created if missing +...val : one or more values appended in order +return value : none + +:content +Appends one or more values to a file, creating it if needed. Each call takes an exclusive lock for the append, so concurrent appends are serialized and readers wait for them; you never manage locks yourself. + +:example +file_unlink("/tmp/doc-append.txt"); +file_append("/tmp/doc-append.txt", "a", "b", "c"); +print(file_get_contents("/tmp/doc-append.txt"), "\n"); :see >sys - -:content -Opens or creates a file and appends data to it. - -The append transparently takes an exclusive file lock for the duration of the append operation. Concurrent writers are serialized, and `file_get_contents()` waits for in-progress writes to finish; callers do not manage locks manually. - -:example -print("file_append example\n"); +file_put_contents +file_get_contents diff --git a/site/doc/pages/file_chmod.txt b/site/doc/pages/file_chmod.txt index a394b10..142ccf1 100644 --- a/site/doc/pages/file_chmod.txt +++ b/site/doc/pages/file_chmod.txt @@ -1,9 +1,19 @@ -file_chmod +:sig +bool file_chmod(String path, u32 mode) -Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync(). +:params +path : file or directory to change +mode : permission bits, e.g. 0644 or 0600 (octal) +return value : true on success + +:content +Sets a path's permission bits, like the shell `chmod`. Pass the mode as an octal literal such as `0600`. + +:example +file_put_contents("/tmp/doc-chmod.txt", "secret"); +print(file_chmod("/tmp/doc-chmod.txt", 0600) ? "mode set" : "failed", "\n"); :see >sys - -:example -print("file_chmod example\n"); +file_stat +file_put_contents diff --git a/site/doc/pages/file_close.txt b/site/doc/pages/file_close.txt index 061fcde..3dd2e15 100644 --- a/site/doc/pages/file_close.txt +++ b/site/doc/pages/file_close.txt @@ -1,11 +1,20 @@ -file_close +:sig +void file_close(u64 h) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle returned by file_open() -See also: file_get_contents, file_put_contents, file_append. +:content +Closes a file handle and releases the lock that `file_open()` took. Always close handles you open: locks are scoped to the handle's lifetime, so a leaked handle holds its lock until the worker recycles. + +:example +u64 h = file_open("/tmp/doc-close.txt", "w"); +file_write(h, "done"); +file_close(h); +print(file_exists("/tmp/doc-close.txt") ? "written and closed" : "missing", "\n"); :see >sys - -:example -print("file_close example\n"); +file_open +file_write +file_read diff --git a/site/doc/pages/file_copy.txt b/site/doc/pages/file_copy.txt index 0471fff..eb32651 100644 --- a/site/doc/pages/file_copy.txt +++ b/site/doc/pages/file_copy.txt @@ -1,9 +1,20 @@ -file_copy +:sig +bool file_copy(String from, String to) -Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error. +:params +from : source path +to : destination path (overwritten if it exists) +return value : true on success + +:content +Copies a file's contents to a new path, overwriting the destination if present. + +:example +file_put_contents("/tmp/doc-copy-src.txt", "payload"); +file_copy("/tmp/doc-copy-src.txt", "/tmp/doc-copy-dst.txt"); +print(file_get_contents("/tmp/doc-copy-dst.txt"), "\n"); :see >sys - -:example -print("file_copy example\n"); +file_rename +file_put_contents diff --git a/site/doc/pages/file_exists.txt b/site/doc/pages/file_exists.txt index 835cdd7..393ebdf 100644 --- a/site/doc/pages/file_exists.txt +++ b/site/doc/pages/file_exists.txt @@ -2,14 +2,18 @@ bool file_exists(String path) :params -path : the path name to be checked -return value : true if the file exists +path : path to check, resolved against the unit's directory +return value : true if a regular file exists at `path` + +:content +Reports whether a regular file exists. Use it to guard reads or to confirm a write or delete took effect. It is file-oriented and returns false for directories — use `file_stat()` and check `is_dir` to test for a directory. + +:example +file_put_contents("/tmp/doc-exists.txt", "x"); +print(file_exists("/tmp/doc-exists.txt") ? "exists" : "missing", " / "); +print(file_exists("/tmp/doc-not-here-xyz.txt") ? "exists" : "missing", "\n"); :see >sys - -:content -Checks whether the file or path specified by `path` exists. - -:example -print("file_exists example\n"); +file_stat +file_unlink diff --git a/site/doc/pages/file_fsync.txt b/site/doc/pages/file_fsync.txt index 056de67..95de055 100644 --- a/site/doc/pages/file_fsync.txt +++ b/site/doc/pages/file_fsync.txt @@ -1,9 +1,20 @@ -file_fsync +:sig +bool file_fsync(u64 h) -Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync(). +:params +h : handle from file_open() opened for writing +return value : true if the flush succeeded + +:content +Flushes a handle's buffered writes to disk, so the data survives a crash or power loss. Call it before `file_close()` when durability matters (e.g. after writing a checkpoint). + +:example +u64 h = file_open("/tmp/doc-fsync.txt", "w"); +file_write(h, "durable"); +print(file_fsync(h) ? "flushed to disk" : "fsync failed", "\n"); +file_close(h); :see >sys - -:example -print("file_fsync example\n"); +file_write +file_close diff --git a/site/doc/pages/file_get_contents.txt b/site/doc/pages/file_get_contents.txt index 863d89b..3af1155 100644 --- a/site/doc/pages/file_get_contents.txt +++ b/site/doc/pages/file_get_contents.txt @@ -2,18 +2,18 @@ String file_get_contents(String file_name) :params -file_name : file name of file that should be read -return value : String containing the file's contents +file_name : file to read, resolved against the unit's directory +return value : the file's contents, or "" if it cannot be read + +:content +Reads an entire file and returns it as a `String`. The read takes a shared lock for its duration, so it waits for any in-progress `file_put_contents()` / `file_append()` write to finish; you never manage locks yourself. For large files or random access, use `file_open()` + `file_read()`. + +:example +file_put_contents("/tmp/doc-get.txt", "hello"); +print(file_get_contents("/tmp/doc-get.txt"), "\n"); :see >sys - -:content -Reads the file identified by `file_name` and returns it as a `String`. - -The read transparently takes a shared file lock for the duration of the call. If another worker is writing the same file with `file_put_contents()` or `file_append()`, this read waits for that exclusive write lock to finish, so callers do not manage locks manually. - -If the file cannot be read, this function returns an empty string. - -:example -print("file_get_contents example\n"); +file_put_contents +file_append +file_open diff --git a/site/doc/pages/file_mtime.txt b/site/doc/pages/file_mtime.txt index eb48e84..836e8f4 100644 --- a/site/doc/pages/file_mtime.txt +++ b/site/doc/pages/file_mtime.txt @@ -1,16 +1,18 @@ :sig -time_t file_mtime(String file_name) +u64 file_mtime(String file_name) :params -file_name : name of the file -return value : Unix time stamp of the file's last modification +file_name : path to inspect, resolved against the unit's directory +return value : last-modified time as a Unix timestamp, or 0 if missing + +:content +Returns a file's last-modified time as a Unix timestamp. Compare two files' mtimes for cache-freshness checks, or test for `> 0` to confirm a file exists with a real timestamp. + +:example +file_put_contents("/tmp/doc-mtime.txt", "x"); +print(file_mtime("/tmp/doc-mtime.txt") > 0 ? "has a modification time" : "missing", "\n"); :see >sys ->time - -:content -Retrieves the last modification date of `file_name` as a Unix timestamp. - -:example -print("file_mtime example\n"); +file_stat +time diff --git a/site/doc/pages/file_open.txt b/site/doc/pages/file_open.txt index 4e88966..f3e5d34 100644 --- a/site/doc/pages/file_open.txt +++ b/site/doc/pages/file_open.txt @@ -1,11 +1,23 @@ -file_open +:sig +u64 file_open(String path, String mode) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +path : file to open, resolved against the unit's directory +mode : "r" read, "w" truncate+write, "a" append, "r+" read+write +return value : opaque handle; 0 means open failed (bad path, policy denial, or lock timeout) -See also: file_get_contents, file_put_contents, file_append. +:content +Opens a file for streaming, handle-based I/O and returns an opaque handle for `file_read()`, `file_write()`, `file_seek()`, and `file_close()`. Locks are automatic and scoped to the handle: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and `file_close()` releases it. Lock waits are bounded by `UCE_FILE_LOCK_TIMEOUT_MS` (default 2000ms), so a contended open returns 0 instead of blocking forever. For whole-file access prefer `file_get_contents()` / `file_put_contents()`. + +:example +u64 h = file_open("/tmp/doc-open.txt", "w"); +file_write(h, "streamed"); +file_close(h); +print(file_get_contents("/tmp/doc-open.txt"), "\n"); :see >sys - -:example -print("file_open example\n"); +file_read +file_write +file_close +file_get_contents diff --git a/site/doc/pages/file_pread.txt b/site/doc/pages/file_pread.txt index 8e2846f..39dccd4 100644 --- a/site/doc/pages/file_pread.txt +++ b/site/doc/pages/file_pread.txt @@ -1,11 +1,25 @@ -file_pread +:sig +String file_pread(u64 h, u64 offset, u64 len) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() opened for reading +offset : absolute byte offset to read from +len : maximum number of bytes to read +return value : up to `len` bytes read at `offset` -See also: file_get_contents, file_put_contents, file_append. +:content +Reads `len` bytes from an absolute `offset` without moving the handle's current offset. Useful for random access reads that interleave with sequential ones. + +:example +u64 w = file_open("/tmp/doc-pread.txt", "w"); +file_write(w, "hello world"); +file_close(w); +u64 r = file_open("/tmp/doc-pread.txt", "r"); +print(file_pread(r, 6, 5), "\n"); +file_close(r); :see >sys - -:example -print("file_pread example\n"); +file_read +file_pwrite +file_open diff --git a/site/doc/pages/file_put_contents.txt b/site/doc/pages/file_put_contents.txt index 7f783e3..2b1a281 100644 --- a/site/doc/pages/file_put_contents.txt +++ b/site/doc/pages/file_put_contents.txt @@ -2,17 +2,20 @@ bool file_put_contents(String file_name, String content) :params -file_name : file name of file that should be written -content : content that should be written -return value : true if write was successful +file_name : file to write, resolved against the unit's directory +content : bytes to write (replaces any existing content) +return value : true on success + +:content +Writes `content` to a file, replacing whatever was there. The write takes an exclusive lock for the truncate-and-write, so concurrent writers are serialized and readers wait for it; you never manage locks yourself. + +:example +file_put_contents("/tmp/doc-put.txt", "first"); +file_put_contents("/tmp/doc-put.txt", "second"); +print(file_get_contents("/tmp/doc-put.txt"), "\n"); :see >sys - -:content -Writes `content` into the file identified by `file_name`, overwriting any pre-existing content. - -The write transparently takes an exclusive file lock for the duration of the truncate-and-write operation. Concurrent writers are serialized, and `file_get_contents()` waits for in-progress writes to finish; callers do not manage locks manually. - -:example -print("file_put_contents example\n"); +file_get_contents +file_append +file_open diff --git a/site/doc/pages/file_pwrite.txt b/site/doc/pages/file_pwrite.txt index a0bc33f..33fe544 100644 --- a/site/doc/pages/file_pwrite.txt +++ b/site/doc/pages/file_pwrite.txt @@ -1,11 +1,26 @@ -file_pwrite +:sig +u64 file_pwrite(u64 h, u64 offset, String data) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() opened for writing +offset : absolute byte offset to write at +data : bytes to write +return value : number of bytes written -See also: file_get_contents, file_put_contents, file_append. +:content +Writes `data` at an absolute `offset` without moving the handle's current offset. The handle must be opened with a writable mode (`"r+"` for in-place edits). + +:example +u64 h = file_open("/tmp/doc-pwrite.txt", "w"); +file_write(h, "hello world"); +file_close(h); +u64 e = file_open("/tmp/doc-pwrite.txt", "r+"); +file_pwrite(e, 6, "earth"); +file_close(e); +print(file_get_contents("/tmp/doc-pwrite.txt"), "\n"); :see >sys - -:example -print("file_pwrite example\n"); +file_write +file_pread +file_open diff --git a/site/doc/pages/file_read.txt b/site/doc/pages/file_read.txt index 8ffc11d..202f91f 100644 --- a/site/doc/pages/file_read.txt +++ b/site/doc/pages/file_read.txt @@ -1,11 +1,25 @@ -file_read +:sig +String file_read(u64 h, u64 len) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() opened for reading +len : maximum number of bytes to read +return value : up to `len` bytes from the current offset (shorter at end of file) -See also: file_get_contents, file_put_contents, file_append. +:content +Reads up to `len` bytes starting at the handle's current offset and advances the offset. Returns fewer bytes (or `""`) at end of file. Use `file_seek()` to move the offset, or `file_pread()` to read at an absolute offset without moving it. + +:example +u64 w = file_open("/tmp/doc-read.txt", "w"); +file_write(w, "hello world"); +file_close(w); +u64 r = file_open("/tmp/doc-read.txt", "r"); +print(file_read(r, 5), "\n"); +file_close(r); :see >sys - -:example -print("file_read example\n"); +file_open +file_pread +file_seek +file_close diff --git a/site/doc/pages/file_rename.txt b/site/doc/pages/file_rename.txt index 22df104..8306157 100644 --- a/site/doc/pages/file_rename.txt +++ b/site/doc/pages/file_rename.txt @@ -1,9 +1,20 @@ -file_rename +:sig +bool file_rename(String from, String to) -Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error. +:params +from : source path +to : destination path +return value : true on success + +:content +Renames or moves a file. After a successful rename the source path no longer exists. Use it for atomic publish patterns: write a temp file, then rename it into place. + +:example +file_put_contents("/tmp/doc-rename-a.txt", "moved"); +file_rename("/tmp/doc-rename-a.txt", "/tmp/doc-rename-b.txt"); +print(file_get_contents("/tmp/doc-rename-b.txt"), " / source ", file_exists("/tmp/doc-rename-a.txt") ? "exists" : "gone", "\n"); :see >sys - -:example -print("file_rename example\n"); +file_copy +file_temp diff --git a/site/doc/pages/file_seek.txt b/site/doc/pages/file_seek.txt index adff054..883775c 100644 --- a/site/doc/pages/file_seek.txt +++ b/site/doc/pages/file_seek.txt @@ -1,11 +1,26 @@ -file_seek +:sig +s64 file_seek(u64 h, s64 offset, s64 whence) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() +offset : byte offset relative to whence +whence : 0 from start, 1 from current, 2 from end +return value : the new absolute offset, or -1 on error -See also: file_get_contents, file_put_contents, file_append. +:content +Moves a handle's read/write offset for random access. Combine with `file_read()` / `file_write()` to jump around a file; `file_tell()` reports the current offset. + +:example +u64 w = file_open("/tmp/doc-seek.txt", "w"); +file_write(w, "hello world"); +file_close(w); +u64 r = file_open("/tmp/doc-seek.txt", "r"); +file_seek(r, 6, 0); +print(file_read(r, 5), "\n"); +file_close(r); :see >sys - -:example -print("file_seek example\n"); +file_tell +file_read +file_open diff --git a/site/doc/pages/file_stat.txt b/site/doc/pages/file_stat.txt index 73ecbcd..80fc7a6 100644 --- a/site/doc/pages/file_stat.txt +++ b/site/doc/pages/file_stat.txt @@ -1,9 +1,20 @@ +:sig DValue file_stat(String path) -Returns `{ exists, size, mtime, ctime, mode, is_dir, is_file, is_symlink }` for a policy-gated path. Missing or denied paths return `exists=false`. +:params +path : path to inspect, resolved against the unit's directory +return value : a map with exists, is_file, is_dir, is_symlink, size, mode, mtime, ctime + +:content +Returns metadata about a path as a `DValue` map. Read fields with the usual accessors, e.g. `st["size"].to_u64()` and `st["is_file"].to_bool()`. When the path does not exist, `exists` is false. + +:example +file_put_contents("/tmp/doc-stat.txt", "12345"); +DValue st = file_stat("/tmp/doc-stat.txt"); +print("size=", st["size"].to_u64(), " is_file=", st["is_file"].to_bool() ? "yes" : "no", "\n"); :see >sys - -:example -print("file_stat example\n"); +dir_list +file_exists +file_mtime diff --git a/site/doc/pages/file_symlink.txt b/site/doc/pages/file_symlink.txt index 261358e..49ac332 100644 --- a/site/doc/pages/file_symlink.txt +++ b/site/doc/pages/file_symlink.txt @@ -1,9 +1,21 @@ -file_symlink +:sig +bool file_symlink(String target, String linkpath) -Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync(). +:params +target : path the symlink should point at +linkpath : symlink to create +return value : true on success + +:content +Creates a symbolic link at `linkpath` pointing to `target`. Reading through the link reads the target; use `path_real()` to resolve a link to its canonical destination. + +:example +file_put_contents("/tmp/doc-symlink-target.txt", "data"); +file_unlink("/tmp/doc-symlink-link.txt"); +file_symlink("/tmp/doc-symlink-target.txt", "/tmp/doc-symlink-link.txt"); +print(file_get_contents("/tmp/doc-symlink-link.txt"), "\n"); :see >sys - -:example -print("file_symlink example\n"); +path_real +file_stat diff --git a/site/doc/pages/file_tell.txt b/site/doc/pages/file_tell.txt index db1545a..aba28d5 100644 --- a/site/doc/pages/file_tell.txt +++ b/site/doc/pages/file_tell.txt @@ -1,11 +1,20 @@ -file_tell +:sig +s64 file_tell(u64 h) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() +return value : the current absolute byte offset -See also: file_get_contents, file_put_contents, file_append. +:content +Returns a handle's current read/write offset, as moved by `file_read()`, `file_write()`, and `file_seek()`. + +:example +u64 h = file_open("/tmp/doc-tell.txt", "w"); +file_write(h, "hello"); +print("offset is ", file_tell(h), "\n"); +file_close(h); :see >sys - -:example -print("file_tell example\n"); +file_seek +file_open diff --git a/site/doc/pages/file_temp.txt b/site/doc/pages/file_temp.txt index ad47dbf..08153d0 100644 --- a/site/doc/pages/file_temp.txt +++ b/site/doc/pages/file_temp.txt @@ -1,9 +1,20 @@ -file_temp +:sig +String file_temp(String prefix) -Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync(). +:params +prefix : path prefix for the temp file; a unique suffix is appended +return value : the path of the newly created temp file, or "" on failure + +:content +Creates a new, uniquely named empty file using `prefix` as the leading path and returns its full path. Use it for scratch files and the write-then-rename publish pattern. + +:example +String path = file_temp("/tmp/doc-temp-"); +file_put_contents(path, "scratch"); +print(str_starts_with(path, "/tmp/doc-temp-") ? "created a temp file" : path, "\n"); +file_unlink(path); :see >sys - -:example -print("file_temp example\n"); +file_rename +file_put_contents diff --git a/site/doc/pages/file_truncate.txt b/site/doc/pages/file_truncate.txt index 5ea6f50..1913cc4 100644 --- a/site/doc/pages/file_truncate.txt +++ b/site/doc/pages/file_truncate.txt @@ -1,9 +1,20 @@ -file_truncate +:sig +bool file_truncate(String path, u64 size) -Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error. +:params +path : file to resize +size : new length in bytes +return value : true on success + +:content +Truncates (or extends) a file to exactly `size` bytes. Shrinking discards trailing bytes; growing pads with zero bytes. + +:example +file_put_contents("/tmp/doc-truncate.txt", "hello world"); +file_truncate("/tmp/doc-truncate.txt", 5); +print(file_get_contents("/tmp/doc-truncate.txt"), "\n"); :see >sys - -:example -print("file_truncate example\n"); +file_put_contents +file_stat diff --git a/site/doc/pages/file_unlink.txt b/site/doc/pages/file_unlink.txt index 0825508..2bede2d 100644 --- a/site/doc/pages/file_unlink.txt +++ b/site/doc/pages/file_unlink.txt @@ -2,13 +2,18 @@ void file_unlink(String file_name) :params -file_name : name of the file +file_name : file to delete, resolved against the unit's directory +return value : none + +:content +Deletes a file. Deleting a path that does not exist is a no-op, so it is safe to call before recreating a file. + +:example +file_put_contents("/tmp/doc-unlink.txt", "x"); +file_unlink("/tmp/doc-unlink.txt"); +print(file_exists("/tmp/doc-unlink.txt") ? "exists" : "gone", "\n"); :see >sys - -:content -Deletes the file identified by `file_name`. - -:example -print("file_unlink example\n"); +file_exists +dir_remove diff --git a/site/doc/pages/file_write.txt b/site/doc/pages/file_write.txt index 69546fa..cdece45 100644 --- a/site/doc/pages/file_write.txt +++ b/site/doc/pages/file_write.txt @@ -1,11 +1,23 @@ -file_write +:sig +u64 file_write(u64 h, String data) -Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms). +:params +h : handle from file_open() opened for writing +data : bytes to write at the current offset +return value : number of bytes written -See also: file_get_contents, file_put_contents, file_append. +:content +Writes `data` at the handle's current offset and advances the offset. The handle must come from `file_open()` with a writable mode (`"w"`, `"a"`, or `"r+"`). Binary-safe: `data` may contain NUL and other non-text bytes. + +:example +u64 h = file_open("/tmp/doc-write.txt", "w"); +u64 n = file_write(h, "hello world"); +file_close(h); +print(n, " bytes -> ", file_get_contents("/tmp/doc-write.txt"), "\n"); :see >sys - -:example -print("file_write example\n"); +file_open +file_pwrite +file_read +file_close diff --git a/site/doc/pages/float_val.txt b/site/doc/pages/float_val.txt index 01f68cf..e48fde7 100644 --- a/site/doc/pages/float_val.txt +++ b/site/doc/pages/float_val.txt @@ -16,4 +16,4 @@ Extracts a floating point number from a `String`. If no usable number can be identified, the result is `0`. :example -print("float_val example\n"); +print(float_val("3.14") + 1.0, "\n"); diff --git a/site/doc/pages/gz_compress.txt b/site/doc/pages/gz_compress.txt index 52c3164..aa923a0 100644 --- a/site/doc/pages/gz_compress.txt +++ b/site/doc/pages/gz_compress.txt @@ -20,4 +20,6 @@ The result is binary data. Store it in a file, send it with an appropriate conte UCE writes a standard gzip wrapper around a deflate stream, including CRC32 and uncompressed-size footer fields. :example -print("gz_compress example\n"); +String original = "hello hello hello hello hello"; +String packed = gz_compress(original); +print(original.length(), " bytes -> ", packed.length(), " bytes; restores to: ", gz_uncompress(packed), "\n"); diff --git a/site/doc/pages/gz_uncompress.txt b/site/doc/pages/gz_uncompress.txt index 20ae5da..0756b74 100644 --- a/site/doc/pages/gz_uncompress.txt +++ b/site/doc/pages/gz_uncompress.txt @@ -18,4 +18,5 @@ Uncompresses a gzip-format byte string and returns the original content. `gz_uncompress()` validates the gzip header, CRC32 footer, and uncompressed-size footer. It throws a runtime error when the input is not a supported gzip stream or fails validation. :example -print("gz_uncompress example\n"); +String packed = gz_compress("round trip data"); +print(gz_uncompress(packed), "\n"); diff --git a/site/doc/pages/hmac_sha256.txt b/site/doc/pages/hmac_sha256.txt index 55f8790..cdf7bc5 100644 --- a/site/doc/pages/hmac_sha256.txt +++ b/site/doc/pages/hmac_sha256.txt @@ -1,10 +1,19 @@ -# hmac_sha256 +:sig +String hmac_sha256(String key, String data) +:params +key : secret key +data : message to authenticate +return value : the raw 32-byte HMAC-SHA256 -Returns the raw 32-byte HMAC-SHA-256 digest for `data` keyed by `key`. +:content +Computes an HMAC-SHA256 and returns it as raw bytes. Prefer `hmac_sha256_hex()` for printable signatures; use the raw form for binary protocols. :example -print(hmac_sha256("key", "data").length(), "\n"); +print(hmac_sha256("key", "message").length(), " raw bytes, hex = ", hmac_sha256_hex("key", "message"), "\n"); :see ->noise +>sys +hmac_sha256_hex +sha256 +crypto_equal diff --git a/site/doc/pages/hmac_sha256_hex.txt b/site/doc/pages/hmac_sha256_hex.txt index 0a9e918..6d9ae20 100644 --- a/site/doc/pages/hmac_sha256_hex.txt +++ b/site/doc/pages/hmac_sha256_hex.txt @@ -1,10 +1,19 @@ -# hmac_sha256_hex +:sig +String hmac_sha256_hex(String key, String data) +:params +key : secret key +data : message to authenticate +return value : the HMAC-SHA256 as a 64-character lowercase hex string -Returns the lowercase hexadecimal HMAC-SHA-256 digest. +:content +Computes an HMAC-SHA256 message authentication code and returns it as hex. Use it to sign tokens, cookies, and webhook payloads, then verify with `crypto_equal()` to avoid timing leaks. :example -print(hmac_sha256_hex("key", "data"), "\n"); +print(hmac_sha256_hex("key", "The quick brown fox jumps over the lazy dog"), "\n"); :see ->noise +>sys +hmac_sha256 +sha256_hex +crypto_equal diff --git a/site/doc/pages/html_escape.txt b/site/doc/pages/html_escape.txt index 30881eb..6cc7a9d 100644 --- a/site/doc/pages/html_escape.txt +++ b/site/doc/pages/html_escape.txt @@ -20,4 +20,4 @@ Returns a version of the input string where special HTML characters are replaced - `"` becomes `"` :example -print("html_escape example\n"); +print(html_escape("Tom & Jerry"), "\n"); diff --git a/site/doc/pages/http_request.txt b/site/doc/pages/http_request.txt index eaf344c..05b9f5c 100644 --- a/site/doc/pages/http_request.txt +++ b/site/doc/pages/http_request.txt @@ -12,4 +12,7 @@ Returns `{ status, headers, body, error }`. `headers` is a name/value map. A mis >socket :example -print("http_request is resource-bound; configure the service or connection, then call it in request code.\n"); +DValue req; req["method"] = "GET"; req["url"] = "http://127.0.0.1/doc/index.uce"; +req["headers"]["Host"] = "uce.openfu.com"; req["timeout_ms"] = (f64)2000; +DValue resp = http_request(req); +print("HTTP ", resp["status"].to_u64(), ", ", resp["body"].to_string().length(), " bytes returned\n"); diff --git a/site/doc/pages/http_request_async.txt b/site/doc/pages/http_request_async.txt index ed0bd2c..ab9b17d 100644 --- a/site/doc/pages/http_request_async.txt +++ b/site/doc/pages/http_request_async.txt @@ -10,4 +10,8 @@ Starts the same bounded curl-backed request as `http_request()` in the file-back >socket :example -print("http_request_async is resource-bound; configure the service or connection, then call it in request code.\n"); +DValue req; req["method"] = "GET"; req["url"] = "http://127.0.0.1/doc/index.uce"; +req["headers"]["Host"] = "uce.openfu.com"; req["timeout_ms"] = (f64)2000; +u64 job = http_request_async(req); +DValue done = job_await(job, 3000); +print("async HTTP ", done["result"]["status"].to_u64(), "\n"); diff --git a/site/doc/pages/int_val.txt b/site/doc/pages/int_val.txt index f0223b7..fa16af7 100644 --- a/site/doc/pages/int_val.txt +++ b/site/doc/pages/int_val.txt @@ -17,4 +17,4 @@ Extracts an integer value from `s`. The `base` argument controls which number system is used while parsing. If no usable number can be identified, the function returns `0`. :example -print("int_val example\n"); +print(int_val("42"), " / ", int_val("ff", 16), "\n"); diff --git a/site/doc/pages/job_await.txt b/site/doc/pages/job_await.txt index 529ecb8..e3c83aa 100644 --- a/site/doc/pages/job_await.txt +++ b/site/doc/pages/job_await.txt @@ -7,4 +7,7 @@ Waits up to `timeout_ms` for a job to finish, then returns status/result data. T >sys :example -print("job_await example\n"); +DValue spec; spec["cmd"] = "printf hello-from-job"; spec["timeout_ms"] = (f64)1000; +u64 job = shell_spawn(spec); +DValue waited = job_await(job, 3000); +print("done=", waited["done"].to_bool() ? "true" : "false", " stdout=", waited["result"]["stdout"].to_string(), "\n"); diff --git a/site/doc/pages/job_cancel.txt b/site/doc/pages/job_cancel.txt index 6d264d8..8aa78c4 100644 --- a/site/doc/pages/job_cancel.txt +++ b/site/doc/pages/job_cancel.txt @@ -7,4 +7,6 @@ Attempts to terminate the background job process group and marks the registry en >sys :example -print("job_cancel example\n"); +DValue spec; spec["cmd"] = "sleep 5"; spec["timeout_ms"] = (f64)10000; +u64 job = shell_spawn(spec); +print(job_cancel(job) ? "job cancelled" : "could not cancel", "\n"); diff --git a/site/doc/pages/job_result.txt b/site/doc/pages/job_result.txt index d814c0c..b0b2dd3 100644 --- a/site/doc/pages/job_result.txt +++ b/site/doc/pages/job_result.txt @@ -7,4 +7,7 @@ Checks a job result with a small bounded wait. The returned value includes the c >sys :example -print("job_result example\n"); +DValue spec; spec["cmd"] = "printf result-data"; spec["timeout_ms"] = (f64)1000; +u64 job = shell_spawn(spec); +job_await(job, 3000); +print(job_result(job)["result"]["stdout"].to_string(), "\n"); diff --git a/site/doc/pages/job_status.txt b/site/doc/pages/job_status.txt index ff8fa9a..55f121f 100644 --- a/site/doc/pages/job_status.txt +++ b/site/doc/pages/job_status.txt @@ -7,4 +7,7 @@ Returns the file-backed async job state, e.g. `{ state, done, kind, pid, job_id >sys :example -print("job_status example\n"); +DValue spec; spec["cmd"] = "printf x"; spec["timeout_ms"] = (f64)1000; +u64 job = shell_spawn(spec); +job_await(job, 3000); +print("state: ", job_status(job)["state"].to_string(), "\n"); diff --git a/site/doc/pages/json_decode.txt b/site/doc/pages/json_decode.txt index f65a680..cee12b3 100644 --- a/site/doc/pages/json_decode.txt +++ b/site/doc/pages/json_decode.txt @@ -29,4 +29,5 @@ Current runtime behavior: - JSON numbers currently deserialize as string-valued `DValue` nodes, so typed conversions such as `to_f64()` and `to_u64()` are the normal way to read numeric content. :example -print("json_decode example\n"); +DValue data = json_decode("{\"name\": \"Ada\", \"tags\": [\"x\", \"y\"]}"); +print(data["name"].to_string(), " has ", data["tags"].keys().size(), " tags\n"); diff --git a/site/doc/pages/list_filter.txt b/site/doc/pages/list_filter.txt deleted file mode 100644 index f146029..0000000 --- a/site/doc/pages/list_filter.txt +++ /dev/null @@ -1,15 +0,0 @@ -:sig -Use filter(StringList items, function f) - -:see -filter -0_StringList - -:content -`list_filter()` was removed because `StringList` is `std::vector` and the generic `filter()` helper covers the same behavior without a second implementation to maintain. - -Use: - - -:example -print("list_filter example\n"); diff --git a/site/doc/pages/list_map.txt b/site/doc/pages/list_map.txt deleted file mode 100644 index cdfc3e8..0000000 --- a/site/doc/pages/list_map.txt +++ /dev/null @@ -1,16 +0,0 @@ -:sig -Use map(StringList items, function f) - -:see -map -filter -0_StringList - -:content -`list_map()` was removed because `StringList` is `std::vector` and the generic `map()` helper covers the same behavior without a second implementation to maintain. - -Use: - - -:example -print("list_map example\n"); diff --git a/site/doc/pages/load.txt b/site/doc/pages/load.txt index 5845840..ec11054 100644 --- a/site/doc/pages/load.txt +++ b/site/doc/pages/load.txt @@ -14,4 +14,7 @@ Use `#load` when you want the current file to pull in declarations or reusable c :example -print("load example\n"); +// #load "file.uce" includes another unit at COMPILE time (a preprocessor directive, +// used at file top level). At RUNTIME, unit_render()/unit_call() invoke another unit: +unit_render("examples/sample_unit.uce"); +print("\n"); diff --git a/site/doc/pages/ls.txt b/site/doc/pages/ls.txt index 065dbca..0aaa4c4 100644 --- a/site/doc/pages/ls.txt +++ b/site/doc/pages/ls.txt @@ -2,16 +2,20 @@ StringList ls(String path) :params -path : a filesystem path -return value : list of directory entries +path : directory to list +return value : a StringList of entry names + +:content +Returns the names of the entries in a directory as a `StringList`. For names only this is simpler than `dir_list()`, which returns full metadata per entry. + +:example +mkdir("/tmp/doc-ls"); +file_put_contents("/tmp/doc-ls/one.txt", "1"); +file_put_contents("/tmp/doc-ls/two.txt", "2"); +print(join(ls("/tmp/doc-ls").sort(), ","), "\n"); +dir_remove("/tmp/doc-ls", true); :see >sys - -:content -Returns a list of files and subdirectories within `path`. - -This is a simple directory listing helper for filesystem-oriented tasks. - -:example -print("ls example\n"); +dir_list +0_StringList diff --git a/site/doc/pages/memcache_command.txt b/site/doc/pages/memcache_command.txt index a831782..2cba7dd 100644 --- a/site/doc/pages/memcache_command.txt +++ b/site/doc/pages/memcache_command.txt @@ -15,4 +15,6 @@ Executes a raw command on an open Memcache connection and returns the server res This is the low-level escape hatch for Memcache operations that are not covered by the dedicated helpers. :example -print("memcache_command is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +if(conn != 0) print(memcache_command(conn, "stats") != "" ? "stats command returned data" : "no data", "\n"); +else print("(requires a reachable memcached server)\n"); diff --git a/site/doc/pages/memcache_connect.txt b/site/doc/pages/memcache_connect.txt index 1009ebe..fc9f45b 100644 --- a/site/doc/pages/memcache_connect.txt +++ b/site/doc/pages/memcache_connect.txt @@ -1,5 +1,5 @@ :sig -u64 memcache_connect(String host = "127.0.0.1", short port = 11211) +u64 memcache_connect(String host = "127.0.0.1", u16 port = 11211) :params host : optional host name of the memcache server, defaults to local address 127.0.0.1 @@ -15,4 +15,5 @@ Connects to a Memcache server instance. If the connection fails, the function returns `-1`. :example -print("memcache_connect is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +print(conn != 0 ? "connected to memcached" : "no memcached server reachable", "\n"); diff --git a/site/doc/pages/memcache_delete.txt b/site/doc/pages/memcache_delete.txt index 1a343cd..548775b 100644 --- a/site/doc/pages/memcache_delete.txt +++ b/site/doc/pages/memcache_delete.txt @@ -15,4 +15,11 @@ Deletes the entry identified by `key`. The return value is `true` when the operation succeeds. :example -print("memcache_delete is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +if(conn != 0) +{ + memcache_set(conn, "doc_del_key", "v"); + memcache_delete(conn, "doc_del_key"); + print(memcache_get(conn, "doc_del_key", "gone"), "\n"); +} +else print("(requires a reachable memcached server)\n"); diff --git a/site/doc/pages/memcache_escape_key.txt b/site/doc/pages/memcache_escape_key.txt index 421b973..23e899d 100644 --- a/site/doc/pages/memcache_escape_key.txt +++ b/site/doc/pages/memcache_escape_key.txt @@ -13,4 +13,4 @@ return value : memcache-safe key string Normalizes whitespace in a memcache key to underscores before it is placed into a text protocol command. :example -print("memcache_escape_key is resource-bound; configure the service or connection, then call it in request code.\n"); +print(memcache_escape_key("a key with spaces"), "\n"); diff --git a/site/doc/pages/memcache_escape_keys.txt b/site/doc/pages/memcache_escape_keys.txt index 50c5a3b..f821aba 100644 --- a/site/doc/pages/memcache_escape_keys.txt +++ b/site/doc/pages/memcache_escape_keys.txt @@ -13,4 +13,7 @@ return value : escaped keys Applies `memcache_escape_key()` to each key in a list. :example -print("memcache_escape_keys is resource-bound; configure the service or connection, then call it in request code.\n"); +StringList keys; +keys.push_back("a b"); +keys.push_back("c\td"); +print(join(memcache_escape_keys(keys), ", "), "\n"); diff --git a/site/doc/pages/memcache_get.txt b/site/doc/pages/memcache_get.txt index 77f0eb6..e9ebfd9 100644 --- a/site/doc/pages/memcache_get.txt +++ b/site/doc/pages/memcache_get.txt @@ -16,4 +16,6 @@ Retrieves a value from an existing Memcache connection. If the key is missing, `default_value` is returned instead. :example -print("memcache_get is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +if(conn != 0) { memcache_set(conn, "doc_get_key", "stored value"); print(memcache_get(conn, "doc_get_key"), "\n"); } +else print("(requires a reachable memcached server)\n"); diff --git a/site/doc/pages/memcache_get_multiple.txt b/site/doc/pages/memcache_get_multiple.txt index f9ee9e0..24ba58f 100644 --- a/site/doc/pages/memcache_get_multiple.txt +++ b/site/doc/pages/memcache_get_multiple.txt @@ -15,4 +15,13 @@ Retrieves multiple entries in one call. The result is returned as a `StringMap` keyed by the requested Memcache keys. :example -print("memcache_get_multiple is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +if(conn != 0) +{ + memcache_set(conn, "doc_k1", "v1"); + memcache_set(conn, "doc_k2", "v2"); + StringList keys; keys.push_back("doc_k1"); keys.push_back("doc_k2"); + StringMap vals = memcache_get_multiple(conn, keys); + print(vals["doc_k1"], " / ", vals["doc_k2"], "\n"); +} +else print("(requires a reachable memcached server)\n"); diff --git a/site/doc/pages/memcache_set.txt b/site/doc/pages/memcache_set.txt index fb18cb8..cb22cfa 100644 --- a/site/doc/pages/memcache_set.txt +++ b/site/doc/pages/memcache_set.txt @@ -17,4 +17,6 @@ Stores `value` under `key` on the Memcache server. `expires_in` controls the expiration timeout and defaults to one hour. :example -print("memcache_set is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 conn = memcache_connect(); +if(conn != 0) print(memcache_set(conn, "doc_set_key", "value") ? "stored" : "store failed", "\n"); +else print("(requires a reachable memcached server)\n"); diff --git a/site/doc/pages/mkdir.txt b/site/doc/pages/mkdir.txt index 861e09d..04fba47 100644 --- a/site/doc/pages/mkdir.txt +++ b/site/doc/pages/mkdir.txt @@ -2,16 +2,19 @@ bool mkdir(String path) :params -path : the path name to be created -return value : returns true if the directory was successfully created +path : directory to create +return value : true on success + +:content +Creates a single directory and returns whether it succeeded. The parent directory must already exist, so build nested paths one level at a time. + +:example +dir_remove("/tmp/doc-mkdir-demo", true); +mkdir("/tmp/doc-mkdir-demo"); +print(file_stat("/tmp/doc-mkdir-demo")["is_dir"].to_bool() ? "created" : "not created", "\n"); +dir_remove("/tmp/doc-mkdir-demo", true); :see >sys - -:content -Creates the directory named by `path`. - -The function returns `true` when the directory was created successfully. - -:example -print("mkdir example\n"); +dir_remove +dir_list diff --git a/site/doc/pages/mysql_connect.txt b/site/doc/pages/mysql_connect.txt index 5e8114f..087c4d5 100644 --- a/site/doc/pages/mysql_connect.txt +++ b/site/doc/pages/mysql_connect.txt @@ -16,4 +16,6 @@ Establishes a connection to a MySQL server and returns a pointer to the connecti This connection handle is then used with helpers such as `mysql_query()`, `mysql_error()`, and `mysql_disconnect()`. :example -print("mysql_connect is resource-bound; configure the service or connection, then call it in request code.\n"); +MySQL* db = mysql_connect(); +print(db != 0 ? "connected to MySQL" : "no MySQL server reachable with these credentials", "\n"); +if(db != 0) mysql_disconnect(db); diff --git a/site/doc/pages/mysql_disconnect.txt b/site/doc/pages/mysql_disconnect.txt index dd93945..a3b657f 100644 --- a/site/doc/pages/mysql_disconnect.txt +++ b/site/doc/pages/mysql_disconnect.txt @@ -13,4 +13,6 @@ Closes an existing connection to a MySQL server. Call this when you are done using a `MySQL*` connection handle. :example -print("mysql_disconnect is resource-bound; configure the service or connection, then call it in request code.\n"); +MySQL* db = mysql_connect(); +if(db != 0) { mysql_disconnect(db); print("connection closed\n"); } +else print("(requires a reachable MySQL server)\n"); diff --git a/site/doc/pages/mysql_error.txt b/site/doc/pages/mysql_error.txt index dc06f54..d4787c2 100644 --- a/site/doc/pages/mysql_error.txt +++ b/site/doc/pages/mysql_error.txt @@ -14,4 +14,11 @@ Returns the last error message associated with the given MySQL connection. If there is no current error, the result is an empty string. :example -print("mysql_error is resource-bound; configure the service or connection, then call it in request code.\n"); +MySQL* db = mysql_connect(); +if(db != 0) +{ + mysql_query(db, "select * from no_such_table_xyz"); + print(mysql_error(db) != "" ? "error reported" : "no error", "\n"); + mysql_disconnect(db); +} +else print("(requires a reachable MySQL server)\n"); diff --git a/site/doc/pages/mysql_escape.txt b/site/doc/pages/mysql_escape.txt index 4d77c87..d9ce740 100644 --- a/site/doc/pages/mysql_escape.txt +++ b/site/doc/pages/mysql_escape.txt @@ -15,4 +15,4 @@ Escapes a string so it can be used safely as a value inside an SQL expression. If `quote_char` is provided, the escaped result is wrapped with that quote character. Pass `NULL` when you only want escaping without wrapping. :example -print("mysql_escape is resource-bound; configure the service or connection, then call it in request code.\n"); +print(mysql_escape("O'Brien; DROP TABLE users", '\''), "\n"); diff --git a/site/doc/pages/mysql_insert_id.txt b/site/doc/pages/mysql_insert_id.txt index d14d00b..eec30ba 100644 --- a/site/doc/pages/mysql_insert_id.txt +++ b/site/doc/pages/mysql_insert_id.txt @@ -14,4 +14,11 @@ Returns the last automatically assigned row ID used by the current MySQL connect This is typically used after an `INSERT` into a table with an `AUTO_INCREMENT` primary key. :example -print("mysql_insert_id is resource-bound; configure the service or connection, then call it in request code.\n"); +MySQL* db = mysql_connect(); +if(db != 0) +{ + DValue row = mysql_query(db, "select last_insert_id() as id"); + print("mysql_insert_id() returns the last AUTO_INCREMENT id for this connection (currently ", mysql_insert_id(db), ")\n"); + mysql_disconnect(db); +} +else print("(requires a reachable MySQL server)\n"); diff --git a/site/doc/pages/mysql_query.txt b/site/doc/pages/mysql_query.txt index bc5e77e..cbd040b 100644 --- a/site/doc/pages/mysql_query.txt +++ b/site/doc/pages/mysql_query.txt @@ -27,4 +27,13 @@ DValue rows = mysql_query(m, The result is returned as a `DValue`, which makes it easy to iterate through rows and read fields with the usual `DValue` accessors. :example -print("mysql_query is resource-bound; configure the service or connection, then call it in request code.\n"); +MySQL* db = mysql_connect(); +if(db != 0) +{ + DValue rows = mysql_query(db, "select 'ada@example.test' as email, 1 + 1 as total"); + String email = "none"; String total = "?"; + rows.each([&](DValue r, String key) { email = r["email"].to_string(); total = r["total"].to_string(); }); + print(email, " / total=", total, "\n"); + mysql_disconnect(db); +} +else print("(requires a reachable MySQL server)\n"); diff --git a/site/doc/pages/ob_close.txt b/site/doc/pages/ob_close.txt index 25e3c29..e2df418 100644 --- a/site/doc/pages/ob_close.txt +++ b/site/doc/pages/ob_close.txt @@ -2,15 +2,18 @@ void ob_close() :params -(none) +return value : none + +:content +Discards the current output buffer and its contents, switching back to the previous buffer on the stack. Use it when you captured output only to throw it away (e.g. rendering for a side effect). + +:example +ob_start(); +print("this output is discarded"); +ob_close(); +print("the buffered text never reached the response\n"); :see >ob - -:content -Discards the current output buffer. - -If more output buffers remain on the stack, UCE switches to the next one. - -:example -print("ob_close example\n"); +ob_get_close +ob_start diff --git a/site/doc/pages/ob_get.txt b/site/doc/pages/ob_get.txt index 93605af..3da9b78 100644 --- a/site/doc/pages/ob_get.txt +++ b/site/doc/pages/ob_get.txt @@ -2,15 +2,19 @@ String ob_get() :params -return value : content of the current output buffer +return value : the current buffer's contents + +:content +Returns the current output buffer's contents WITHOUT discarding the buffer, so more output can still be appended. Use `ob_get_close()` when you also want to pop the buffer. + +:example +ob_start(); +print("partial"); +String snapshot = ob_get(); +ob_close(); +print("snapshot was: ", snapshot, "\n"); :see >ob - -:content -Returns the contents of the current output buffer. - -Unlike `ob_get_close()`, this does not discard the buffer. - -:example -print("ob_get example\n"); +ob_get_close +ob_start diff --git a/site/doc/pages/ob_get_close.txt b/site/doc/pages/ob_get_close.txt index c801ca3..a3ee9ca 100644 --- a/site/doc/pages/ob_get_close.txt +++ b/site/doc/pages/ob_get_close.txt @@ -2,15 +2,20 @@ String ob_get_close() :params -return value : content of the current output buffer +return value : the current buffer's contents + +:content +Returns the current output buffer's contents and discards the buffer, switching back to the previous one on the stack. This is the usual way to capture rendered output for post-processing. + +:example +ob_start(); +print("hello "); +print("world"); +String html = ob_get_close(); +print("captured ", html.length(), " chars: ", html, "\n"); :see >ob - -:content -Returns the contents of the current output buffer and then discards that buffer. - -If more output buffers remain on the stack, UCE switches to the next one. - -:example -print("ob_get_close example\n"); +ob_start +ob_get +ob_close diff --git a/site/doc/pages/ob_start.txt b/site/doc/pages/ob_start.txt index c34a7af..83615ef 100644 --- a/site/doc/pages/ob_start.txt +++ b/site/doc/pages/ob_start.txt @@ -2,15 +2,19 @@ void ob_start() :params -(none) +return value : none + +:content +Starts a new output buffer. Subsequent `print()` output is captured into it instead of the response until you collect it with `ob_get_close()` (or discard it with `ob_close()`). Buffers nest: each `ob_start()` pushes another buffer onto the stack. Use it to render a fragment, post-process it, then emit the result. + +:example +ob_start(); +print("buffered text"); +String captured = ob_get_close(); +print("captured: ", captured, "\n"); :see >ob - -:content -Starts a new output buffer. - -All subsequent output is directed into that buffer until it is closed or collected. Every call to `ob_start()` pushes another buffer onto the output buffer stack. `ob_close()` and `ob_get_close()` destroy buffers and remove them from the stack. - -:example -print("ob_start example\n"); +ob_get_close +ob_get +ob_close diff --git a/site/doc/pages/path_is_within.txt b/site/doc/pages/path_is_within.txt index b72b122..89162d1 100644 --- a/site/doc/pages/path_is_within.txt +++ b/site/doc/pages/path_is_within.txt @@ -3,15 +3,20 @@ bool path_is_within(String path, String root) :params path : path to test -root : containing directory -return value : true when both paths canonicalize and `path` is equal to or inside `root` +root : directory that should contain it +return value : true if `path` is inside `root` + +:content +Reports whether `path` lies inside `root`. It resolves both paths to their canonical form (like `path_real()`), so they must exist and `..` traversal is collapsed first. Use it to reject path-traversal attempts before opening user-supplied paths. + +:example +mkdir("/tmp/doc-within"); +file_put_contents("/tmp/doc-within/app.txt", "x"); +print(path_is_within("/tmp/doc-within/app.txt", "/tmp/doc-within") ? "inside" : "outside", " / "); +print(path_is_within("/tmp/doc-within/../doc-within/app.txt", "/tmp/doc-within") ? "inside" : "outside", "\n"); +dir_remove("/tmp/doc-within", true); :see >sys ->path_real - -:content -Performs canonical containment on the host. It resolves `..`, symlinks, and trailing slash differences before comparing, so `/foo/bar2` is not considered inside `/foo/bar`. - -:example -print("path_is_within example\n"); +path_real +expand_path diff --git a/site/doc/pages/path_join.txt b/site/doc/pages/path_join.txt index 5ab9aa3..f3daef4 100644 --- a/site/doc/pages/path_join.txt +++ b/site/doc/pages/path_join.txt @@ -2,17 +2,18 @@ String path_join(String base, String child) :params -base : parent path -child : child path or absolute override -return value : combined path +base : base path +child : path to append +return value : the two joined with a single separator + +:content +Joins two path fragments with exactly one separator, regardless of whether `base` ends in a slash or `child` begins with one. Use it instead of string concatenation to build paths safely. + +:example +print(path_join("/var/www", "site/index.uce"), "\n"); :see >sys - -:content -Joins two filesystem-style path fragments with a single `/` when needed. - -If `child` is empty, `base` is returned. If `child` already starts with `/`, it is returned unchanged. That makes `path_join()` a better fit for app-level path assembly than open-coded string concatenation. - -:example -print("path_join example\n"); +basename +dirname +path_real diff --git a/site/doc/pages/path_real.txt b/site/doc/pages/path_real.txt index 75ae72a..7cbefdd 100644 --- a/site/doc/pages/path_real.txt +++ b/site/doc/pages/path_real.txt @@ -2,15 +2,18 @@ String path_real(String path) :params -path : filesystem path -return value : canonical absolute path, or an empty string when the path cannot be resolved +path : path to resolve +return value : the canonical absolute path, or "" if it cannot be resolved + +:content +Resolves a path to its canonical absolute form, following symlinks and collapsing `.` and `..` segments. The path must exist. Combine with `path_is_within()` to validate that user-supplied paths stay inside an allowed root. + +:example +file_put_contents("/tmp/doc-real.txt", "x"); +print(path_real("/tmp/../tmp/./doc-real.txt"), "\n"); :see >sys ->path_is_within - -:content -Returns the host canonical path using `realpath()`. Relative paths resolve according to the worker process current directory. - -:example -print("path_real example\n"); +path_is_within +expand_path +file_symlink diff --git a/site/doc/pages/process_start_directory.txt b/site/doc/pages/process_start_directory.txt index 2c48b17..2f9c366 100644 --- a/site/doc/pages/process_start_directory.txt +++ b/site/doc/pages/process_start_directory.txt @@ -13,4 +13,4 @@ return value : worker process start directory Returns the directory captured when the native process started. It remains stable even if `cwd_set()` changes the current working directory. :example -print("process_start_directory example\n"); +print(process_start_directory() != "" ? "have a start directory" : "none", "\n"); diff --git a/site/doc/pages/random_bytes.txt b/site/doc/pages/random_bytes.txt index 5334f8e..c64a69b 100644 --- a/site/doc/pages/random_bytes.txt +++ b/site/doc/pages/random_bytes.txt @@ -1,11 +1,18 @@ -# random_bytes +:sig +String random_bytes(u64 n) +:params +n : number of bytes to generate +return value : `n` cryptographically random bytes -Returns up to `n` bytes from the host CSPRNG. Requests are capped to a bounded size. +:content +Returns `n` cryptographically secure random bytes, suitable for session IDs, tokens, and salts. The bytes are binary; wrap them with `base64_encode()` or `sha256_hex()` when you need printable text. :example -String bytes = random_bytes(4); -print(bytes.length(), " bytes\n"); +String token = base64_encode(random_bytes(16)); +print(random_bytes(16).length(), " raw bytes; as base64 token: ", token.length(), " chars\n"); :see ->noise +>sys +base64_encode +sha256_hex diff --git a/site/doc/pages/request_perf.txt b/site/doc/pages/request_perf.txt index da60b55..32c3a64 100644 --- a/site/doc/pages/request_perf.txt +++ b/site/doc/pages/request_perf.txt @@ -12,4 +12,5 @@ return value : performance snapshot for the active request/workspace Returns a DValue with timing and process metadata such as worker pid, parent pid, request count, request start times, and workspace birth timing when available. :example -print("request_perf example\n"); +DValue perf = request_perf(); +print(perf.get_type_name() != "invalid" ? "captured a timing snapshot" : "no data", "\n"); diff --git a/site/doc/pages/request_route_from_raw_path.txt b/site/doc/pages/request_route_from_raw_path.txt index 1631752..28c1eed 100644 --- a/site/doc/pages/request_route_from_raw_path.txt +++ b/site/doc/pages/request_route_from_raw_path.txt @@ -14,4 +14,5 @@ return value : route metadata DValue Normalizes and validates a raw route path. The returned DValue includes route fields used by request context population, including sanitized path and validity metadata. :example -print("request_route_from_raw_path example\n"); +DValue route = request_route_from_raw_path("blog/2024/hello-world"); +print("page=", route["page"].to_string(), " valid=", route["valid"].to_bool() ? "yes" : "no", "\n"); diff --git a/site/doc/pages/runtime_safe_key.txt b/site/doc/pages/runtime_safe_key.txt index a94057d..dcc06b0 100644 --- a/site/doc/pages/runtime_safe_key.txt +++ b/site/doc/pages/runtime_safe_key.txt @@ -14,4 +14,4 @@ return value : stable SHA-1 key, or an empty string for empty input in wasm Trims a runtime key and converts it to a stable SHA-1 identifier suitable for task/runtime file names. :example -print("runtime_safe_key example\n"); +print(runtime_safe_key(" My Task Name "), "\n"); diff --git a/site/doc/pages/safe_name.txt b/site/doc/pages/safe_name.txt index 1789cb6..2a29e9e 100644 --- a/site/doc/pages/safe_name.txt +++ b/site/doc/pages/safe_name.txt @@ -2,15 +2,16 @@ String safe_name(String raw) :params -raw : arbitrary display/name text -return value : normalized safe name +raw : arbitrary input string +return value : a sanitized name safe to use as a file or key name + +:content +Sanitizes a string into a safe name by normalizing or stripping characters that are unsafe in file names and identifiers (whitespace, separators, control characters). Use it before turning user input into a file name or cache key. + +:example +print(safe_name("My Report: 2024/Q1!"), "\n"); :see >string ->ascii_safe_name - -:content -Returns a filesystem/identifier-friendly name while preserving more Unicode input than `ascii_safe_name()` where supported. - -:example -print("safe_name example\n"); +ascii_safe_name +runtime_safe_key diff --git a/site/doc/pages/server_start_http.txt b/site/doc/pages/server_start_http.txt index b2508f1..dc6fc3e 100644 --- a/site/doc/pages/server_start_http.txt +++ b/site/doc/pages/server_start_http.txt @@ -38,4 +38,6 @@ SERVE_HTTP:admin(Request* req) The custom server dispatcher uses the same nonblocking HTTP listener machinery as the built-in HTTP/WebSocket path, and hands request/response data through the normal `Request` object. Handler code may block in the first implementation; future worker dispatch can be added without changing this API. :example -print("server_start_http is resource-bound; configure the service or connection, then call it in request code.\n"); +pid_t pid = server_start_http("doc-demo-server", "/tmp/uce/custom-servers/doc-demo.sock", "examples/sample_unit.uce", "RENDER"); +print(pid > 0 ? "custom HTTP server started" : "start failed", "\n"); +server_stop("doc-demo-server"); diff --git a/site/doc/pages/server_stop.txt b/site/doc/pages/server_stop.txt index 6fab4b6..be692e3 100644 --- a/site/doc/pages/server_stop.txt +++ b/site/doc/pages/server_stop.txt @@ -19,4 +19,5 @@ server_stop("site-tests-http"); `server_stop()` currently targets custom servers started through `server_start_http()`. The same key model is intended to extend to future `server_start_tcp()` and `server_start_udp()` helpers. :example -print("server_stop is resource-bound; configure the service or connection, then call it in request code.\n"); +server_start_http("doc-demo-stop", "/tmp/uce/custom-servers/doc-stop.sock", "examples/sample_unit.uce", "RENDER"); +print(server_stop("doc-demo-stop") ? "server stopped" : "no such server", "\n"); diff --git a/site/doc/pages/sha256.txt b/site/doc/pages/sha256.txt index 00412fd..f0bcc6e 100644 --- a/site/doc/pages/sha256.txt +++ b/site/doc/pages/sha256.txt @@ -1,10 +1,17 @@ -# sha256 +:sig +String sha256(String data) +:params +data : bytes to hash +return value : the raw 32-byte SHA-256 digest -Returns the raw 32-byte SHA-256 digest for `data`. Use `sha256_hex()` for printable lowercase hex. +:content +Returns the SHA-256 digest of `data` as raw bytes. Prefer `sha256_hex()` when you need printable text; use the raw form when feeding the digest into other binary operations. :example -print(sha256("hello").length(), "\n"); +print(sha256("abc").length(), " raw bytes, hex = ", sha256_hex("abc"), "\n"); :see ->noise +>sys +sha256_hex +hmac_sha256 diff --git a/site/doc/pages/sha256_hex.txt b/site/doc/pages/sha256_hex.txt index fbf8d2b..d00ec13 100644 --- a/site/doc/pages/sha256_hex.txt +++ b/site/doc/pages/sha256_hex.txt @@ -1,10 +1,18 @@ -# sha256_hex +:sig +String sha256_hex(String data) +:params +data : bytes to hash +return value : the SHA-256 digest as a 64-character lowercase hex string -Returns the lowercase hexadecimal SHA-256 digest for `data`. +:content +Returns the SHA-256 digest of `data` as hex text. Use it for checksums, cache keys, and content fingerprints where you want a printable digest. For the raw 32-byte digest use `sha256()`. :example -print(sha256_hex("hello"), "\n"); +print(sha256_hex("abc"), "\n"); :see ->noise +>sys +sha256 +hmac_sha256_hex +gen_sha1 diff --git a/site/doc/pages/shell_escape.txt b/site/doc/pages/shell_escape.txt index fc4d308..f410ec8 100644 --- a/site/doc/pages/shell_escape.txt +++ b/site/doc/pages/shell_escape.txt @@ -14,4 +14,4 @@ Escapes a parameter so it can be used more safely with `shell_exec()`. This is the helper to reach for when building shell command lines from dynamic input. :example -print("shell_escape example\n"); +print(shell_escape("rm -rf /; echo pwned"), "\n"); diff --git a/site/doc/pages/shell_exec.txt b/site/doc/pages/shell_exec.txt index 940a150..6a08301 100644 --- a/site/doc/pages/shell_exec.txt +++ b/site/doc/pages/shell_exec.txt @@ -14,4 +14,4 @@ Executes a Linux shell command and returns the generated output. When command text includes user-controlled input, escape that input first with `shell_escape()`. :example -print("shell_exec example\n"); +print(shell_exec("echo hello from the shell")); diff --git a/site/doc/pages/shell_spawn.txt b/site/doc/pages/shell_spawn.txt index b80a7d1..ddbf58f 100644 --- a/site/doc/pages/shell_spawn.txt +++ b/site/doc/pages/shell_spawn.txt @@ -9,4 +9,7 @@ Use `job_status()`, `job_await()`, `job_result()`, or `job_cancel()` with the re >sys :example -print("shell_spawn example\n"); +DValue spec; spec["cmd"] = "printf 'spawned in the background'"; spec["timeout_ms"] = (f64)1000; +u64 job = shell_spawn(spec); +DValue done = job_await(job, 3000); +print(done["result"]["stdout"].to_string(), "\n"); diff --git a/site/doc/pages/signal_name.txt b/site/doc/pages/signal_name.txt index 13bd7de..71c6e74 100644 --- a/site/doc/pages/signal_name.txt +++ b/site/doc/pages/signal_name.txt @@ -1,5 +1,5 @@ :sig -String signal_name(int sig) +String signal_name(s32 sig) :params sig : POSIX signal number @@ -19,4 +19,4 @@ Example: This is mostly useful in diagnostics and error reporting code. :example -print("signal_name example\n"); +print(signal_name(SIGSEGV), " / ", signal_name(SIGABRT), "\n"); diff --git a/site/doc/pages/socket_close.txt b/site/doc/pages/socket_close.txt index 1960845..d267617 100644 --- a/site/doc/pages/socket_close.txt +++ b/site/doc/pages/socket_close.txt @@ -13,4 +13,6 @@ Closes an existing socket connection. Use this when you are done with a socket opened through `socket_connect()`. :example -print("socket_close is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 fd = socket_connect("127.0.0.1", 80); +print(fd != 0 ? "opened a socket" : "open failed", ", now closing it\n"); +socket_close(fd); diff --git a/site/doc/pages/socket_connect.txt b/site/doc/pages/socket_connect.txt index 14587df..ee4115f 100644 --- a/site/doc/pages/socket_connect.txt +++ b/site/doc/pages/socket_connect.txt @@ -1,5 +1,5 @@ :sig -u64 socket_connect(String host, short port) +u64 socket_connect(String host, u16 port) :params host : host name @@ -15,4 +15,6 @@ Opens a socket connection to the given `host` and `port`. The returned socket handle is then used with `socket_read()`, `socket_write()`, and `socket_close()`. :example -print("socket_connect is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 fd = socket_connect("127.0.0.1", 80); +print(fd != 0 ? "connected to the local server" : "connect failed", "\n"); +socket_close(fd); diff --git a/site/doc/pages/socket_read.txt b/site/doc/pages/socket_read.txt index 0f4bf78..a767776 100644 --- a/site/doc/pages/socket_read.txt +++ b/site/doc/pages/socket_read.txt @@ -16,4 +16,8 @@ Reads data from a socket connection. `max_length` limits how much data is read, and `timeout` controls how long the operation is allowed to wait. :example -print("socket_read is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 fd = socket_connect("127.0.0.1", 80); +socket_write(fd, "GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n"); +String chunk = socket_read(fd, 1024, 2); +socket_close(fd); +print(str_starts_with(chunk, "HTTP/") ? "read an HTTP response from the server" : "no data", "\n"); diff --git a/site/doc/pages/socket_write.txt b/site/doc/pages/socket_write.txt index b90d9e5..54a70f5 100644 --- a/site/doc/pages/socket_write.txt +++ b/site/doc/pages/socket_write.txt @@ -15,4 +15,7 @@ Writes `data` to the given socket. The function returns `true` when the write succeeds. :example -print("socket_write is resource-bound; configure the service or connection, then call it in request code.\n"); +u64 fd = socket_connect("127.0.0.1", 80); +bool sent = socket_write(fd, "GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n"); +socket_close(fd); +print(sent ? "request sent" : "send failed", "\n"); diff --git a/site/doc/pages/sqlite_affected_rows.txt b/site/doc/pages/sqlite_affected_rows.txt index 5681a03..9889efa 100644 --- a/site/doc/pages/sqlite_affected_rows.txt +++ b/site/doc/pages/sqlite_affected_rows.txt @@ -19,4 +19,10 @@ print(sqlite_affected_rows(db)); ``` :example -print("sqlite_affected_rows is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-affected.db"); +sqlite_query(db, "drop table if exists t"); +sqlite_query(db, "create table t(id integer primary key, v text)"); +sqlite_query(db, "insert into t(id, v) values(1,'a'),(2,'b'),(3,'c')"); +sqlite_query(db, "update t set v = 'x' where id <= 2"); +print("rows updated: ", sqlite_affected_rows(db), "\n"); +sqlite_disconnect(db); diff --git a/site/doc/pages/sqlite_connect.txt b/site/doc/pages/sqlite_connect.txt index 8aa95fe..d865cf4 100644 --- a/site/doc/pages/sqlite_connect.txt +++ b/site/doc/pages/sqlite_connect.txt @@ -28,4 +28,7 @@ SQLite itself handles file locking and one-writer/many-reader concurrency. UCE d Connections are registered for request cleanup, but explicit `sqlite_disconnect()` is still preferred when you are done with the handle. :example -print("sqlite_connect is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-connect.db"); +sqlite_query(db, "create table if not exists t(id integer primary key, name text)"); +print(db != 0 ? "connected" : "failed to connect", "\n"); +sqlite_disconnect(db); diff --git a/site/doc/pages/sqlite_disconnect.txt b/site/doc/pages/sqlite_disconnect.txt index a203d02..5c82a81 100644 --- a/site/doc/pages/sqlite_disconnect.txt +++ b/site/doc/pages/sqlite_disconnect.txt @@ -14,4 +14,7 @@ Closes an SQLite connection and deletes the UCE connection wrapper. UCE also cleans up SQLite connections that remain open at request end, but explicit disconnects keep resource lifetime local and clear. :example -print("sqlite_disconnect is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-disc.db"); +sqlite_query(db, "create table if not exists t(id integer)"); +sqlite_disconnect(db); +print("connection closed\n"); diff --git a/site/doc/pages/sqlite_error.txt b/site/doc/pages/sqlite_error.txt index e657bf2..7608c73 100644 --- a/site/doc/pages/sqlite_error.txt +++ b/site/doc/pages/sqlite_error.txt @@ -15,4 +15,7 @@ Returns the latest SQLite connector status or error message. Successful calls usually set the message to `ok` or `connected`. Failed prepare, bind, step, pragma, or open operations include connector context plus SQLite's own error text. :example -print("sqlite_error is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-error.db"); +sqlite_query(db, "select * from no_such_table"); +print(sqlite_error(db) != "" ? "error reported" : "no error", "\n"); +sqlite_disconnect(db); diff --git a/site/doc/pages/sqlite_insert_id.txt b/site/doc/pages/sqlite_insert_id.txt index a9adc18..5f6090e 100644 --- a/site/doc/pages/sqlite_insert_id.txt +++ b/site/doc/pages/sqlite_insert_id.txt @@ -19,4 +19,9 @@ u64 id = sqlite_insert_id(db); ``` :example -print("sqlite_insert_id is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-insertid.db"); +sqlite_query(db, "drop table if exists t"); +sqlite_query(db, "create table t(id integer primary key autoincrement, v text)"); +sqlite_query(db, "insert into t(v) values('x')"); +print("new row id: ", sqlite_insert_id(db), "\n"); +sqlite_disconnect(db); diff --git a/site/doc/pages/sqlite_query.txt b/site/doc/pages/sqlite_query.txt index 1908d28..c534aca 100644 --- a/site/doc/pages/sqlite_query.txt +++ b/site/doc/pages/sqlite_query.txt @@ -37,4 +37,13 @@ Result rows are objects keyed by column name. SQLite integer, float, text, blob, For statements that do not return rows, inspect `sqlite_affected_rows()` or `sqlite_insert_id()` after the call. :example -print("sqlite_query is resource-bound; configure the service or connection, then call it in request code.\n"); +SQLite* db = sqlite_connect("/tmp/doc-sqlite-query.db"); +sqlite_query(db, "drop table if exists users"); +sqlite_query(db, "create table users(id integer primary key, email text)"); +StringMap params; params["email"] = "ada@example.test"; +sqlite_query(db, "insert into users(email) values(:email)", params); +DValue rows = sqlite_query(db, "select email from users"); +String first = "none"; +rows.each([&](DValue row, String key) { first = row["email"].to_string(); }); +print(first, "\n"); +sqlite_disconnect(db); diff --git a/site/doc/pages/task.txt b/site/doc/pages/task.txt index 1da25b7..e999c15 100644 --- a/site/doc/pages/task.txt +++ b/site/doc/pages/task.txt @@ -23,4 +23,5 @@ Task keys may contain ordinary user-facing text. UCE hashes the key before using `timeout` is enforced in the child process with an alarm. The default is ten minutes. Pass `0` only for tasks that have their own shutdown path. :example -print("task example\n"); +task("doc-demo-task", []() { usleep(100000); }); +print(task_pid("doc-demo-task") > 0 ? "background task is running" : "no task", "\n"); diff --git a/site/doc/pages/task_kill.txt b/site/doc/pages/task_kill.txt index 21f6f9a..61cdeb4 100644 --- a/site/doc/pages/task_kill.txt +++ b/site/doc/pages/task_kill.txt @@ -1,5 +1,5 @@ :sig -int task_kill(pid_t pid, int sig = 0) +int task_kill(pid_t pid, s32 sig = 0) :params pid : PID of the process @@ -22,4 +22,6 @@ Wraps the standard POSIX `kill()` function for positive process IDs. Passing `0` as the signal performs an existence and permission check without actually delivering a signal. :example -print("task_kill example\n"); +task("doc-demo-kill", []() { usleep(3000000); }); +pid_t pid = task_pid("doc-demo-kill"); +print(pid > 0 && task_kill(pid) == 0 ? "task signalled to stop" : "no task to kill", "\n"); diff --git a/site/doc/pages/task_pid.txt b/site/doc/pages/task_pid.txt index 60b7901..1699577 100644 --- a/site/doc/pages/task_pid.txt +++ b/site/doc/pages/task_pid.txt @@ -16,4 +16,5 @@ Returns `0` when no matching task is active or task state cannot be read safely. New task status records include the Linux process start tick from `/proc//stat`, so `task_pid()` can reject a stale status file if the PID has exited and the numeric PID has since been reused by another process. :example -print("task_pid example\n"); +task("doc-demo-pid", []() { usleep(100000); }); +print("task pid > 0: ", task_pid("doc-demo-pid") > 0 ? "yes" : "no", "\n"); diff --git a/site/doc/pages/task_repeat.txt b/site/doc/pages/task_repeat.txt index ed4eeca..cb0eca6 100644 --- a/site/doc/pages/task_repeat.txt +++ b/site/doc/pages/task_repeat.txt @@ -24,4 +24,7 @@ If a process with the same `key` is already running anywhere in the runtime inst `timeout` bounds the lifetime of the repeating worker. The default is ten minutes. Pass `0` only for workers that have another shutdown path. :example -print("task_repeat example\n"); +task_repeat("doc-demo-repeat", 60.0, []() { usleep(10000); }); +pid_t pid = task_pid("doc-demo-repeat"); +print(pid > 0 ? "repeating task scheduled" : "not scheduled", "\n"); +if(pid > 0) task_kill(pid); diff --git a/site/doc/pages/ucb_decode.txt b/site/doc/pages/ucb_decode.txt index dd25fde..ac49d0a 100644 --- a/site/doc/pages/ucb_decode.txt +++ b/site/doc/pages/ucb_decode.txt @@ -14,4 +14,6 @@ The one-argument form returns an empty `DValue` on invalid input. The three-argu :example -print("ucb_decode example\n"); +DValue original; original["n"] = "7"; +DValue restored = ucb_decode(ucb_encode(original)); +print(restored["n"].to_string(), "\n"); diff --git a/site/doc/pages/ucb_encode.txt b/site/doc/pages/ucb_encode.txt index 4d073df..a4af1b7 100644 --- a/site/doc/pages/ucb_encode.txt +++ b/site/doc/pages/ucb_encode.txt @@ -13,4 +13,7 @@ UCEB1 is length-prefixed and binary-safe. It preserves nested maps, list-shaped :example -print("ucb_encode example\n"); +DValue original; +original["msg"] = "hello"; +String packed = ucb_encode(original); +print("encoded ", packed.length(), " bytes; round-trips to: ", ucb_decode(packed)["msg"].to_string(), "\n"); diff --git a/site/doc/pages/unit_call.txt b/site/doc/pages/unit_call.txt index 663124f..7c45f8c 100644 --- a/site/doc/pages/unit_call.txt +++ b/site/doc/pages/unit_call.txt @@ -45,4 +45,4 @@ Calling a page render handler through `unit_call()`: :example -print("unit_call example\n"); +unit_call("examples/sample_unit.uce", "doc_greet"); diff --git a/site/doc/pages/unit_compile.txt b/site/doc/pages/unit_compile.txt index 88d17e4..47a9938 100644 --- a/site/doc/pages/unit_compile.txt +++ b/site/doc/pages/unit_compile.txt @@ -17,4 +17,4 @@ Triggers a manual recompile of a UCE compilation unit through the host membrane. If `path` is relative, it is resolved by the host unit resolver. The function returns whether compilation succeeded; it does not return or expose a native `SharedUnit*` to wasm units. :example -print("unit_compile example\n"); +print(unit_compile("examples/sample_unit.uce") ? "compiled the sample unit" : "compile failed", "\n"); diff --git a/site/doc/pages/unit_info.txt b/site/doc/pages/unit_info.txt index 7f83ffc..20590d0 100644 --- a/site/doc/pages/unit_info.txt +++ b/site/doc/pages/unit_info.txt @@ -21,4 +21,5 @@ If `path` is relative, it is resolved by the same host unit resolver used for un Because unit metadata lives in worker process memory, request and timing counters reflect the current runtime process, not an aggregate across every worker process. :example -print("unit_info example\n"); +DValue info = unit_info("examples/sample_unit.uce"); +print("unit_info() returns ", info.get_type_name(), " metadata for the unit\n"); diff --git a/site/doc/pages/unit_load.txt b/site/doc/pages/unit_load.txt index c6fbb1c..1467bad 100644 --- a/site/doc/pages/unit_load.txt +++ b/site/doc/pages/unit_load.txt @@ -17,4 +17,6 @@ native-only internal API. `unit_load()` returns a process-local `SharedUnit*`, w Use unit-facing APIs such as `unit_info()`, `unit_compile()`, `unit_call()`, `unit_render()`, or component helpers instead. :example -print("unit_load example\n"); +// unit_load() is a native/internal loader; from a unit, use unit_compile() +// to make sure a unit is built, then unit_call()/unit_render() to invoke it. +print(unit_compile("examples/sample_unit.uce") ? "sample unit is loaded and ready" : "load failed", "\n"); diff --git a/site/doc/pages/unit_render.txt b/site/doc/pages/unit_render.txt index 259f0c5..e59cf43 100644 --- a/site/doc/pages/unit_render.txt +++ b/site/doc/pages/unit_render.txt @@ -22,4 +22,4 @@ Examples: :example -print("unit_render example\n"); +unit_render("examples/sample_unit.uce"); diff --git a/site/doc/pages/units_list.txt b/site/doc/pages/units_list.txt index 65b72b0..09bfad1 100644 --- a/site/doc/pages/units_list.txt +++ b/site/doc/pages/units_list.txt @@ -13,4 +13,4 @@ Returns the normalized paths of all known `.uce` units. This includes the shared known-unit registry plus any units already loaded in the current runtime process. :example -print("units_list example\n"); +print("the runtime knows ", units_list().size(), " compiled units\n"); diff --git a/site/doc/pages/usleep.txt b/site/doc/pages/usleep.txt index 74555c1..f6f8733 100644 --- a/site/doc/pages/usleep.txt +++ b/site/doc/pages/usleep.txt @@ -1,5 +1,5 @@ :sig -int usleep(unsigned int usec) +s32 usleep(u32 usec) :params usec : microseconds to sleep diff --git a/site/doc/pages/var_dump.txt b/site/doc/pages/var_dump.txt index 1ceddb0..6574408 100644 --- a/site/doc/pages/var_dump.txt +++ b/site/doc/pages/var_dump.txt @@ -18,4 +18,7 @@ print Returns a string representation of `t` intended for debugging. :example -print("var_dump example\n"); +DValue user; +user["name"] = "Ada"; +user["role"] = "engineer"; +print(var_dump(user)); diff --git a/site/doc/pages/ws_close.txt b/site/doc/pages/ws_close.txt index e18cafc..fb1f8fc 100644 --- a/site/doc/pages/ws_close.txt +++ b/site/doc/pages/ws_close.txt @@ -14,4 +14,5 @@ Queues a WebSocket close frame and closes the targeted connection. If `connection_id` is omitted, the current connection handled by `WS(Request& context)` is closed. :example -print("ws_close is resource-bound; configure the service or connection, then call it in request code.\n"); +bool closed = ws_close("example-connection-id"); +print(closed ? "close queued" : "no such connection to close", "\n"); diff --git a/site/doc/pages/ws_connection_count.txt b/site/doc/pages/ws_connection_count.txt index cb258a4..f8ce52e 100644 --- a/site/doc/pages/ws_connection_count.txt +++ b/site/doc/pages/ws_connection_count.txt @@ -14,4 +14,4 @@ Returns the number of currently connected WebSocket clients for the given scope. If `scope` is omitted, the current page scope is used. :example -print("ws_connection_count is resource-bound; configure the service or connection, then call it in request code.\n"); +print("connection count in this scope: ", ws_connection_count(), "\n"); diff --git a/site/doc/pages/ws_connection_id.txt b/site/doc/pages/ws_connection_id.txt index e20af68..9604b78 100644 --- a/site/doc/pages/ws_connection_id.txt +++ b/site/doc/pages/ws_connection_id.txt @@ -13,4 +13,5 @@ Returns the runtime-generated connection ID of the client whose message is curre This ID can be passed to `ws_send_to()` or `ws_close()` to target a single connected client. :example -print("ws_connection_id is resource-bound; configure the service or connection, then call it in request code.\n"); +String id = ws_connection_id(); +print(id == "" ? "(empty outside a WS handler)" : id, "\n"); diff --git a/site/doc/pages/ws_connections.txt b/site/doc/pages/ws_connections.txt index 9d1b87e..1b7ff63 100644 --- a/site/doc/pages/ws_connections.txt +++ b/site/doc/pages/ws_connections.txt @@ -14,4 +14,5 @@ Returns the currently connected WebSocket client IDs for the given scope. If `scope` is omitted, the current page scope is used. :example -print("ws_connections is resource-bound; configure the service or connection, then call it in request code.\n"); +StringList ids = ws_connections(); +print("active connections in this scope: ", ids.size(), "\n"); diff --git a/site/doc/pages/ws_is_binary.txt b/site/doc/pages/ws_is_binary.txt index 43477a4..2c45e89 100644 --- a/site/doc/pages/ws_is_binary.txt +++ b/site/doc/pages/ws_is_binary.txt @@ -13,4 +13,4 @@ Returns whether the message currently being handled by `WS(Request& context)` ar If this returns `false`, the current message was delivered as a text frame. :example -print("ws_is_binary is resource-bound; configure the service or connection, then call it in request code.\n"); +print(ws_is_binary() ? "binary frame" : "text frame (default outside a WS handler)", "\n"); diff --git a/site/doc/pages/ws_message.txt b/site/doc/pages/ws_message.txt index 56cda9a..6322020 100644 --- a/site/doc/pages/ws_message.txt +++ b/site/doc/pages/ws_message.txt @@ -15,4 +15,5 @@ For text frames this is the decoded text payload. For binary frames this `String Use `ws_is_binary()` or `ws_opcode()` to choose how to parse the payload. :example -print("ws_message is resource-bound; configure the service or connection, then call it in request code.\n"); +// Inside a WS message handler ws_message() returns the current frame payload. +print("payload length in this non-WS context: ", ws_message().length(), "\n"); diff --git a/site/doc/pages/ws_opcode.txt b/site/doc/pages/ws_opcode.txt index 7b411dd..5877d11 100644 --- a/site/doc/pages/ws_opcode.txt +++ b/site/doc/pages/ws_opcode.txt @@ -16,4 +16,5 @@ Common values are: - `0x2` for binary messages :example -print("ws_opcode is resource-bound; configure the service or connection, then call it in request code.\n"); +// ws_opcode() returns the WebSocket frame opcode (1 text, 2 binary, 8 close, ...). +print("opcode: ", (u64)ws_opcode(), " (0 outside a WS frame)\n"); diff --git a/site/doc/pages/ws_scope.txt b/site/doc/pages/ws_scope.txt index f2d4feb..403457a 100644 --- a/site/doc/pages/ws_scope.txt +++ b/site/doc/pages/ws_scope.txt @@ -15,4 +15,5 @@ This is the same default scope used by `ws_send()`, `ws_connections()`, and `ws_ In the current runtime implementation this scope is the page's internal endpoint identifier, typically the absolute `SCRIPT_FILENAME` of the `.uce` file that accepted the WebSocket upgrade. :example -print("ws_scope is resource-bound; configure the service or connection, then call it in request code.\n"); +String scope = ws_scope(); +print(scope == "" ? "(no WS scope in this context)" : scope, "\n"); diff --git a/site/doc/pages/ws_send.txt b/site/doc/pages/ws_send.txt index 9727c97..6fed98a 100644 --- a/site/doc/pages/ws_send.txt +++ b/site/doc/pages/ws_send.txt @@ -16,4 +16,6 @@ Queues a WebSocket message for every client connected to the given scope. If `scope` is omitted, the current page scope is used. :example -print("ws_send is resource-bound; configure the service or connection, then call it in request code.\n"); +// Inside a WS handler this broadcasts to the current scope. +bool sent = ws_send("hello subscribers"); +print(sent ? "broadcast queued" : "no active WS scope to broadcast to", "\n"); diff --git a/site/doc/pages/ws_send_to.txt b/site/doc/pages/ws_send_to.txt index d29b9f1..f775c3c 100644 --- a/site/doc/pages/ws_send_to.txt +++ b/site/doc/pages/ws_send_to.txt @@ -14,4 +14,5 @@ return value : true if the target connection exists and the message was queued Queues a WebSocket message for one specific connected client. :example -print("ws_send_to is resource-bound; configure the service or connection, then call it in request code.\n"); +bool sent = ws_send_to("example-connection-id", "direct message"); +print(sent ? "message queued" : "no such connection", "\n"); diff --git a/site/doc/pages/zip_create.txt b/site/doc/pages/zip_create.txt index c092444..6c8438b 100644 --- a/site/doc/pages/zip_create.txt +++ b/site/doc/pages/zip_create.txt @@ -27,4 +27,8 @@ An entry may also provide `file` instead of `content`; UCE reads that source fil Entry names are normalized to forward slashes and rejected when they are absolute paths, drive-qualified paths, empty names, or contain `..` path segments. :example -print("zip_create example\n"); +DValue entries; +entries["readme.txt"] = "hello from UCE"; +entries["license.txt"] = "MIT license"; +zip_create("/tmp/doc-zip.zip", entries); +print("readme.txt in archive: ", zip_read("/tmp/doc-zip.zip", "readme.txt"), "\n"); diff --git a/site/doc/pages/zip_extract.txt b/site/doc/pages/zip_extract.txt index 1cb02af..dcc7599 100644 --- a/site/doc/pages/zip_extract.txt +++ b/site/doc/pages/zip_extract.txt @@ -21,4 +21,10 @@ UCE creates the destination directory and any needed child directories when they For safety, every member name is normalized and checked before extraction. Absolute paths, drive-qualified paths, empty names, and `..` path segments are rejected so archives cannot write outside the destination directory. :example -print("zip_extract example\n"); +DValue entries; +entries["note.txt"] = "extracted content"; +zip_create("/tmp/doc-zipext.zip", entries); +mkdir("/tmp/doc-zipext-out"); +zip_extract("/tmp/doc-zipext.zip", "/tmp/doc-zipext-out"); +print(file_get_contents("/tmp/doc-zipext-out/note.txt"), "\n"); +dir_remove("/tmp/doc-zipext-out", true); diff --git a/site/doc/pages/zip_list.txt b/site/doc/pages/zip_list.txt index 671056b..388c5ec 100644 --- a/site/doc/pages/zip_list.txt +++ b/site/doc/pages/zip_list.txt @@ -34,4 +34,13 @@ Example: :example -print("zip_list example\n"); +DValue entries; +entries["a.txt"] = "alpha"; +entries["b.txt"] = "beta"; +zip_create("/tmp/doc-ziplist.zip", entries); +DValue listing = zip_list("/tmp/doc-ziplist.zip"); +String names = ""; +listing["entries"].each([&](DValue e, String key) { + names += (names == "" ? "" : ", ") + e["name"].to_string(); +}); +print(listing["count"].to_u64(), " entries: ", names, "\n"); diff --git a/site/doc/pages/zip_read.txt b/site/doc/pages/zip_read.txt index 1e8633a..343df94 100644 --- a/site/doc/pages/zip_read.txt +++ b/site/doc/pages/zip_read.txt @@ -21,4 +21,7 @@ Reads one file member from a ZIP archive and returns its uncompressed bytes as a Use `zip_list()` when you need to discover entry names before reading them. :example -print("zip_read example\n"); +DValue entries; +entries["greeting.txt"] = "hello world"; +zip_create("/tmp/doc-zipread.zip", entries); +print(zip_read("/tmp/doc-zipread.zip", "greeting.txt"), "\n"); diff --git a/site/tests/cli_runner.uce b/site/tests/cli_runner.uce index b3b5f84..8ddaa8d 100644 --- a/site/tests/cli_runner.uce +++ b/site/tests/cli_runner.uce @@ -23,7 +23,7 @@ String cli_truncate(String value, u64 max_len = 240) return(value.substr(0, max_len) + "..."); } -CliHttpResponse cli_http_request(String host, short port, String path, String extra_headers = "") +CliHttpResponse cli_http_request(String host, u16 port, String path, String extra_headers = "") { CliHttpResponse res; u64 fd = socket_connect(host, port); @@ -160,6 +160,35 @@ bool cli_doc_source_has_example(String source) return(false); } +// A real example must exercise the documented API and print a derived result. +// These placeholder shapes (auto-generated stubs) are forbidden by the gate. +bool cli_doc_example_is_placeholder(String source) +{ + bool in_example = false; + StringList body; + for(String line : split(source, "\n")) + { + String t = trim(line); + if(!in_example) + { + if(t == ":example") + in_example = true; + continue; + } + if(t.length() > 0 && t.substr(0, 1) == ":") + break; + body.push_back(line); + } + String joined = join(body, "\n"); + if(cli_contains(joined, " example\\n\");")) + return(true); + if(cli_contains(joined, "is resource-bound; configure")) + return(true); + if(cli_contains(joined, "documents a UCE concept")) + return(true); + return(false); +} + void cli_run_doc_pages_gate() { StringList error_markers; @@ -172,10 +201,10 @@ void cli_run_doc_pages_gate() for(String file_name : ls("../doc/pages/")) { + String source = file_get_contents("../doc/pages/" + file_name); String page = nibble(file_name, "."); if(page == "") continue; - String source = file_get_contents("../doc/pages/" + file_name); bool has_example = cli_doc_source_has_example(source); String path = "/doc/index.uce?p=" + uri_encode(page); CliHttpResponse res = cli_frontend(path); @@ -194,6 +223,11 @@ void cli_run_doc_pages_gate() ok = false; summary += "; example reported an error"; } + if(cli_doc_example_is_placeholder(source)) + { + ok = false; + summary += "; placeholder example (must exercise the API)"; + } } cli_test_case("uce_doc_gate:doc page " + page, ok, ok ? summary : summary + "; body=" + cli_truncate(res.body)); } diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp index 2f6acde..a0c1efc 100644 --- a/src/lib/sys.cpp +++ b/src/lib/sys.cpp @@ -179,7 +179,7 @@ String file_pread(u64 h, u64 offset, u64 len) } u64 file_write(u64 h, String data) { return(uce_host_file_handle_write(h, data.data(), data.size())); } u64 file_pwrite(u64 h, u64 offset, String data) { return(uce_host_file_handle_pwrite(h, offset, data.data(), data.size())); } -s64 file_seek(u64 h, s64 offset, int whence) { return((s64)uce_host_file_handle_seek(h, offset, whence)); } +s64 file_seek(u64 h, s64 offset, s64 whence) { return((s64)uce_host_file_handle_seek(h, offset, (int)whence)); } s64 file_tell(u64 h) { return((s64)uce_host_file_handle_tell(h)); } void file_close(u64 h) { uce_host_file_handle_close(h); } static DValue wasm_decode_dvalue_result(String encoded) @@ -352,10 +352,10 @@ String process_start_directory() cwd.resize(got <= required ? got : 0); return(cwd); } -time_t file_mtime(String file_name) +u64 file_mtime(String file_name) { String current = wasm_current_unit_file(); - return((time_t)uce_host_file_mtime(file_name.data(), file_name.size(), current.data(), current.size())); + return((u64)uce_host_file_mtime(file_name.data(), file_name.size(), current.data(), current.size())); } void file_unlink(String file_name) { @@ -447,9 +447,9 @@ u64 time_parse(String time_String) { return(int_val(trim(shell_exec("date -u -d " + shell_escape(time_String) + " +'%s'")))); } -u64 socket_connect(String host, short port) +u64 socket_connect(String host, u16 port) { - return(uce_host_socket_connect(host.data(), host.size(), port)); + return(uce_host_socket_connect(host.data(), host.size(), (int)port)); } void socket_close(u64 sockfd) { uce_host_socket_close(sockfd); } bool socket_write(u64 sockfd, String data) @@ -527,7 +527,7 @@ String backtrace_capture(u32 max_frames, u32 skip_frames) trace.resize(got <= required ? got : 0); return(trace); } -String signal_name(int sig) +String signal_name(s32 sig) { switch(sig) { @@ -559,7 +559,7 @@ StringList memcache_escape_keys(StringList keys) result.push_back(memcache_escape_key(s)); return(result); } -u64 memcache_connect(String host, short port) +u64 memcache_connect(String host, u16 port) { if(host == "") host = "127.0.0.1"; @@ -645,7 +645,7 @@ extern "C" int uce_wasm_task_run(uint64_t callback_id) return(0); } -int task_kill(pid_t pid, int sig) { return(uce_host_task_kill(pid, sig)); } +int task_kill(pid_t pid, s32 sig) { return(uce_host_task_kill(pid, (int)sig)); } String runtime_safe_key(String key, String label) { (void)label; @@ -670,7 +670,7 @@ pid_t task_repeat(String key, f64 interval, std::function exec_after_spa } pid_t task_pid(String key) { return((pid_t)uce_host_task_pid(key.data(), key.size())); } extern "C" unsigned int sleep(unsigned int seconds) { return(uce_host_sleep_us((uint64_t)seconds * 1000000ull)); } -extern "C" int usleep(unsigned int usec) { uce_host_sleep_us(usec); return(0); } +extern "C" s32 usleep(u32 usec) { uce_host_sleep_us(usec); return(0); } pid_t server_start_http(String key, String socket_fn_or_port, String call_uce_filename, String call_function) { String current = wasm_current_unit_file(); @@ -806,7 +806,7 @@ String backtrace_capture(u32 max_frames, u32 skip_frames) return(backtrace_get_frames(frames.data(), size, skip_frames)); } -String signal_name(int sig) +String signal_name(s32 sig) { switch(sig) { @@ -1089,7 +1089,7 @@ void cwd_set(String path) chdir(path.c_str()); } -time_t file_mtime(String file_name) +u64 file_mtime(String file_name) { struct stat info; if (stat(file_name.c_str(), &info) != 0) @@ -1184,7 +1184,7 @@ u64 time_parse(String time_String) return(int_val(trim(shell_exec("date -u -d "+shell_escape(time_String)+" +'%s'")))); } -u64 socket_connect(String host, short port) +u64 socket_connect(String host, u16 port) { /*String addrinfo { @@ -1269,7 +1269,7 @@ StringList memcache_escape_keys(StringList keys) return(result); } -u64 memcache_connect(String host, short port) +u64 memcache_connect(String host, u16 port) { return(socket_connect(host, port)); } @@ -1454,7 +1454,7 @@ void task_close_inherited_fds() close(fd); } -int task_kill(pid_t pid, int sig) +int task_kill(pid_t pid, s32 sig) { if(pid <= 0) { diff --git a/src/lib/sys.h b/src/lib/sys.h index 3be3982..554a90d 100644 --- a/src/lib/sys.h +++ b/src/lib/sys.h @@ -16,7 +16,7 @@ typedef int pid_t; extern "C" { int raise(int); unsigned int sleep(unsigned int seconds); -int usleep(unsigned int usec); +s32 usleep(u32 usec); } #else #include @@ -59,7 +59,7 @@ String file_read(u64 h, u64 len); String file_pread(u64 h, u64 offset, u64 len); u64 file_write(u64 h, String data); u64 file_pwrite(u64 h, u64 offset, String data); -s64 file_seek(u64 h, s64 offset, int whence); +s64 file_seek(u64 h, s64 offset, s64 whence); s64 file_tell(u64 h); void file_close(u64 h); DValue file_stat(String path); @@ -82,7 +82,7 @@ inline bool file_append(String file_name, Ts... args) String cwd_get(); void cwd_set(String path); String process_start_directory(); -time_t file_mtime(String file_name); +u64 file_mtime(String file_name); void file_unlink(String file_name); String expand_path(String path, String relative_to_path = ""); StringList ls(String dir); @@ -97,7 +97,7 @@ String time_format_utc(String format = "", u64 timestamp = 0); String time_format_relative(u64 timestamp, String format_very_recent = "", u64 medium_recency_seconds = 0, String format_medium_recent = "", u64 not_recent_seconds = 0, String format_not_recent = ""); u64 time_parse(String time_String); -u64 socket_connect(String host, short port); +u64 socket_connect(String host, u16 port); void socket_close(u64 sockfd); bool socket_write(u64 sockfd, String data); String socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1); @@ -115,11 +115,11 @@ bool ws_close(String connection_id = ""); String backtrace_get_frames(void* const* frames, size_t size, u32 skip_frames = 0); String backtrace_capture(u32 max_frames = 32, u32 skip_frames = 0); -String signal_name(int sig); +String signal_name(s32 sig); String memcache_escape_key(String key); StringList memcache_escape_keys(StringList keys); -u64 memcache_connect(String host = "127.0.0.1", short port = 11211); +u64 memcache_connect(String host = "127.0.0.1", u16 port = 11211); String memcache_command(u64 connection, String command); bool memcache_set(u64 connection, String key, String value, u64 expires_in = 60*60); bool memcache_delete(u64 connection, String key); @@ -137,7 +137,7 @@ extern pid_t my_pid; #endif void on_segfault(int sig); -int task_kill(pid_t pid, int sig = 0); +int task_kill(pid_t pid, s32 sig = 0); String runtime_safe_key(String key, String label = "runtime key"); pid_t task(String key, std::function exec_after_spawn, u64 timeout = 60*10);