BBS:      TELESC.NET.BR
Assunto:  src/sftp/sftp.h sftp_client.c src/syncterm/Wren.adoc src/syncterm/scri
De:       Deuc¨
Data:     Tue, 28 Apr 2026 17:47:40 -0700
-----------------------------------------------------------
https://gitlab.synchro.net/main/sbbs/-/commit/50dfd1318ca341eaea03d98b
Modified Files:
	src/sftp/sftp.h sftp_client.c src/syncterm/Wren.adoc src/syncterm/scripts/syncterm.wren wrentest.wren src/syncterm/term.c wren_bind.c wren_host.c wren_host_internal.h
Log Message:
SyncTERM: Wren SFTP API + Input.nextEvent fiber-arg primitive

Adds a Wren-callable SFTP surface on top of the async sftpc_ library
and converts Input.nextEvent to the same fiber-arg shape.

Async ops take the fiber-to-resume as their first argument, fire the
underlying request, and return immediately:

  SFTP.(fiber, args...) -> null | SFTPError

null means the request was queued (the caller may yield to receive
the result via fiber.call); SFTPError means the request couldn't be
queued (session is gone, OOM at the foreign-method site)  no
callback will fire.  All other errors round-trip through the result
queue so the caller's yield surfaces them as an SFTPError too.

Common idioms:

  // fire and immediately await
  var r = SFTP.realpath(Fiber.current, ".") || Fiber.yield()

  // multi-fire event loop, demuxed by type
  SFTP.stat(Fiber.current, "/a")
  SFTP.stat(Fiber.current, "/b")
  Input.nextEvent(Fiber.current)
  while (true) {
    var x = Fiber.yield()
    if (x is SFTPStat)  ...
    if (x is KeyEvent)  { Input.nextEvent(Fiber.current); ... }
    if (x is SFTPError) break
  }

  // hook-friendly: callback fiber, calling fiber doesn't yield
  SFTP.realpath(Fiber.new {|r| ... }, ".")

Library additions (src/sftp/):

  sftpc_mkdir / sftpc_rmdir / sftpc_remove / sftpc_rename  four
  status-only async ops that were stripped in 97dd3955d6.  They
  reuse the bare struct sftpc_pending plus parse_status_only and
  share a do_one_path helper for the single-path variants.

Wren classes (foreign):

  SFTP         static-only; available, pubdir, plus 12 async ops
                (realpath, stat, opendir, readdir, close, open,
                read, write, mkdir, rmdir, remove, rename).
  SFTPEntry    name, longname, size, mtime, isDir, hash.
  SFTPStat     size, mtime, atime, mode, uid, gid.
  SFTPHandle   opaque server file/dir handle; finalizer fires
                sftpc_close fire-and-forget when GC'd.
  SFTPError    code (sftp_err_code_t), serverStatus (SSH_FX_*),
                message, isTransient.
  FileFlag     six SSH_FXF_* bitmask constants for SFTP.open.
Zero-copy delivery: the recv-thread cb runs under state->mtx and
just stamps ctx->pending = p + pushes onto the result queue.  The
owner-thread deliver fn reads typed-pending fields directly
(sftp_str_t bytes, sftpc_attrs getters, sftpc_dir_entry array) and
frees the pending in the queue's free fn.  No memcpy under the
mutex; the only Wren-heap copy is wrenSetSlotBytes / strdup at
delivery, which is unavoidable.

Input.nextEvent converted to the same shape:

  Input.nextEvent(fiber) -> null

...replacing the prior Input.park_(fiber) primitive + Input.nextEvent()
auto-yielding wrapper.  The wrapper existed only to work around now-
removed quirks (implicit CTerm.suspended setting, the now-dropped
Hook.onOutput).  Dropping it lets hooks pass Fiber.new {|ev| ...}
for callback-style delivery without nesting Fiber.new {...}.call().
Throws if another fiber is already registered (single-subscriber
is structural).

Wren.adoc, wrentest.wren, and the relevant internal-comment
references updated to match the new API.
n
---
  mSynchronetn  hgVertrauen n hHome of Synchronet n gh[vert/cvs/bbs].synchro.net

-----------------------------------------------------------
[Voltar]