pax_global_header00006660000000000000000000000064122031455200014504gustar00rootroot0000000000000052 comment=d8519f7eda2ed1f2d46eef2fa608220ca259a354 lua-lpty-1.0.1/000077500000000000000000000000001220314552000132525ustar00rootroot00000000000000lua-lpty-1.0.1/Makefile000066400000000000000000000022111220314552000147060ustar00rootroot00000000000000# simple Makefile for lpty. Works for Linux, MacOS X, probably other unixen # # Gunnar Zötl , 2011. # Released under MIT/X11 license. See file LICENSE for details. # try some automatic discovery OS = $(shell uname -s) LUAVERSION = $(shell lua -v 2>&1|awk '{split($$2, a, "."); print a[1] "." a[2]}') LUADIR = $(shell dirname `which lua`) LUAROOT = $(shell dirname $(LUADIR)) # Defaults CC = gcc TARGET = lpty.so DEBUG= #-g -lefence CFLAGS=-O2 -fPIC $(DEBUG) INCDIRS=-I$(LUAROOT)/include LIBDIRS=-L$(LUAROOT)/lib LDFLAGS=-shared $(DEBUG) INSTALL_ROOT=/usr/local SO_INST_ROOT=$(INSTALL_ROOT)/lib/lua/$(LUAVERSION) LUA_INST_ROOT=$(INSTALL_ROOT)/share/lua/$(LUAVERSION) # OS specialities ifeq ($(OS),Darwin) CC = cc LDFLAGS = -bundle -undefined dynamic_lookup -all_load endif all: $(TARGET) $(TARGET): lpty.o $(CC) $(LDFLAGS) -o $@ $(LIBDIRS) $< lpty.o: lpty.c $(CC) $(CFLAGS) $(INCDIRS) -c $< -o $@ install: all cp $(TARGET) $(SO_INST_ROOT) test: all cd samples && LUA_CPATH=../\?.so lua lptytest.lua clean: find . -name "*~" -exec rm {} \; find . -name .DS_Store -exec rm {} \; find . -name "._*" -exec rm {} \; rm -f *.o *.so core lua-lpty-1.0.1/doc/000077500000000000000000000000001220314552000140175ustar00rootroot00000000000000lua-lpty-1.0.1/doc/LICENSE000066400000000000000000000021061220314552000150230ustar00rootroot00000000000000 Copyright (c) 2010-2013 Gunnar Zötl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lua-lpty-1.0.1/doc/README000066400000000000000000000346131220314552000147060ustar00rootroot00000000000000 README lpty a library that allows lua to start processes and control them via pty Author: Gunnar Zötl , 2010-2013. Released under MIT/X11 license. See file LICENSE for details. Introduction This is a simple interface to pty functionality, providing the ability to fork a process and run it under pty control. It does not try to mimic the posix API but instead focuses on the function of running and controlling a program. The interface is very bare bones, no additional functionality is provided, especially nothing like expect, which should be a different package. This has been developed on Linux and tested on MacOS X. It should compile and run on any platform supporting the Unix 98 interface to pty's. Support for additional Unixen might follow in the future, depending on demand (especially my own ;) ). No support for windows is planned, as ptys are a Unix thing. It might work with cygwin. Installing This uses only stuff that comes with your system, so you do not need to install any additional libraries to satisfy any dependencies. There are 2 options for building and installing this module, the first and most obvious being luarocks. Normally, calling: sudo luarocks install lpty or when you have an unpacked source folder: sudo luarocks make should do the right thing. There is also a Makefile in the distribution, that has been created for use with Linux and MacOS X. It may work for other platforms, or you may have to edit it by hand. Calling: make && make install will build the module and install it to some standard location for your version of lua. Using Load the module with: lpty = require "lpty" Note: since version 1.0, lpty no longer registers a global table lpty with lua. Constructor pty = lpty.new([options]) Creates and fully initializes the master side of a pty, and returns an object to interact with the pty. Options for the pty may be passed as key - value pairs in a table. These options are: throw_errors If true, errors are thrown, otherwise they are returned as a standard nil, "error message" pair. Default is false. no_local_echo If true, stuff sent to the parent side of the pty will not be echoed back to the parent side, otherwise it will be echoed back. Default is false. Beware that this may not always work as expected. use_path If true, the pty:startproc() method for the created pty will search for an executable file if the specified filename does not contain a slash (/) character. The search path is the path specified in the parents environment by the PATH variable, no default will be used. If false, the path to the executable must be explicitly specified. Default is true. If you do not want to pass any options, you may omit the options table altogether. Process handling pty:startproc(command, arg1, arg2, ...) Starts a process with the slave side of the pty as its controlling terminal. The child processes stdin, stdout and stderr are set to use the slave side of th pty. The command and its arguments must be separate arguments to the startproc method. If the pty has been created with use_path=true (the default), then this uses the PATH environment variable of the calling process (the one using lpty) to find any executables, so in that case specifying a full path to the command is not always necessary. Returns true if the call to fork() was successful. If anything goes wrong whilst starting the new process, an error is signalled (that is, thrown or returned as two values nil, "error message", depending on the setting of the throw_errors flag. If there is already an active process attached to the pty, the new process is not started and the method returns false. The pty:startproc() method can not check whether running the command in the child process or basically anything that happens in the child process after the fork is successful. If pty:startproc() returns true, this means that setting up the client side of the tty and fork()ing went well and yielded a running child process. You may want to check wether it is still running when you need it using the pty:hasproc() method, especially if you're dealing with an interactive program. If the program the child was supposed to start is not running when you expect it to, you will have to call the pty:exitstatus() method and maybe even check the contents of the pty for the reason. pty:endproc([kill]) Terminates the process running with the pty as its controlling terminal. If the optional parameter kill is false or omitted, send the child process a SIGTERM, otherwise send a SIGKILL. It is not an error to call this on a pty object that has no active child processes. You do not need to call it when you have already terminated the child process using more civilized means (like, for example, sending it a quit command it understands). After the child process has been terminated, a new one can be started using the same PTY. pty:hasproc() returns true if the pty has an active child process, false if not (none has been created, the child has terminated, ...). pty:exitstatus() Determines the reason why a previous child process has terminated. Returns a the kind of termination and the code. Possible combinations are: false, nil if the pty has an active child process or never had one, 'exit', exitcode if the child process has exited normally, 'sig', signum if the child process was terminated by a signal, 'unk', 0 if the reason for the death of the child process can not be determined any more. The pty:exitstatus() method works by having a fixed length ringbuffer, in which the signal handler responsible for reaping child processes places what waitpid() returns. Because of the fixed size, when you create a lot of processes, the ring buffer might wrap before you retrieve the exit status of your process, in which case the 'unk', 0 values will be returned, 'unk' meaning "unknown". If you hit this a lot, you may want to check the exit status earlier, or if that does not help, change the value for EXITSTATUS_BUFSIZ in lpty.c. Process Environments pty:getenviron() Returns a table containing the environment a child process of the pty will be executed in. If no custom environment has been set using the pty:setenviron() method, this will be the environment the parent process is running in. pty:setenviron(env) Sets a custom environment for the child process to be executed within the pty. The argument must be a table containing a complete environment for the child process. If you want to modify the current environment, obtain it with the pty:getenviron() method, modify it and pass it into this method as a new environment. Calling this function with nil as its argument resets the environment to the environment the parent process is running in. Communication pty:readok([timeout]) returns true if the pty has data available to be read, false otherwise. If a timeout value is specified, the call will wait at most that amount of seconds for data to become available to read. No value is equivalent to a timeout of 0 seconds. pty:read([timeout]) reads data from the pty. This is a blocking read, so you can specify an optional timeout in seconds after which the read should return to the caller whether there was data or not. If the timeout is omitted, the read will block until there is something to read. Returns the data read, or nil if the request timed out or was interrupted (failed with EINTR or ECHILD). If an error occurred during the read attempt, it is signalled to lua. Note: unless you specified no_local_echo=true when creating the pty, this reads data output from the client process as well as data sent to the client process from the controlling skript. The pty:read() method reads its data into a buffer. The size of this buffer determines how much data can be returned from an individual read. It is set by a #define in the module source and by default is set to 4096. Considering the C-typical terminating zero, this will leave a maximum of 4095 bytes to be read in one go. In order to fetch all available data, just loop while pty:readok() returns true and concatenate the results. pty:sendok([timeout]) returns true if the pty can accept data sent to it, false otherwise. If a timeout value is specified, the call will wait at most that many seconds for the pty to accept data, No value is equivalent to a timeout of 0 seconds. pty:send(data [, timeout]) sends data to the pty. This will then be available to the child process on its stdin. As sending data may block, there is an optional argument for a timeout in seconds on the send operation. If the timeout argument is omitted, pty:send() will block until the pty can accept the data. Returns the number of bytes written, or nil if the send timed out or was interrupted (failed with EINTR or ECHILD). If an error occurred during the attempt to write it is signalled to lua. The pty:send() method may not send all data you gave it in one go. For small amounts of data this is quite improbable, but in industrial strength applications you should probably check the return value of the pty:send() method against the length of the data you intended to send. pty:flush([mode]) flushes data from the pty. mode is an optional string argument. If mode is "i", flush data received but not read. If mode is "o", flush data written but not transmitted. If mode is anything else or omitted, flush both sides. Note that the mode "o" is hardly ever useful, because when send() returns, the data it claims to have sent will already be transmitted to the client side. Information pty:ttyname() return the tty name of the slave side of the pty. pty:getfd() return the file handle of the master side of the pty. This is intended for use with luasocket's select() or luaposix's poll() and similar functions. Additional Details The local echo thing As written in the description of the no_local_echo flag, this may not always work as expected. Programs are free to set their terminals to whatever settings they like, and indeed some programs do, or even completely roll their own local echo policy. The most notable piece of code to do this is Gnu readline, though it seems that this does not happen for all versions of it. On Mac OS X, it certainly does, whereas on my Ubuntu Box it does not. So if you write something using lpty that is supposed to be used on different unixen, you better not rely on no_local_echo to do what you think it should. Communication and process handling If you have a pty without a running child process, you can still write to it. Anything you write to the pty will be available as input to a child process that you start later. You can also read from a pty without a child process, but only what you have written to it - unless you specified no_local_echo=true for pty creation, in which case there is nothing to read from it. This means that if you (or the last active child process) have nothing written to it, a read with timeout will always time out, and a read without timeout will block. Also, if you read stuff back from the master side of a pty without a child, that you have written there, it will not be removed from a subsequent child process's input. Starting a process takes some time, so after you have called pty:startproc() it may take a while for data to become available from the child process. So don't be surprised if a pty:readok() fails directly after a pty:startproc(). Similar things are true for calls to pty:hasproc(). If you call pty:hasproc() directly after pty:startproc(), chances are it will return true whatever the outcome of starting the child process was, because it just may not have had enough time to crash. Calling pty:hasproc() will yield a much saner result if you do it after, for example, a pty:read() timed out when you didn't expect it to, or similar occurrences. Garbage disposal When a lpty object is garbage collected, its master side pty handle is closed, and if it has a child process, that is terminated with a SIGKILL. So it is reasonably safe to let pty's with active processes become garbage, it just isn't very friendly towards the child processes. Example A very simple example for lpty follows. In a real application you should probably do some checking for errors and stuff output by your child process. This uses bc to compute the sum of 2 numbers: lpty = require "lpty" p = lpty.new() p:startproc("lua") p:read() -- skip startup message p:send("=111+234\n") r = p:read() print("Result is "..r) p:send("os.exit()\n") -- terminate lua all friendly-like print("Done.") Also, see the samples folder in the distribution archive. References Read up on ptys on your local friendly linux system: /www.kernel.org/doc/man-pages/online/pages/man7/pty.7.html>man 7 pty and friends. This link is linux specific, but as lpty is implemented using Unix 98 pseudo ttys, which Linux is using, the information is probably valid whereever lpty runs. lua-lpty-1.0.1/lpty-1.0.1-1.rockspec000066400000000000000000000013301220314552000165630ustar00rootroot00000000000000package = "lpty" version = "1.0.1-1" source = { url = "http://www.tset.de/downloads/lpty-1.0.1-1.tar.gz" } description = { summary = "A simple facility for lua to control other programs via PTYs.", detailed = [[ This is a simple wrapper around unix PTYs. PTYs can be opened, lua controls the master side of the PTY. Programs can be started with the client side of those PTYs as their controlling terminals. ]], homepage = "http://www.tset.de/lpty/", license = "MIT/X11", maintainer = "Gunnar Zötl " } supported_platforms = { "unix" } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { lpty = { sources = { "lpty.c" }, }, }, copy_directories = { 'doc', 'samples' }, } lua-lpty-1.0.1/lpty.c000066400000000000000000000603361220314552000144160ustar00rootroot00000000000000/* lpty.c * * provide simple pty interface for lua * * Gunnar Zötl , 2010-2013 * Released under MIT/X11 license. See file LICENSE for details. */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include #include #include #include extern char** environ; #include "lua.h" #include "lauxlib.h" #define LPTY_VERSION "1.0.1" #define LPTY "lPtyHandler" #define TOSTRING_BUFSIZ 64 #define READER_BUFSIZ 4096 #define EXITSTATUS_BUFSIZ 16 #if LUA_VERSION_NUM == 501 #define luaL_newlib(L,funcs) lua_newtable(L); luaL_register(L, NULL, funcs) #define luaL_setfuncs(L,funcs,x) luaL_register(L, NULL, funcs) /* I realize these do not behave quite the same, but for the purposes of * this module this will do */ #define lua_setuservalue(L, idx) lua_setfenv(L, idx) #define lua_getuservalue(L, idx) lua_getfenv(L, idx) #endif /* structure for pty userdata */ typedef struct _lpty_pty { int m_fd; /* file descriptor for pty master side */ int s_fd; /* file descriptor for pty slave side */ pid_t child; /* pid of process attached to this pty */ struct { unsigned int throwerrors :1; unsigned int nolocalecho :1; unsigned int usepath :1; } flags; } lPty; /*** C level child process related utility functions ***/ /* structure to cache caught exit stati in. Not very pretty... but works. */ static struct { int cur; struct { pid_t child; int status; } ecodes[EXITSTATUS_BUFSIZ]; } _lpty_exitstatus_buffer; /* helper function to set SIGCHLD handler */ static int _lpty_set_sigchld_handler(void (*handler)(int)) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; return sigaction(SIGCHLD, &sa, NULL); } /* signal handler for SIGCHLD * doesn't do much, just collect all child processes that have died. */ static void _lpty_sigchld_handler(int sig) { pid_t child; int status; /* WNOHANG because this might occur for a child that has already been * finalized (most probably with __gc) */ while ((child = waitpid(-1, &status, WNOHANG)) > 0) { _lpty_exitstatus_buffer.ecodes[_lpty_exitstatus_buffer.cur].child = child; _lpty_exitstatus_buffer.ecodes[_lpty_exitstatus_buffer.cur].status = status; _lpty_exitstatus_buffer.cur = (_lpty_exitstatus_buffer.cur + 1) % EXITSTATUS_BUFSIZ; } } /* cleanup function to be called at program exit. SIGCHLD signals may arrive * after this shared library has been unmapped, which will result in a segmentation * fault! So we reset the signal handler on exit, and all is well. */ static void _lpty_sigchld_handlerexit_cleanup(void) { _lpty_set_sigchld_handler(SIG_DFL); } /* _lpty_hasrunningchild * * check wether a pty has a running child process * * Arguments: * pty the pty structure to check for running children * * Returns * 0 if there are no running child processes or 1 if the is one. */ static int _lpty_hasrunningchild(lPty *pty) { pid_t child = pty->child; /* no child started yet */ if (child == -1) return 0; /* we send the child a 0 signal. If this succeeds, there is a running child. */ if (kill(child, 0) == 0) return 1; /* if we get here, there is no child. */ return 0; } /*** Userdata handling ***/ /* lpty_toLPty * * If the value at the given acceptable index is a full userdata, returns its block address. * Otherwise, returns NULL. * * Arguments: * L Lua State * index stack index where the userdata is expected */ static lPty* lpty_toLPty(lua_State *L, int index) { lPty *pty = (lPty*) lua_touserdata(L, index); return pty; } /* lpty_checkLPty * * Checks whether the function argument narg is a userdata of the type LPTY. If so, returns * its block address, else throw an error. * * Arguments: * L Lua State * index stack index where the userdata is expected */ static lPty* lpty_checkLPty(lua_State *L, int index) { lPty *pty = (lPty*) luaL_checkudata(L, index, LPTY); return pty; } /* lpty_pushLPty * * create a new, empty lpty userdata and push it to the stack. * * Arguments: * L Lua state */ static lPty* lpty_pushLPty(lua_State *L) { lPty *pty = (lPty*) lua_newuserdata(L, sizeof(lPty)); luaL_getmetatable(L, LPTY); lua_setmetatable(L, -2); lua_newtable(L); lua_setuservalue(L, -2); return pty; } /*** Housekeeping metamethods ***/ /* lpty_gc * * __gc metamethod for the pty userdata. * Kills child process, closes all fd's. * * Arguments: * L Lua State * * Lua Stack: * 1 pty object */ static int lpty__gc(lua_State *L) { lPty *pty = lpty_toLPty(L, 1); /* if here is a running child process, dispose of it. * We do the waitpid() here because this might occur on program shutdown * and thus the SIGCHLD might be delivered after we've terminated. WNOHANG * ensures that we don't bother waiting for a dead child that has already * been reaped, and keeping the signal handler active ensures that no dying * child is left behind. */ if (_lpty_hasrunningchild(pty)) { /* no need to be gentle, this process has been abandoned. */ kill(pty->child, SIGKILL); /* also, no need to collect exit status, as the controlling object ist just being collected */ waitpid(pty->child, NULL, WNOHANG); } if (pty->m_fd > 0) close(pty->m_fd); if (pty->s_fd > 0) close(pty->s_fd); return 0; } /* lpty_toString * * __tostring metamethod for the lpty userdata. * Returns a string representation of the lpty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata */ static int lpty__toString(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); char buf[TOSTRING_BUFSIZ]; /* length of type name + length of hex pointer rep + '0x' + ' ()' + '\0' */ if (strlen(LPTY) + (sizeof(void*) * 2) + 2 + 4 > TOSTRING_BUFSIZ) return luaL_error(L, "Whoopsie... the string representation seems to be too long."); /* this should not happen, just to be sure! */ sprintf(buf, "%s (%p)", LPTY, pty); lua_pushstring(L, buf); return 1; } /* metamethods for the pty userdata */ static const luaL_Reg lpty_meta[] = { {"__gc", lpty__gc}, {"__tostring", lpty__toString}, {0, 0} }; /* _lpty_error * * Helper function for error handling: if dothrow is true, throw the error, * otherwise pack it onto the lua stack and return 2 * * Arguments: * L Lua State * dothrow 1 if the error should be thrown, 0 otherwise * msg Error message * * Returns: * 2 if the error should be returned to lua, or not at all if it is thrown. * * Lua Stack: * - * * Lua Returns: * +1 nil * +2 error messagel */ static int _lpty_error(lua_State *L, int dothrow, char *msg, ...) { va_list ap; va_start(ap, msg); char buf[BUFSIZ]; vsnprintf(buf, BUFSIZ, msg, ap); if (dothrow) return luaL_error(L, buf); else { lua_pushnil(L); lua_pushstring(L, buf); return 2; } } /* _lpty_optboolean * * helper for optional boolean options, when nil is found on the stack, * the default value is returned. * * Arguments: * L Lua State * idx index on the stack where the value is to be found * dfl default value for when the value it index idx on the stack is nil * * Returns: * either the value on the stack, converted to a truth value, or the * default value, if there was nil on the stack. */ static int _lpty_optboolean(lua_State *L, int idx, int dfl) { if (lua_isnil(L, idx)) return dfl; else return lua_toboolean(L, idx); } /* lpty_new * * create a new lpty object, initialize it, put it into a userdata and * return it to the user. * * Arguments: * L Lua State * * Lua Stack: * - * * Lua Returns: * +1 the lpty userdata */ static int lpty_new(lua_State *L) { /* create pty master side */ int mfd = posix_openpt(O_RDWR); int sfd = -1; int failed = (mfd < 0); int throwe = 0; /* throw errors, default = no */ int usep = 1; /* use path, default = yes */ int nle = 0; /* no local echo, default 0 */ /* check for options */ if (lua_gettop(L) > 0) { luaL_checktype(L, 1, LUA_TTABLE); lua_pushstring(L, "throw_errors"); lua_rawget(L, 1); throwe = _lpty_optboolean(L, 2, throwe); lua_pop(L, 1); lua_pushstring(L, "no_local_echo"); lua_rawget(L, 1); nle = _lpty_optboolean(L, 2, nle); lua_pop(L, 1); lua_pushstring(L, "use_path"); lua_rawget(L, 1); usep = _lpty_optboolean(L, 2, usep); lua_pop(L, 1); } if (mfd > 0) { /* setup parent side of pty. BEWARE: * behaviour of grantpt is undefined if a SIGCHLD handler is active */ struct sigaction oldhandler; _lpty_set_sigchld_handler(SIG_DFL); failed = grantpt(mfd); _lpty_set_sigchld_handler(_lpty_sigchld_handler); failed = failed || unlockpt(mfd); /* open slave side of pty */ if (!failed) { char *ttyn = ptsname(mfd); if (ttyn) { sfd = open(ttyn, O_RDWR); failed = (sfd < 0); } else failed = 1; } /* cleanup if anything went wrong */ if (failed) { close(mfd); } } if (failed) return _lpty_error(L, throwe, "pty initialisation failed: %s", strerror(errno)); lPty *pty = lpty_pushLPty(L); pty->m_fd = mfd; pty->s_fd = sfd; pty->child = -1; pty->flags.nolocalecho = nle; pty->flags.throwerrors = throwe; pty->flags.usepath = usep; return 1; } /*** Process handling ***/ /* _lpty_makeenv * * create an environment usable with execve() from the lpty-userdatas stored * custom environment. Returns NULL if no special environment is requested. * * Arguments: * L lua State * * Returns: * an array of strings containing "name=value" pairs, just like environ */ static char **_lpty_makeenv(lua_State *L) { int nenv = 16; char **env = NULL; lua_getuservalue(L, 1); lua_rawgeti(L, -1, 1); lua_remove(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 1); } else { const char *k, *v; char *c; int n = 0; env = calloc(nenv, sizeof(char*)); lua_pushnil(L); while (lua_next(L, -2) != 0) { /* we only care for the things with "real" names */ if (lua_type(L, -2) == LUA_TSTRING) { k = lua_tostring(L, -2); v = lua_tostring(L, -1); c = malloc(strlen(k) + 1 + strlen(v) + 1); sprintf(c, "%s=%s", k, v); env[n++] = c; if (n >= nenv + 1) { nenv = nenv * 2; env = realloc(env, nenv * sizeof(char*)); } } lua_pop(L, 1); /* value */ } env[n] = NULL; lua_pop(L, 2); /* key and environ table */ } return env; } /* _lpty_freeenv * * free an environment created with _lpty_makeenv above * * Arguments: * env environment created with _lpty_makeenv * * Returns: * - */ static void _lpty_freeenv(char **env) { char *c, **e = env; if (env == NULL) return; while (c = *e++) free(c); free(env); } /* _lpty_execvpe * * This function will search for an executable file if the specified filename * does not contain a slash (/) character. The search path is the path * specified in the environment by the PATH variable. If the file can not * be found along the search path, the function will return -1 with errno * set to ENOENT. If the file has been found but we were not allowed to * execute it, we continue to search along the path. If we finally fail, * in this case, the function will return -1 with errno set to EACCES. * * Arguments: * filename name (+path) of file to execute * argv arguments to said executable * envp environment for execution attempt. * * Returns: * never if succesfull, -1 on failure. */ static int _lpty_execvpe(const char *filename, char *const argv[], char *const envp[]) { if (strchr(filename, '/')) { return execve(filename, argv, envp); } else { char *protopath = getenv("PATH"); char *path = strdup(protopath); char *p = path, *q; char *pbuf = malloc(strlen(protopath) + strlen(filename) + 2); int e = ENOENT; /* find individual components of the path and try them */ q = strchr(p, ':'); while (q) { *q = 0; sprintf(pbuf, "%s/%s", p, filename); execve(pbuf, argv, envp); /* if that succeeded, we will never get here */ if (errno == EACCES) e = errno; p = q + 1; q = strchr(p, ':'); } sprintf(pbuf, "%s/%s", p, filename); execve(pbuf, argv, envp); if (errno == EACCES) e = errno; /* if we got here, execve() failed for all alternatives. */ free(pbuf); errno = e; return -1; } } /* lpty_startproc * * start a process with our pty as its controlling terminal. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1 false if the re was already an active subprocess of this pty, true if * not and we started one. * * Note: * We can not determine wether the command to be spawned in the child process * was successful. */ static int lpty_startproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *cmd = luaL_checkstring(L, 2); /* if pty already has an active child process we just return false and do * nothing */ if (_lpty_hasrunningchild(pty)) lua_pushboolean(L, 0); else { pid_t child; int ttyfd = pty->s_fd; _lpty_set_sigchld_handler(_lpty_sigchld_handler); /* now start child process */ child = fork(); if (child == 0) { /* child process */ /* fill execvp args array from function arguments */ int nargs = lua_gettop(L) - 1; const char **args = calloc(nargs + 1, sizeof(char*)); int i; args[0] = cmd; for (i = 1; i < nargs; ++i) args[i] = lua_tostring(L, 2 + i); args[nargs] = NULL; /* suppress local echo on slave side if wanted */ if (pty->flags.nolocalecho) { struct termios ttys; tcgetattr(ttyfd, &ttys); ttys.c_lflag &= ~(ECHO | ICANON); tcsetattr(ttyfd, TCSANOW, &ttys); } /* prepare child processes standard file handles */ dup2(ttyfd, 0); dup2(ttyfd, 1); dup2(ttyfd, 2); /* need to create new session id for slave in order for the tty to * become a controlling tty */ if (setsid() < (pid_t)0) { fprintf(stderr, "lpty failed to create new session id."); exit(EXIT_FAILURE); /* we need to terminate here! */ } /* reset SIGCHLD handler then start our process */ _lpty_set_sigchld_handler(SIG_DFL); char **e = _lpty_makeenv(L); if (pty->flags.usepath) _lpty_execvpe(cmd, (char* const*)args, e ? e : environ); else execve(cmd, (char* const*)args, e ? e : environ); _lpty_freeenv(e); /* if we ever get here, an error has occurred. * Note: this error will only be visible as output to the pty from the parent side! */ free(args); fprintf(stderr, "error: lpty failed to start child process: %s", strerror(errno)); exit(EXIT_FAILURE); /* we need to terminate here! */ } else if (child > 0) { /* parent process: clean up, store child pid, return success */ pty->child = child; lua_pushboolean(L, 1); } else { return _lpty_error(L, pty->flags.throwerrors, "lpty failed to create child process: %s", strerror(errno)); } } return 1; } /* lpty_endproc * * kill the process corresponding to our pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) flag wether to send SIGTERM (default, false) or SIGKILL (true) * * Lua Returns: * - * * Note: * No error is generated if the child process was not active any more. */ static int lpty_endproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); int sigkill = 0; if (lua_gettop(L) > 1) { luaL_checktype(L, 2, LUA_TBOOLEAN); sigkill = lua_toboolean(L, 2); } if (_lpty_hasrunningchild(pty)) { if (sigkill) kill(pty->child, SIGKILL); else kill(pty->child, SIGTERM); } return 0; } /* lpty_hasproc * * Check wether this pty has an active process attached to it. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1 true if there is an active subprocess, false if not. */ static int lpty_hasproc(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); int hasit = _lpty_hasrunningchild(pty); lua_pushboolean(L, hasit); return 1; } /* lpty_exitstatus * * Tries to find the exit status of the last process running in a pty. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1, +2 false, nil if the is an active subprocess * 'exit', code if the previous subprocess exited via exit * 'sig', signum if the previous process was terminated by a signal * 'unk', 0 if we have no information about the previous process */ static int lpty_exitstatus(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); if (!_lpty_hasrunningchild(pty) && pty->child != -1) { int i; int r = 0; for (i = 0; i < EXITSTATUS_BUFSIZ; ++i) { if (_lpty_exitstatus_buffer.ecodes[i].child == pty->child) { int status = _lpty_exitstatus_buffer.ecodes[i].status; if (WIFEXITED(status)) { lua_pushliteral(L, "exit"); lua_pushnumber(L, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { lua_pushliteral(L, "sig"); lua_pushnumber(L, WTERMSIG(status)); } r = 2; break; } } if (i == EXITSTATUS_BUFSIZ) { lua_pushliteral(L, "unk"); lua_pushnumber(L, 0); } } else { lua_pushboolean(L, 0); lua_pushnil(L); } return 2; } /*** Environment ***/ /* lpty_getenviron * * return the environment the child process is executed in * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * * Lua Returns: * +1 a table containing the current environment for the child process */ static int lpty_getenviron(lua_State *L) { /* lPty *pty = lpty_checkLPty(L, 1); */ char *c = NULL, **e = environ, *p; size_t buflen = 64; char *buf = malloc(buflen); lua_getuservalue(L, 1); lua_rawgeti(L, 2, 1); lua_remove(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_newtable(L); while (c = *e++) { if (strlen(c) >= buflen) { buflen += strlen(c); buf = realloc(buf, buflen); } strcpy(buf, c); p = strchr(buf, '='); *p = 0; ++p; lua_pushstring(L, (const char*) buf); lua_pushstring(L, (const char*) p); lua_rawset(L, -3); } } free(buf); return 1; } /* lpty_setenviron * * set the environment the child process is executed in * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 table containing environment for the child process * * Lua Returns: * - */ static int lpty_setenviron(lua_State *L) { /* lPty *pty = lpty_checkLPty(L, 1); */ if (!lua_isnil(L, 2) && !lua_istable(L, 2)) luaL_argerror(L, 2, "must be table or nil"); lua_getuservalue(L, 1); lua_pushvalue(L, 2); lua_rawseti(L, -2, 1); lua_pop(L, 1); return 0; } /*** Terminal I/O ***/ /* _lpty_waitfordata * * wait for the master side of a pty to have readable data available or to * accept data. * * Arguments: * pty the pty to wait on * timeo a timeout value in seconds, wait no longer than this * send if 0 then check for read possible, otherwise check for write * * Returns: * 0 if no data is available to read, 1 if there is data available to read. */ static int _lpty_waitfordata(lPty *pty, double timeo, int send) { fd_set rfds; struct timeval tv; int ok = -1; FD_ZERO(&rfds); FD_SET(pty->m_fd, &rfds); tv.tv_sec = (int)timeo; tv.tv_usec = (int)((timeo - tv.tv_sec) * 1000000); if (send == 0) ok = select(pty->m_fd + 1, &rfds, NULL, NULL, &tv); else ok = select(pty->m_fd + 1, NULL, &rfds, NULL, &tv); return ok; } /* lpty_readok * * Check wether the master side of this pty has data available for reading * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 true if there is data available to read, false if not. */ static int lpty_readok(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, 0); int ok = _lpty_waitfordata(pty, timeo, 0); lua_pushboolean(L, ok > 0); return 1; } /* lpty_read * * read data from the master side of a pty. * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 the data read from the master side of the pty, or nil if the read timed * out * * Note: * you also read back the stuff written to the pty with lpty_write() below! */ static int lpty_read(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, -1); char buf[READER_BUFSIZ]; // should probably be more flexible int readn = -1; int ok = 1; if (timeo >= 0) ok = _lpty_waitfordata(pty, timeo, 0); if (ok > 0) readn = read(pty->m_fd, buf, READER_BUFSIZ); if (readn >= 0) { buf[readn] = 0; lua_pushstring(L, buf); /* we don't consider EINTR and ECHILD errors */ } else if (errno && (errno != EINTR) && (errno != ECHILD)) return _lpty_error(L, pty->flags.throwerrors, "lpty read failed: (%d) %s", errno, strerror(errno)); else lua_pushnil(L); return 1; } /* lpty_sendok * * Check wether the master side of this pty can accept data from us * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) timeout in seconds * * Lua Returns: * +1 true if pty can accept data, false if not. */ static int lpty_sendok(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); double timeo = (double)luaL_optnumber(L, 2, 0); int ok = _lpty_waitfordata(pty, timeo, 1); lua_pushboolean(L, ok > 0); return 1; } /* lpty_send * * write data to the master side of a pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 data to write * 3 (optional) timeout in seconds * * Lua Returns: * +1 the amount of bytes actually written, or nil if the write attempt timed * out */ static int lpty_send(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *data = luaL_checkstring(L, 2); double timeo = (double)luaL_optnumber(L, 3, -1); int written = -1; int ok = 1; if (timeo >= 0) ok = _lpty_waitfordata(pty, timeo, 1); if (ok > 0) written = write(pty->m_fd, data, strlen(data)); if (written >= 0) lua_pushinteger(L, written); else if (errno && (errno != EINTR) && (errno != ECHILD)) return _lpty_error(L, pty->flags.throwerrors, "lpty send failed: (%d) %s", errno, strerror(errno)); else lua_pushnil(L); return 1; } /* lpty_flush * * flush data from pty * * Arguments: * L Lua State * * Lua Stack: * 1 lpty userdata * 2 (optional) mode, what to flush * * Lua Returns: * nothing */ static int lpty_flush(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); const char *mode = luaL_optstring(L, 2, NULL); int which = TCIOFLUSH; if (mode) { int l = strlen(mode); if (l == 1) { switch (*mode) { case 'i': case 'I': which = TCIFLUSH; break; case 'o': case 'O': which = TCOFLUSH; break; } } } tcflush(pty->m_fd, which); return 0; } /*** utility ***/ /* lpty_ttyname * * return the name of the slave side of the pty * * Arguments: * L Lua State * * Lua Stack: * lpty userdata * * Lua Returns: * +1 the name of the terminal on the slave side of this pty */ static int lpty_ttyname(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); char *name = ptsname(pty->m_fd); if (name) lua_pushstring(L, name); else return _lpty_error(L, pty->flags.throwerrors, "lpty could not fetch slave tty name: %s", strerror(errno)); return 1; } /* lpty_getfd * * Get the fd of the master side of the pty * * Arguments: * L Lua state * * Lua stack: 1 lpty userdata * * Lua returns: * +1 The master fd (number) of the pty */ static int lpty_getfd(lua_State *L) { lPty *pty = lpty_checkLPty(L, 1); lua_pushinteger(L, pty->m_fd); return 1; } /* Function list / object metatable */ static const struct luaL_Reg lpty [] ={ {"new", lpty_new}, {"ttyname", lpty_ttyname}, {"startproc", lpty_startproc}, {"endproc", lpty_endproc}, {"hasproc", lpty_hasproc}, {"exitstatus", lpty_exitstatus}, {"getenviron", lpty_getenviron}, {"setenviron", lpty_setenviron}, {"readok", lpty_readok}, {"read", lpty_read}, {"sendok", lpty_sendok}, {"send", lpty_send}, {"flush", lpty_flush}, {"getfd", lpty_getfd}, {NULL, NULL} }; /* luaopen_lpty * * open and initialize this library */ int luaopen_lpty(lua_State *L) { /* itialize exit code buffer */ int i; for (i = 0; i < EXITSTATUS_BUFSIZ; ++i) { _lpty_exitstatus_buffer.ecodes[i].child = 0; _lpty_exitstatus_buffer.ecodes[i].status = 0; } _lpty_exitstatus_buffer.cur = 0; luaL_newlib(L, lpty); lua_pushliteral(L, "_VERSION"); lua_pushliteral(L, LPTY_VERSION); lua_rawset(L, -3); /* add lPty userdata metatable */ luaL_newmetatable(L, LPTY); luaL_setfuncs(L, lpty_meta, 0); /* methods */ lua_pushliteral(L, "__index"); lua_pushvalue(L, -3); lua_rawset(L, -3); lua_pop(L, 1); /* make sure to reset the signal handler on program exit */ atexit(_lpty_sigchld_handlerexit_cleanup); return 1; } lua-lpty-1.0.1/samples/000077500000000000000000000000001220314552000147165ustar00rootroot00000000000000lua-lpty-1.0.1/samples/expectex.lua000066400000000000000000000032441220314552000172510ustar00rootroot00000000000000#!/usr/bin/env lua --[[ expectbc.lua - - a somewhat less simple example for lpty, using bc via quite simplistic - expect like functionality - - Gunnar Zötl , 2010, 2011 - Released under MIT/X11 license. See file LICENSE for details. --]] local lpty = require "lpty" local function start(...) local p = lpty.new() p:startproc(...) while p:hasproc() and not p:readok() do end if not p:hasproc() then local what, code = p:exitstatus() error("start failed: child process terminated because of " .. tostring(what) .. " " ..tostring(code)) end return p end local function expect(p, what, plain, timeout) if not p:hasproc() then return nil, "no running process." end local res = "" local found = false -- consume all output from client while searching for our pattern while not found do local r, why = p:read(timeout) if r ~= nil then res = res .. r local first, last, capture = string.find(res, what, 1, plain) if first then if capture then found = capture else found = string.sub(res, first, last) end end else if why then error("read failed: " .. why) else local what, code = p:exitstatus() if what then error("read failed: child process terminated because of " .. tostring(what) .. " " ..tostring(code)) end end end end return found end local function send(p, what) local s = p:send(what) -- wait for reply while not p:readok() and p:hasproc() do end return s end -- and here's how we use it p = start("lua") if expect(p, "> $") then send(p, "=111+234\n") res = expect(p, "([0-9]+)[^0-9]*> $") print("Got '"..tostring(res).."'") send(p, "os.exit()\n") else print "Whoops..." end lua-lpty-1.0.1/samples/lptytest.lua000066400000000000000000000217041220314552000173150ustar00rootroot00000000000000#!/usr/bin/env lua --[[ lptytest.lua - - test aspects of lpty - - Gunnar Zötl , 2010, 2011 - Released under MIT/X11 license. See file LICENSE for details. --]] lpty = require "lpty" p = lpty.new() ntests = 0 nfailed = 0 -- announce which test we are performing and what it tests function announce(str) str = str or "" ntests = ntests + 1 print("Test " .. tostring(ntests) .. ": " .. str) end -- print that the test did not work out and optionally why function fail(str) local msg = " TEST FAILED" nfailed = nfailed + 1 if str ~= nil then msg = msg .. ": " .. str end print(msg) end -- just prints wether the test went ok or not. An optional reason for failure may be passed, -- which will then be printed. function check(ok, msg) if ok==false then fail(msg) else print " TEST OK" end end -- wait for a fraction of a second function waitabit(n) n = n or 1 t0 = os.clock() t1 = t0 while t1 - t0 < 0.1 * n do t1 = os.clock() end end -- 1 announce("starting test client") ok, val = pcall(lpty.startproc, p, "lua", "testclient.lua") if ok then check(val) else fail(tostring(val)) end -- 2 announce("checking whether pty has process, should return true") ok, val = pcall(lpty.hasproc, p) if ok then check(val) else fail(tostring(val)) end -- 3 announce("checking whether we can read from the pty, should return false") ok, val = pcall(lpty.readok, p) if ok then check(val == false) else fail(tostring(val)) end -- 4 announce("reading from pty with a timeout of 1 second, should return nil") ok, val = pcall(lpty.read, p, 1) if ok then check(val == nil) else fail(tostring(val)) end -- 5 announce("checking whether we can write to the pty, should return true") ok, val = pcall(lpty.sendok, p) if ok then check(val) else fail(tostring(val)) end -- 6 announce("writing data 'abcba\\n' to the pty, should return length of data -> 6") ok, val = pcall(lpty.send, p, "abcba\n") if ok then check(val == 6) else fail(tostring(val)) end -- allow the client to react waitabit() -- 7 announce("checking whether we can read from the pty, should return true") ok, val = pcall(lpty.readok, p) if ok then check(val) else fail(tostring(val)) end -- 8 announce("reading from pty, should return 'abcba\\n+abcba+\\n'") ok, val = pcall(lpty.read, p, 1) if ok then val = string.gsub(val, "[\r\n]+", '.') -- normalize line endings check(val == "abcba.+abcba+.") else fail(tostring(val)) end -- 9 announce("terminating child process") ok = pcall(lpty.endproc, p) check(ok) -- allow client to terminate waitabit() -- 10 announce("Checking whether pty has child process, should return false") ok, val = pcall(lpty.hasproc, p) if ok then check(val == false) else fail(tostring(val)) end -- 11 announce("checking whether we can read from pty, should return false") ok, val = pcall(lpty.readok, p) if ok then check(val == false) else fail(tostring(val)) end -- 12 announce("reading from pty with a timeout of 1 second, should return nil") ok, val = pcall(lpty.read, p, 1) if ok then check(val == nil) else fail(tostring(val)) end -- test timeout length. In order for this to work there may be no pending data in the pty to read. function testto(to, tm) local t0 = os.time() local i for i=1,10 do p:read(to) end local t = os.time() - t0 -- allow for some deviation return (tm - 1 < t and t < tm + 1) end -- 13 announce("testing timeout 0.5 second by running r:read(0.5) 10 times should take about 5 seconds") check(testto(0.5, 5)) -- 14 announce("testing timeout 1.5 second by running r:read(1.5) 10 times should take about 15 seconds") check(testto(1.5, 15)) -- creating pty with no local echo for remaining tests pn = lpty.new { no_local_echo = true } -- 15 announce("checking for data from no_local_echo pty, should return false") ok, val = pcall(lpty.readok, pn) if ok then check(val == false) else fail(tostring(val)) end -- 16 announce("sending data to no_local_echo pty then checking for data, should return false") ok, val = pcall(lpty.send, pn, "abc\n") if not ok then fail(tostring(val)) else ok, val = pcall(lpty.readok, pn) if ok then check(val == false) else fail(tostring(val)) end end -- 17 announce("starting test client for no_local_echo pty") ok, val = pcall(lpty.startproc, pn, "lua", "testclient.lua") if ok then check(val) else fail(tostring(val)) end -- 18 announce("reading from no_local_echo pty, should now return '+abc+\\n'") ok, val = pcall(lpty.read, pn, 0.5) if ok then val = string.gsub(val, "[\r\n]+", '.') -- normalize line endings check(val == "+abc+.") else fail(tostring(val)) end -- 19 announce("sending 'xxx\\n' to pty, reading back, should return '+xxx+\\n'") ok, val = pcall(lpty.send, pn, "xxx\n") if not ok then fail(tostring(val)) else ok, val = pcall(lpty.read, pn, 0.5) if ok then val = string.gsub(val, "[\r\n]+", '.') -- normalize line endings check(val == "+xxx+.") else fail(tostring(val)) end end -- 20 announce("testing exit status for current child, should return (false, nil)") ok, val, code = pcall(lpty.exitstatus, pn) if not ok then fail(tostring(val)) else check((val == false) and (code == nil)) end -- 21 announce("quitting child then testing exit status, should return ('exit', 0)") ok, val = pcall(lpty.send, pn, "quit\n") if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val, code = pcall(lpty.exitstatus, pn) if not ok then fail(tostring(val)) else check((val == 'exit') and (code == 0)) end end -- 22 announce("starting child with invalid executable, then testing exit status, should return ('exit', 1)") ok, val = pcall(lpty.startproc, pn, "./firsebrumf") if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val, code = pcall(lpty.exitstatus, pn) if not ok then fail(tostring(val)) else check((val == 'exit') and (code == 1)) end end -- 23 announce("starting child process, then killing it, then testing exit status, should return ('sig', *) with *>0") ok, val = pcall(lpty.startproc, pn, "lua") term = 0 if not ok then fail(tostring(val)) else while not pn:hasproc() do end ok, val = pcall(lpty.endproc, pn) if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val, code = pcall(lpty.exitstatus, pn) if not ok then fail(tostring(val)) else term = code check((val == 'sig') and (code > 0)) end end end -- 24 announce("starting child process, then killing it with kill=true, then testing exit status, should return ('sig', *) with *>0 and also not equal to * from prev. test") ok, val = pcall(lpty.startproc, pn, "lua") if not ok then fail(tostring(val)) else while not pn:hasproc() do end ok, val = pcall(lpty.endproc, pn, true) if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val, code = pcall(lpty.exitstatus, pn) if not ok then fail(tostring(val)) else check((val == 'sig') and (code > 0) and (code ~= term)) end end end -- cleanup pn:flush() -- 25 announce("reading environment from pty, should return a table with stuff in it") ok, env = pcall(lpty.getenviron, pn) envsiz = 0 if not ok then fail(tostring(val)) else ok = true for k, v in pairs(env) do envsiz = envsiz + 1 ok = ok and (type(k) == 'string') and (type(v) == 'string') end check(ok) end -- 26 announce("calling /usr/bin/env with an empty environment, then reading output, should return nothing at all") ok = pcall(lpty.setenviron, pn, {}) if not ok then fail(tostring(val)) else ok = pcall(lpty.startproc, pn, '/usr/bin/env') if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val = pcall(lpty.read, pn, 1) if not ok then fail(tostring(val)) else check(val == nil) end end end -- 27 announce("calling /usr/bin/env with {a=1} as its environment, then reading output, should return 'a=1\\n'") ok = pcall(lpty.setenviron, pn, {a=1}) if not ok then fail(tostring(val)) else ok = pcall(lpty.startproc, pn, '/usr/bin/env') if not ok then fail(tostring(val)) else while pn:hasproc() do end ok, val = pcall(lpty.read, pn, 1) if not ok then fail(tostring(val)) else val = string.gsub(tostring(val), "[\r\n]+", '.') -- normalize line endings check(val == "a=1.") end end end -- 28 announce("resetting then reading environment from pty, should return a table with stuff in it, size as before we messed with it") ok = pcall(lpty.setenviron, pn, nil) ok, env = pcall(lpty.getenviron, pn) mysiz = 0 if not ok then fail(tostring(val)) else ok = true for k, v in pairs(env) do mysiz = mysiz + 1 ok = ok and (type(k) == 'string') and (type(v) == 'string') end check(ok and (mysiz == envsiz)) end -- 29 announce("calling /usr/bin/env on standard environment, should return as many lines as there are entries in the environment (as counted before)") ok = pcall(lpty.startproc, pn, '/usr/bin/env') if not ok then fail(tostring(val)) else while pn:hasproc() do end val = "" while pn:readok() do val = val .. pn:read() end cnt = 0 string.gsub(val, ".-\n", function () cnt = cnt + 1 end) check(cnt == envsiz) end -- all done print("Tests " .. tostring(ntests) .. " failed " .. tostring(nfailed)) lua-lpty-1.0.1/samples/simpleex.lua000066400000000000000000000006511220314552000172510ustar00rootroot00000000000000#!/usr/bin/env lua --[[ simplebc.lua - - a very simple example of how to use lpty - - Gunnar Zötl , 2010, 2011 - Released under MIT/X11 license. See file LICENSE for details. --]] lpty = require "lpty" p = lpty.new() p:startproc("lua") p:read() -- skip startup message p:send("=111+234\n") r = p:read() print("Result is "..r) p:send("os.exit()\n") -- terminate lua all friendly-like print("Done.") lua-lpty-1.0.1/samples/testclient.lua000066400000000000000000000004371220314552000176030ustar00rootroot00000000000000#!/usr/bin/env lua --[[ testclient.lua - - a simple test client for lptytest.lua - - Gunnar Zötl , 2010, 2011 - Released under MIT/X11 license. See file LICENSE for details. --]] while true do s = io.read() print("+"..s.."+") if s == "quit" then os.exit() end end