aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2025-01-05 00:18:05 +0100
committerPřemysl Eric Janouch <p@janouch.name>2025-01-05 00:18:05 +0100
commite99903778e81836fd306ade2d1a5f59396f85b52 (patch)
tree71041061968464504f795212c9243422a5deec4d /tools
parentb8b01466f3ff0cceef0b718e06f501e2fd954d4d (diff)
downloadliberty-master.tar.gz
liberty-master.tar.xz
liberty-master.zip
Diffstat (limited to 'tools')
-rw-r--r--tools/wdye/wdye.c150
1 files changed, 134 insertions, 16 deletions
diff --git a/tools/wdye/wdye.c b/tools/wdye/wdye.c
index a827732..8d6446b 100644
--- a/tools/wdye/wdye.c
+++ b/tools/wdye/wdye.c
@@ -18,8 +18,6 @@
#define PROGRAM_NAME "wdye"
#define PROGRAM_VERSION "1"
-
-#define LIBERTY_WANT_POLLER
#include "../../liberty.c"
#include <lua.h>
@@ -34,6 +32,20 @@
#include <curses.h>
#include <term.h>
+static int64_t
+clock_msec (void)
+{
+#ifdef _POSIX_TIMERS
+ struct timespec tp;
+ hard_assert (clock_gettime (CLOCK_BEST, &tp) != -1);
+ return (int64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
+#else
+ struct timeval tp;
+ hard_assert (gettimeofday (&tp, NULL) != -1);
+ return (int64_t) tp.tv_sec * 1000 + *msec = tp.tv_usec / 1000;
+#endif
+}
+
// --- Pseudoterminal ----------------------------------------------------------
// This is largely taken from Advanced Programming in the UNIX® Environment,
// just without a bunch of bugs.
@@ -213,12 +225,19 @@ struct pattern
{
enum pattern_kind kind; ///< Tag
int ref_process; ///< Process for RE/EOF/DEFAULT
+ struct process *process; ///< Weak pointer to the process
regex_t *re; ///< Regular expression for RE
- int64_t timeout; ///< Timeout for TIMEOUT/DEFAULT
+ int64_t timeout; ///< Timeout for TIMEOUT/DEFAULT (s)
bool notransfer; ///< Do not consume process buffer
int ref_values; ///< Return values as a table reference
+ // Patterns are constructed in place, used once, and forgotten,
+ // so we can just shove anything extra in here.
+
struct error *e; ///< Error buffer
+ regmatch_t *matches; ///< Match indexes within input buffer
+ int64_t deadline; ///< Absolute timeout (Unix epoch)
+ bool eof; ///< End of file seen
// TODO(p):
// - ref_values is a LUA_TTABLE with arbitrary values.
@@ -238,6 +257,7 @@ xlua_pattern_new (lua_State *L, enum pattern_kind kind)
self->ref_process = LUA_NOREF;
self->timeout = -1;
self->ref_values = LUA_NOREF;
+ self->deadline = -1;
return self;
}
@@ -251,6 +271,7 @@ xlua_pattern_gc (lua_State *L)
luaL_unref (L, LUA_REGISTRYINDEX, self->ref_values);
if (self->e)
error_free (self->e);
+ free (self->matches);
return 0;
}
@@ -281,6 +302,7 @@ struct process
int terminal_fd; ///< Process stdin/stdout/stderr
pid_t pid; ///< Process ID
int ref_term; ///< Terminal information
+ struct str buffer; ///< Terminal input buffer
};
static struct process *
@@ -293,6 +315,7 @@ xlua_process_new (lua_State *L)
self->terminal_fd = -1;
self->pid = -1;
self->ref_term = LUA_NOREF;
+ self->buffer = str_make ();
return self;
}
@@ -305,6 +328,7 @@ xlua_process_gc (lua_State *L)
if (self->pid != -1)
kill (self->pid, SIGKILL);
luaL_unref (L, LUA_REGISTRYINDEX, self->ref_term);
+ str_free (&self->buffer);
return 0;
}
@@ -340,6 +364,11 @@ xlua_process_re (lua_State *L)
return luaL_error (L, "too many arguments");
struct pattern *pattern = xlua_pattern_new (L, PATTERN_RE);
+ lua_pushvalue (L, 1);
+ pattern->ref_process = luaL_ref (L, LUA_REGISTRYINDEX);
+ pattern->process = self;
+
+ // TODO(p): Try to use REG_STARTEND, when defined.
lua_getfield (L, 2, "nocase");
int flags = REG_EXTENDED;
@@ -358,6 +387,7 @@ xlua_process_re (lua_State *L)
const char *re = lua_tolstring (L, -1, &len);
if (!(pattern->re = regex_compile (re, flags, &pattern->e)))
return luaL_error (L, "%s", pattern->e->message);
+ pattern->matches = xcalloc (pattern->re->re_nsub, sizeof *pattern->matches);
lua_pop (L, 3);
xlua_newtablecopy (L, 2, 2, lua_rawlen (L, 2));
@@ -368,6 +398,7 @@ xlua_process_re (lua_State *L)
static luaL_Reg xlua_process_table[] =
{
{ "__gc", xlua_process_gc },
+ // TODO(p): __index should include the "term" object through ref_terminal.
{ "send", xlua_process_send },
{ "re", xlua_process_re },
// { "eof", xlua_process_eof },
@@ -572,6 +603,17 @@ xlua_spawn (lua_State *L)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#define DEFAULT_TIMEOUT 10
+
+static bool
+xlua_match (struct pattern *self,
+ int64_t now, struct pollfd *pfds, size_t pdfs_len)
+{
+ // PATTERN_RE will want to provide regexp matches to Lua... what do?
+ // Perhaps just remember submatch positions.
+ return false;
+}
+
static int
xlua_expect (lua_State *L)
{
@@ -579,27 +621,103 @@ xlua_expect (lua_State *L)
for (int i = 1; i <= nargs; i++)
luaL_checkudata (L, i, XLUA_PATTERN_METATABLE);
- struct poller poller;
- poller_init (&poller);
+ size_t patterns_len = nargs;
+ struct pattern **patterns = xcalloc (patterns_len, sizeof *patterns);
+ for (int i = 1; i <= nargs; i++)
+ patterns[i - 1] = luaL_checkudata (L, i, XLUA_PATTERN_METATABLE);
- // TODO(p): Add pattern terminals to an event loop,
- // add timers, run the loop...
+ // TODO(p): Clear any relevant pattern fields,
+ // such as match indexes, or eof (should eof be within process?)
- for (int i = 1; i <= nargs; i++)
+ // The liberty poller is not particularly appropriate for this use case.
+ struct pollfd *pfds = xcalloc (nargs, sizeof *pfds);
+ size_t pfds_len = 0;
+
+ int64_t first_timeout = INT64_MAX;
+ for (size_t i = 0; i < patterns_len; i++)
{
- struct pattern *pattern =
- luaL_checkudata (L, i, XLUA_PATTERN_METATABLE);
- switch (pattern->kind)
+ struct pattern *pattern = patterns[i];
+ if (pattern->kind == PATTERN_RE
+ || pattern->kind == PATTERN_EOF
+ || pattern->kind == PATTERN_DEFAULT)
{
+ // TODO(p): If a process has already seen EOF,
+ // don't add it to polling, or clear its .events.
+ // Further, once it starts seeing EOF, also clear its .events.
+ pattern->eof = false;
+
+ bool found = false;
+ for (size_t i = 0; i < pfds_len; i++)
+ if (pfds[i].fd == pattern->process->terminal_fd)
+ found = true;
+ if (!found)
+ pfds[pfds_len++] = (struct pollfd)
+ { .fd = pattern->process->terminal_fd, .events = POLLIN };
}
+ if (pattern->kind == PATTERN_TIMEOUT
+ || pattern->kind == PATTERN_DEFAULT)
+ {
+ if (pattern->timeout >= 0)
+ first_timeout = MIN (first_timeout, pattern->timeout);
+ else
+ first_timeout = MIN (first_timeout, DEFAULT_TIMEOUT);
+ }
+ }
+
+ // There is always at least a default timeout.
+ int64_t timeout = first_timeout != INT64_MAX
+ ? first_timeout
+ : DEFAULT_TIMEOUT;
+
+ // TODO(p): First, check if anything matches already.
+ // - The result of matching is an index to the matching pattern.
+
+ // TODO(p): We actually need to track elapsed time better,
+ // because succeeding in reading more data doesn't mean we succeed
+ // in matching anything.
+
+ int n = 0;
+ int64_t deadline = clock_msec () + timeout * 1000;
+restart:
+ n = poll (pfds, pfds_len, timeout * 1000);
+ if (n < 0)
+ {
+ // TODO(p): On error conditions, carefully destroy everything.
}
+ if (n == 0)
+ {
+ // TODO(p): This will match all pattern->timeout <= timeout,
+ // for any PATTERN_TIMEOUT or PATTERN_DEFAULT.
+ // If nothing matches, assume a null action.
+ }
+ else
+ {
+ for (size_t i = 0; i < patterns_len; i++)
+ {
+ struct pattern *pattern = patterns[i];
+ if (pattern->kind != PATTERN_RE
+ && pattern->kind != PATTERN_EOF
+ && pattern->kind != PATTERN_DEFAULT)
+ continue;
+
+ bool found = false;
+ for (size_t i = 0; i < pfds_len; i++)
+ if (pfds[i].fd == pattern->process->terminal_fd)
+ found = true;
+ if (!found)
+ continue;
+
+ // TODO(p): Read more process data.
+ }
+ }
+
+ // TODO(p): See if anything non-timeout matches now.
+ // If something matches, pcall-filter-execute (have to, anyway)
+ // pattern->ref_values.
- // TODO(p): Keep reading input into a buffer,
- // until there's a match.
- // - It seems we need to keep the buffer within the process object,
- // because we can read too much.
+ free (pfds);
+ free (patterns);
- poller_free (&poller);
return 0;
}