diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2025-01-05 00:18:05 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2025-01-05 00:18:05 +0100 |
commit | e99903778e81836fd306ade2d1a5f59396f85b52 (patch) | |
tree | 71041061968464504f795212c9243422a5deec4d /tools | |
parent | b8b01466f3ff0cceef0b718e06f501e2fd954d4d (diff) | |
download | liberty-master.tar.gz liberty-master.tar.xz liberty-master.zip |
Diffstat (limited to 'tools')
-rw-r--r-- | tools/wdye/wdye.c | 150 |
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; } |