Fix and document console.write()
Added example on using framed data transmission over the console.
This commit is contained in:
parent
13553ddf76
commit
231db92619
|
@ -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));
|
||||||
|
|
|
@ -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,8 +54,7 @@ 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)
|
||||||
```
|
```
|
||||||
|
@ -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")
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in New Issue