Commit graph

33 commits

Author SHA1 Message Date
Yucong Sun
5f7be1008f
Char-mode input: deliver real keystrokes (BS/DEL, whole UTF-8, raw ESC); fix NAWS lost at logon (#1245)
* Char-mode input: deliver real keystrokes (BS/DEL, whole UTF-8, raw ESC); fix NAWS lost at logon

Five input-path fixes that make raw-keystroke (get_char) applications
viable, found by building the LPC TUI library on top of them:

- comm.cc: char mode delivered "" for Backspace/Delete (the byte was
  zeroed before delivery, making BS/DEL/NUL indistinguishable). The
  literal byte is now delivered; line-mode in-buffer editing unchanged.
- comm.cc: char mode delivered one *byte* per callback, splitting a
  multi-byte UTF-8 character into 2-4 invalid one-byte strings.
  Extraction is now UTF-8 aware: a complete sequence arrives as one
  callback carrying one valid character; malformed bytes still go
  byte-at-a-time (no stalls).
- comm.cc: the "no ansi" + "strip before process input" ESC->space
  rewrite (both default on) also applied to char mode, so arrow keys
  arrived as literal "[A". The rewrite is an anti-ANSI-injection
  protection for line-mode commands; char mode now always passes ESC
  through.
- net/telnet.cc: each received chunk was u8_sanitize()d independently,
  so a UTF-8 character split across TCP segments became U+FFFD in any
  input mode. An incomplete trailing sequence (new u8_incomplete_tail(),
  GTest-covered) is now carried over in interactive_t and prepended to
  the next chunk.
- net/telnet.cc + comm.cc: fast clients answer the initial DO NAWS
  while ip->ob is still the master object, so the window_size apply
  fired on the wrong object and the size was lost until the next
  resize. The last report is cached and replayed on the user object at
  logon.

docs/efun/interactive/get_char.md documents the delivery contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* Add /std/tui: an LPC TUI library (readline + ncurses for the prompt line and full-screen apps)

A terminal-UI toolkit in pure LPC, strictly layered (design + rationale
in testsuite/std/tui/DESIGN.md; user docs in docs/concepts/general/tui.md):

- ansi.lpc: escape builders plus the width toolkit the driver lacks
  (visible_width/wslice/wpad are ANSI-blind and wide-char aware).
- keys.lpc: keystroke decoder state machine: the get_char byte stream ->
  key events (CSI/SS3 with xterm modifiers, Alt prefixes, bracketed
  paste as one event, SGR mouse, UTF-8, lone-ESC via caller-driven
  flush()).
- readline.lpc: the line editor: emacs keymap (motion/kill/yank/
  transpose), Up/Down history, C-r/C-s incremental search, Tab
  completion, masked mode, horizontal scrolling with wide-char aware
  viewport; repaints only its own line so it works at any scroll
  position.
- screen.lpc: virtual cell grid + minimal-diff frame renderer with
  copy-on-write rows (frame cost tracks touched rows, not W x H);
  wide chars own two cells, boxes/fills/attrs as SGR param strings.
- widget.lpc + w/ (label, list, textfield) + app.lpc: widget protocol,
  focus cycling, event routing; textfield embeds a readline engine.
- terminal.lpc: the one impure module, inherited by the user object:
  get_char re-arm loop (I_NOECHO across re-arms), NAWS/TTYPE caching
  via the window_size/terminal_type applies, lone-ESC walltime timeout,
  guaranteed teardown. tui_readline() is a drop-in input_to()
  replacement; tui_open() runs full-screen apps on the alt screen.

Everything below terminal.lpc is a pure state machine: 194 checks in
single/tests/std/tui/ cover the decoder, editor sessions, history
search, screen diffing and widgets headlessly. `tuidemo` /
`tuidemo app` (command/tuidemo.lpc) demo both modes over any telnet
client; the whole stack was verified end-to-end over a live connection
including split-packet UTF-8, modifier keys, paste, resize and
alternate-screen teardown.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* /std/tui v2: pterm/blessed-inspired widgets, inline printers and prompts, showcases

Reviewed the pterm (Go) and blessed widget catalogs and ported what earns
its keep in a MUD; DESIGN.md is renamed to README.md and documents the
full set (including what was deliberately not ported).

New layers:
- print.lpc — pterm-style printers that compose with plain write():
  p_table (boxed, width-aware, header rule), p_tree, p_bars, p_spark,
  p_panel, p_bullets, p_header, p_progress, p_info/success/warn/error,
  and p_bigtext via the existing /std/bitmap_font.
- menu.lpc + terminal glue — inline interactive prompts in the normal
  output flow: tui_select(), tui_multiselect() (Space toggles, list
  windows by height, collapses to a "? prompt: answer" line) and
  tui_confirm() (y/n with a default).

New widgets (/std/tui/w/): table (columns + header + selection), tree
(collapsible, arrows fold), checklist, radiolist, button, progress,
spinner (app-driven tick()), and log (bottom-anchored scrollback pane).

Showcases (command/tuidemo.lpc): `tuidemo select` (prompt chain),
`tuidemo print` (all printers), `tuidemo dashboard` (animated spinner,
progress bars, sparkline, live table and log on a call_out tick), and
`tuidemo form` (textfield, radio group, checkboxes, buttons).

Coverage: three new test files (print exact-output, menu sessions,
widgets2) bring /std/tui to 8 files in the suite; the live-connection
e2e run now drives all six showcases end-to-end (52 checks), including
the select->multiselect->confirm chain, unattended dashboard animation,
and full form entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* /std/tui: charts — braille canvas, line charts, vertical bars, heatmap

Fills the chart gap left by v2 (only the horizontal p_bars existed):

- canvas.lpc: a braille dot canvas (each cell is a 2x4 dot grid from the
  U+2800 block — the blessed-contrib/drawille technique), giving sub-cell
  resolution: c_set/c_unset/c_get, Bresenham c_line, and c_plot (scale a
  value series across the canvas), with per-cell colour. Renders as a
  string, per-row strings, or per-cell ({ ch, attr }) pairs for blitting
  into a screen.
- print.lpc: p_chart (multi-series braille line chart with y-axis gutter
  and coloured legend), p_vbars (vertical bar chart with eighth-block
  partial tops and optional value row), p_heatmap (2D matrix as
  256-colour cells, cool-to-hot ramp, optional axis labels).
- w/chart.lpc: the live line-chart widget — add_series()/add_point()
  rolling history sized to the widget, auto or fixed y-range; replaces
  the dashboard's sparkline label with a real animating graph.
- `tuidemo charts` showcase; chart docs in README.md and the docs page
  (heatmap removed from the not-ported list).

Tests: single/tests/std/tui/charts.lpc pins the braille bit math with
exact glyphs, line drawing, plot endpoints, exact p_vbars output,
chart/heatmap structure, and the widget's rolling window. The live e2e
run verifies `tuidemo charts` output and braille frames streaming from
the dashboard chart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* docs: move the TUI page from Concepts to STDLIB

/std/tui is a mudlib library like base64/break_string/json, so its page
belongs under docs/stdlib/ with the rest of the /std modules, not under
Concepts. Registered in stdlib/index.md; README file-layout pointer
updated. (Concepts' sidebar is autogenerated, so the old entry disappears
with the file.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 15:00:36 -04:00
Yucong Sun
e6f0c377f8
wasm: run the full driver in the browser (Emscripten port) (#1231)
The driver cross-compiles to WebAssembly and runs a complete mudlib
inside a webpage: compiler, VM, all portable efun packages, and the
real telnet protocol layer -- the page is the telnet client, JavaScript
is the wire. The LPC testsuite passes inside the wasm driver (browser
and node).

Architecture (src/wasm/README.md):
- Transport interface (src/net/transport.h): each interactive_t owns an
  abstract byte pipe (write/flush/schedule_command/close). comm.cc and
  telnet.cc are transport-agnostic and compile unchanged on every
  target. Implementations select at link time: SocketTransport +
  WebsocketTransport (net/transport_libevent.cc, native) vs
  WasmConsoleTransport (src/wasm/comm_wasm.cc).
- Inverted event loop: the shared gametick/event core stays in
  backend.cc; backend_libevent.cc (blocking loop) vs
  src/wasm/backend_wasm.cc (page-driven fluffos_tick(now_ms), walltime
  priority queue, capped catch-up). Other per-target singletons (TLS,
  DNS resolver, crash handler) follow the same link-time pattern -- no
  #ifdef __EMSCRIPTEN__ in shared logic files.
- Mudlib rides in Emscripten's MEMFS via file_packager; driver file I/O
  needed zero changes.

jsbridge package (WASM only): js_eval() (sync), js_call() (async page
handlers with LPC callbacks), js_export() (page calls LPC via
Module.fluffos.callLPC, Promise-resolved on a later tick) -- fetch,
canvas/WebGL, storage, page UIs driving the game. Demo in
testsuite/command/jsdemo.lpc + the bundled web terminal.

Build/tooling: native-tools + wasm CMake presets (host codegen tools,
then cross build); tools/wasm/build-deps.sh (ICU + zlib cross-builds,
including the ICU genccode data quirk); tools/wasm/pack-mudlib.sh
(any mudlib + driver -> static web bundle, also works standalone from
the release zip); src/www/wasm/index.html (self-contained web terminal
with a minimal telnet client). On emsdk >= 3.1.57 the build uses native
wasm exceptions (-fwasm-exceptions); older toolchains fall back to
-sDISABLE_EXCEPTION_CATCHING=0.

CI/release: a wasm CI job (latest emsdk, deps cache keyed on resolved
emcc version) gates PRs on the LPC testsuite running inside the wasm
driver under node; release.yml ships fluffos-<version>-wasm.zip
(driver + web terminal + pack-mudlib.sh). Fixed a stale-predefine bug:
options.autogen.h now depends on packages.autogen.h + config.h.

Docs: docs/build-wasm.md (end-to-end workflow), docs/driver/wasm.md
(packer + jsbridge cookbook), docs/efun/jsbridge/*, README + AGENTS
updated. Testsuite files for optional packages guard themselves with
#ifdef __PACKAGE_*__.


Claude-Session: https://claude.ai/code/session_01VVpphH3cgXyziRDCbjUVkb

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 23:33:51 -04:00
Yucong Sun
ebc86aab02 Every efun has a test: ~130 new files close the whole spec inventory
Systematic review of all 326 spec-declared efuns against
testsuite/single/tests/efuns/; every efun now has a test file named
after it. Pure functions get exact behavioral pins (trig/log/vector
math with domain-error cases, trim family, pcre group semantics,
min/max index form, compress/encoding round trips, bit strings,
Levenshtein string_difference, class assembly/introspection, deep
copy() independence, dump_trace shape, save-string round trips);
environment-dependent efuns get honest contracts (sockets: real
create/bind/listen/connect lifecycle on ephemeral ports; interactive/
protocol/ed efuns: graceful no-interactive behavior; package-gated
efuns guarded by __PACKAGE_*__ / efun_defined()). testsuite/include
gains the driver-shipped socket.h/socket_err.h. Fixtures:
catch_tell_probe (catch_tell recorder + self-mover + make_living),
event_probe, shadow_probe, syntax_parent.

The new coverage flushed out SEVEN real driver bugs, all fixed:
- memory_summary: four division-by-zero sites in memory_share() when a
  value's refcount is 0 (UBSan)
- send_zmp/start_request_term_type: command_giver dereferenced before
  the null check -- crash with no interactive (UBSan)
- socket_create: LPC int loaded into enum socket_mode before
  validation -- UB for out-of-range modes; validated as int first
- async_db_exec: manual callback ref taken before handle validation --
  error() unwind leaked the function pointer (AGENTS.md section 4)
- link(): epilogue abandoned both string arguments -- two shared-string
  refs leaked per call
- assemble_class(): built through copy_array then retagged T_CLASS,
  skewing num_arrays/num_classes and the DEBUGMALLOC tag; now built
  with allocate_class_by_size
- parser_mark_verbs(): marked only the HEAD of each verb's rule list
  (later rules unaccounted), double-marked base verbs through synonym
  entries (verb_syn_t::real overlays the node slot), and computed
  header offsets off NULL for rule-less verbs (UBSan)
- pending resolve() queries had no DEBUGMALLOC accounting at all: new
  pending-query registry + mark_dns_requests(), mirroring
  mark_call_outs

Runner: uncaught errors are now PRINTED as well as recorded (a failing
file was otherwise silent about why).

Verified: testsuite x3 randomized (ASan Debug) + ctest 297, clang
RelWithDebInfo sanitizer full suite, RelWithDebInfo full ctest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
Yucong Sun
12d99317d8 Testsuite coverage round 2: ctest test named "testsuite"; save_object overflow fixed
The ctest registration is now simply `testsuite` (label too; CI steps
run `ctest -LE testsuite` / `-L testsuite`), with README/AGENTS updated.

New coverage, and what it caught:

- save_object.lpc's ".lpc" case caught a REAL heap-buffer-overflow on
  CI's clang+ASan RelWithDebInfo job: save_object() sized its buffer
  for the stripped name + ".o" but strcpy'd the UNSTRIPPED path -- the
  4-byte ".lpc" strip overflows the 2-byte ".o" headroom (the legacy
  ".c" strip fit by coincidence; DEBUGMALLOC padding hid it from local
  ASan Debug runs). Fixed with a bounded copy, plus the pre-existing
  file[len-sel] underflow read guarded. Verified in the exact failing
  configuration (clang RelWithDebInfo sanitizer: file case + full suite).

- replace_program.lpc (new; the efun had NO testsuite coverage): pins
  name matching -- extension-less resolves the inherited program's real
  .lpc file, explicit ".lpc" matches exactly, explicit ".c" of an
  .lpc-compiled parent errors, non-inherited program errors. This
  surfaced that pending replace_ob_t records tripped check_memory()
  between test files: they legitimately live until the backend's
  replace_programs() sweep, so they get a dedicated whitelisted
  TAG_REPLACE_OB (same pattern as TAG_SCRATCHPAD) instead of
  TAG_TEMPORARY.

- dual_extension.lpc: the inherit-retry loop keeps the caller's exact
  spelling (a ".c" child inheriting an unloaded parent must reload as
  ".c", not fall back to its .lpc twin -- pins load_object's
  raw-spelling retry), and function_exists() strips a real ".c" suffix.

- restore_object.lpc: ".lpc"/".c" argument spellings resolve the same
  ".o" save file.

- std/harness.lpc: self-test of the failure-recording machinery
  (record/query/pop round-trip via the new master::pop_failure(), and
  the per-assertion check counter).

Verified: testsuite x3 + ctest 297/297 (ASan Debug), full ctest
(RelWithDebInfo), and the clang RelWithDebInfo sanitizer build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
Yucong Sun
f5fa913fd3 LPC testsuite: gtest-style harness, ctest registration, high-value pins
The runner (command/tests.lpc) now speaks the gtest protocol: a
[ RUN ]/[ OK ]/[ FAILED ] block per test file with per-file timing
(perf_counter_ns), then a recap with total checks/files/elapsed.
Failed checks are RECORDED (tests.h OUTPUT -> master::record_failure;
clear_last_error doubles as the check counter) and the run CONTINUES --
one run reports every failure, then the recap lists each failed file
and the driver exits nonzero; "Checks succeeded." + exit 0 is the pass
signal. Interactive runs report without shutting the driver down.
-ftest also accepts globs now (-ftest:efuns/dual*), and the fail-dir
compile-log copy is skipped when a filtered run compiled nothing.

The suite is a first-class ctest test: add_test(lpc-testsuite) with
PASS/FAIL_REGULAR_EXPRESSION and LABELS lpc; CI's two test steps are
now ctest -LE lpc (GTest) and ctest -L lpc (this suite).

New coverage:
- compiler/preprocessor.lpc: end-to-end pins for ## paste, # stringize,
  nested-comma arguments, backslash continuation, self-reference
  termination, redefinition-takes-effect, #warn survival, #undef,
  defined()/token-precedence #if, __FILE__/__DIR__/__LINE__
- compiler/fail/inherit_exact_ext.lpc (+ dedicated never-loaded
  fixture): an explicit ".c" inherit of an .lpc-only file must fail --
  fixture is private to the test because the extension-blind registry
  would otherwise satisfy the inherit under randomized order
- dual_extension.lpc: children() is extension-blind across spellings;
  a ".c"-spelled inherit of an already-loaded .lpc program compiles
  (registry identity, the positive twin of the fail pin)
- save_object.lpc: ".lpc"/".c" are stripped before ".o" is appended

Verified: full suite x3 (ASan Debug, randomized order) + ctest 297/297,
and RelWithDebInfo ctest -L lpc / -LE lpc all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
Yucong Sun
592dfc0688 Prefer .lpc source extension; rename testsuite to .lpc
Source resolution is now explicit-extension-exact: load_object("/foo.c")
probes only foo.c, never foo.lpc (and vice versa); extension-less names
prefer .lpc and fall back to .c. Object identity stays extension-blind:
object names carry no extension, any spelling finds a loaded object, and
the registry is consulted before the filesystem. The caller's raw
spelling now flows through find_object()/inherit/master/simul_efun loads
instead of being pre-stripped away.

- filename_to_obname and otable basename() strip .lpc too (children())
- save_object() strips either source extension before appending .o
- replace_program()/function_exists() handle both suffixes
- testsuite: all LPC sources renamed to .lpc; runner globs, master
  get_include_path cases, and program-name assertions updated;
  README.md rewritten with the extension rules and suite conventions
- new single/tests/efuns/dual_extension.lpc pins exact-pick, fallback,
  no-crossover, identity, and registry-before-filesystem with .c/.lpc
  fixture pairs in /clone

Verified: ctest 297/297 and driver-autotest x3 (ASan Debug) plus
RelWithDebInfo ctest + autotest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
gesslar
1f1fc66e96
Support single test file execution via -ftest:/path syntax (#1159)
Add support for running individual test files with `-ftest:/path/to/file.c`
flag, allowing focused testing of specific test modules instead of full suite.

- Modified flag() to parse optional file path from -ftest:/path syntax
- Pass file path to tests.c main() for selective execution
- Added single_test cleanup via shutdown handler
- Fixed typo: "supproted" → "supported"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 12:33:11 -08:00
William
11092fd24a
Websocket telnet fixes (#1100)
* Change on_user_websocket_received to use maybe_schedule_user_command

* Remove unneeded comments

* Fix msdp_send_variable string type

* Add MSDP docs and docs for has_msp

* MSDP docs fix some copy paste data i missed

* Fix doc indexes

* Fix send_msdp_variable copy paste issue

* Move msdp index entries to proper alpha sorting

* Move protocol url to correct doc file

* Add testsuite support for msdp

* Fix typo in msdp index link

* Fix index link for send_msdp_variable
Add see also line to msp_enable for has_msp

* Fix some copy paste errors
2024-10-08 12:50:21 -07:00
gesslar
4a6fafb554
Updating external_start() to use debug_levels rather than debug_message to log (#1084)
* moving debug_messages to debug macro and set/clear_debug_level. updated testsuite command to use it

* updating doc for clear_debug_level

* making the messaging consistent for debug messages
2024-07-17 23:20:34 -07:00
Yucong Sun
1fd7f61df3
Adding two new mode: STREAM_TLS, STREAM_TLS_BINARY, also add two new efun socket_set_option() and socket_get_option() for setting TLS params. (#1033)
Also update test_tls.c and /std/http.c to showcase connecting to https://www.google.com
2023-12-22 20:54:54 -08:00
Yucong Sun
0c72b263ca add a string benchmark 2023-12-02 20:00:33 -08:00
Michael Programs
53b5e0e696 fix typo in crasher.c 2023-08-21 23:38:33 -07:00
Yucong Sun
0fa11d4952 external_start: use posix_spawn() to improve peformance, fix zombie child process 2022-12-27 03:19:06 -08:00
Yucong Sun
47d56698c4 Add windows support for PACAKGE_EXTERNAL 2022-06-19 14:02:27 -07:00
Yucong Sun
4ec62e92a6 Add speed benchmark for static/dynamic invoke 2022-04-01 20:06:01 -04:00
Yucong Sun
57fbff76be adds callouts command to test call_out timing. 2021-08-27 00:35:28 -07:00
Yucong Sun
6b32518707 Fix sanitizer warning for efun file_length() 2021-08-01 11:23:00 -07:00
Yucong Sun
b8437bbaae Adding json to speed test 2021-07-15 16:35:42 -07:00
Yucong Sun
334be92bf3 finalize tests 2021-07-11 09:29:16 -04:00
Yucong Sun
a395aea201 performance improvment in strsrch/explode
1. fast track short ascii string search
2. create EGCIterator to avoid multi-passing string.
2021-07-11 09:29:16 -04:00
Yucong Sun
062ad9e0c7 Adding string find/split (strsrch/explode) benchmark 2021-07-11 09:29:16 -04:00
Yucong Sun
e89db73d66 Fix apply_cache_lookup returing wrong runtime_index 2021-03-03 13:52:06 -08:00
Yucong Sun
9b39bd24f9 EFUN: dump_jemalloc() to generate memory dumps 2021-02-26 00:15:25 -08:00
Yucong Sun
d8b3663c85 revert send_gmcp(), act_mxp(), send_zmp(), telnet_nop(),
request_term_type(), start_request_term_type(), request_term_size() etc to behave as receive().
2021-02-23 14:05:22 -08:00
Yucong Sun
2d4c46dca4 Fix GA behavior and add a working input_to demo 2021-02-23 14:05:22 -08:00
Yucong Sun
93d6944941 Adding a refs command 2021-02-23 14:05:22 -08:00
Yucong Sun
cc86ff0e1f properly display dangling object 2021-02-23 14:05:22 -08:00
Yucong Sun
89b8804fdb restore TELNET_GA behavior to mudos 2021-02-23 14:05:22 -08:00
Yucong Sun
6d8441870c EFUN: int perf_counter_ns(), add VM benchmark reference in python 2021-02-23 14:05:22 -08:00
Yucong Sun
2db73a06c7 Make ws_ascii only accept utf8 frame, accept multi frame line too 2020-11-11 17:09:54 -08:00
Yucong Sun
742d43eb61 Adding config switch for MXP, GMCP, ZMP, MSSP 2020-09-10 15:56:44 -07:00
Yucong Sun
fafdcc7875 Implement basic UTF8 validation tests 2019-12-03 04:33:27 -05:00
Yucong Sun
b327a775dc
Migrate buildsystem to CMake (#431)
fix #361

* basic functional cmake rules
* Adding missing libraies
* Successfully compiled under cmake
* Make sure .sh always has eol=lf
* final cmake related changes
* Getting rid of autoconf automake and jemalloc
* Move testsuite to top directory
* auto config packages using cmake
* Fix file modes
* basic preprocessor
* Adding portbind back
* Fix travis
* Lower libevent requirement to 2.0
* Restore testsuite/log dir
2019-01-07 20:29:34 -08:00