Fix and document console.write()

Added example on using framed data transmission over the console.
This commit is contained in:
Jade Mattsson 2024-10-23 14:54:08 +11:00
parent 13553ddf76
commit 231db92619
2 changed files with 152 additions and 31 deletions

View File

@ -203,13 +203,19 @@ static int console_mode(lua_State *L)
} }
// Lua: console.write(string1, [string2], ..., [stringn]) // Lua: console.write(str_or_num [, str_or_num2 ... ])
static int console_write(lua_State *L) static int console_write(lua_State *L)
{ {
int total = lua_gettop(L); int total = lua_gettop(L);
for (int s = 1; s <= total; ++s) for (int s = 1; s <= total; ++s)
{ {
if (lua_isnumber(L, s)) if (lua_type(L, s) == LUA_TSTRING)
{
size_t len = 0;
const char *buf = lua_tolstring(L, s, &len);
retrying_write(buf, len);
}
else if (lua_isnumber(L, s))
{ {
int n = lua_tointeger(L, s); int n = lua_tointeger(L, s);
if (n < 0 || n > 255) if (n < 0 || n > 255)
@ -217,13 +223,6 @@ static int console_write(lua_State *L)
char ch = n; char ch = n;
retrying_write(&ch, 1); retrying_write(&ch, 1);
} }
else
{
luaL_checktype(L, s, LUA_TSTRING);
size_t len = 0;
const char *buf = lua_tolstring(L, s, &len);
retrying_write(buf, len);
}
} }
fflush(stdout); fflush(stdout);
fsync(fileno(stdout)); fsync(fileno(stdout));

View File

@ -39,16 +39,14 @@ To unregister the callback, specify `nil` as the function.
#### Example #### Example
```lua ```lua
-- when 4 chars is received. -- when 4 chars is received.
console.on("data", 4, console.on("data", 4, function(data)
function(data)
print("received from console:", data) print("received from console:", data)
if data=="quit" then if data=="quit" then
console.on("data", 0, nil) console.on("data", 0, nil)
end end
end) end)
-- when '\r' is received. -- when '\r' is received.
console.on("data", "\r", console.on("data", "\r", function(data)
function(data)
print("received from console:", data) print("received from console:", data)
if data=="quit\r" then if data=="quit\r" then
console.on("data", 0, nil) console.on("data", 0, nil)
@ -56,10 +54,9 @@ console.on("data", "\r",
end) end)
-- error handler -- error handler
console.on("error", console.on("error", function(err)
function(err)
print("error on console:", err) print("error on console:", err)
end) end)
``` ```
## console.mode() ## console.mode()
@ -81,8 +78,133 @@ Controls the interactivity of the console.
`nil` `nil`
#### Example #### Example
Implement a REL instead of the usual REPL
```lua ```lua
-- Implement a REL instead of the usual REPL
console.on("data", "\r", function(line) node.input(line.."\r\n") end) console.on("data", "\r", function(line) node.input(line.."\r\n") end)
console.mode(console.NONINTERACTIVE) console.mode(console.NONINTERACTIVE)
``` ```
Receive potentially binary data on the console, and process it without letting
it reach the Lua interpreter.
```lua
-- Instantiate a stream handling function that processes a classic framing
-- protocol: <STX><escaped-data><ETX> where <escaped-data> has the
-- STX, ETX and DLE symbols escaped as <DLE><STX>, <DLE><ETX>, <DLE><DLE>.
-- The chunk_cb gets called incrementally with partial stream data which is
-- effectively unescaped. When the end of frame is encountered, the done_cb
-- gets invoked.
function transmission_receiver(chunk_cb, done_cb)
local inframe = false
local escaped = false
local done = false
local STX = 2
local ETX = 3
local DLE = 16
local function dispatch(data, i, j)
if (j - i) < 0 then return end
chunk_cb(data:sub(i, j))
end
return function(data)
if done then return end
local from
local to
for i = 1, #data
do
local b = data:byte(i)
if inframe
then
if not from then from = i end -- first valid byte
if escaped
then
escaped = false
else
if b == DLE
then
escaped = true
dispatch(data, from, i-1)
from = nil
elseif b == ETX
then
done = true
to = i-1
break
end
end
else -- look for an (unescaped) STX to sync frame start
if b == DLE then escaped = true
elseif b == STX and not escaped then inframe = true
else escaped = false
end
end
-- else ignore byte outside of framing
end
if from then dispatch(data, from, to or #data) end
if done then done_cb() end
end
end
function print_hex(chunk)
for i = 1, #chunk
do
print(string.format("%02x ", chunk:sub(i,i)))
end
end
function resume_interactive()
console.on("data", 0, nil)
console.mode(console.INTERACTIVE)
end
-- The 0 may be adjusted upwards for improved efficiency, but be mindful to
-- always send enough data to reach the ETX marker.
console.on("data", 0, transmission_receiver(print_hex, resume_interactive))
console.mode(console.NONINTERACTIVE)
```
Example C program for encoding data suitable for the above.
```C
#include <stdio.h>
#define STX 0x02
#define ETX 0x03
#define DLE 0x10
int main(int argc, char *argv[])
{
putchar(STX);
int ch;
while ((ch = getchar()) != EOF)
{
switch(ch)
{
case STX: case ETX: case DLE: putchar(DLE); break;
default: break;
}
putchar(ch);
}
putchar(ETX);
return 0;
}
```
## console.write()
Provides ability to write raw, unformatted data to the console.
#### Syntax
`console.write(str_or_num [, str2_or_num ...])`
#### Parameters
- `str_or_num` Either
- A string to write to the console. May contain binary data.
- A number representing the character code to write the console.
Multiple parameters may be given, and they will be written in sequence.
#### Returns
`nil`
#### Example
Write "Hello world!\n" to the console, in a roundabout manner
```lua
console.write("Hello", 0x20, "world", 0x21, "\n")
```