Skip to content

Instantly share code, notes, and snippets.

@firegurafiku
Last active December 12, 2015 01:59
Show Gist options
  • Save firegurafiku/4695545 to your computer and use it in GitHub Desktop.
Save firegurafiku/4695545 to your computer and use it in GitHub Desktop.

Lua snippets

This gist contains snippets code in Lua, one snippet per file. Files here should be name after functions they contain, functions should have short descriptive comments in front of their bodies and the link to there they are taken from (if any) or copyright notice.

require "posix_c"
--- Creates a subprocess with pipelined I/O streams.
--- Synapsis:
--- @code
--- pid, wr, rd, rderr = spawn(command, arg1, arg2, ... )
--- @endcode
--- Where @a command is the name of command to run (it will be looked through
--- PATH list), @a arg1, @a arg2 and so on are command arguments. Every
--- argument is conveyed on its own. Variable/pattern expansion and shell
--- constructs are not supported.
---
--- On success funtions returns process ID in @a pid, file descriptor to write
--- to child's @em stdin in @a wr, file descriptors to read child's @em stdout
--- and @em stderr in @a rd and @a rderr respectively. On failure only one
--- result @a nil is returned. Please note that you have to close returned file
--- descriptors yourself (using @c posix.close()). Do not silently ignore
--- results.
---
--- @author Pavel Kretov <firegurafiku@gmail.com>
--- @copyright WTFPL Public License
---
--- The code of this function is based on the slacy's answer at Stack Overflow:
--- http://stackoverflow.com/questions/548063
function spawn(command, ...)
-- We must create three pipelines in order to fully control child process
-- input/output: for stdin, stdout and stderr streams of child process.
-- The figure below illustrates this:
--
-- +--------+ write:1 read:0 +-------+
-- | | --------------0-----------> | |
-- | | | |
-- | | read:0 write:1 | |
-- | parent | <-------------1------------ | child |
-- | | | |
-- | | read:0 write:1 | |
-- | | <-------------2------------ | |
-- +--------+ +-------+
--
-- Every arrow end node is a file descriptor. Below in program they are
-- denoted as "pipeXX" with two digits in name. The first one is the
-- number of pipeline and the secnd one is the number of endnode. This
-- convention was taken after C.
local pipe00, pipe01 = posix.pipe()
local pipe10, pipe11 = posix.pipe()
local pipe20, pipe21 = posix.pipe()
if pipe00 == nil or pipe10 == nil or pipe20 == nil then
error("Unable to create pipelines.")
end
-- After this line the lua interpreter forks itself.
local pid = posix.fork()
if pid < 0 then
-- Parent: call to fork() failed.
-- It is quite strange.
return nil
elseif pid > 0 then
-- Parent: call to fork() succeeded.
-- Now return process ID and file descriptors to caller.
return pid, pipe01, pipe10, pipe20
else
-- Child: call to fork() succeeded.
-- Before executing another process we must substitute his standard
-- file descriptors to end nodes of our pipelines. End nodes on the
-- another side of pipes shouldn't be accessible by the child, so
-- them are closed.
posix.close(pipe01)
posix.close(pipe10)
posix.close(pipe20)
posix.dup2(pipe00, posix.fileno(io.stdin))
posix.dup2(pipe11, posix.fileno(io.stdout))
posix.dup2(pipe21, posix.fileno(io.stderr))
-- Now everything ready for exec(). The "p" at the end of the function
-- name means it will do PATH lookup. This line is the last line child
-- interpreter to run, if the execution falls below it means something
-- went wrong.
posix.execp(command, ...)
error("Unable to execute process.")
end
end
--- Unindents multiline text.
--- Function looks for first non-whitespace line and determines its
--- indentation symbol. This amount of whitespaces is removed from
--- the beginning of every line in text.
---
--- @note
--- For this approach to work, indentation must be used in a consistent way.
---
--- @author Pavel Kretov <firegurafiku@gmail.com>
--- @copyright WTFPL Public License
function unindent(str)
-- Extract indentation characters of first non-empty line.
local indent = string.match(str, '^[\r\n]*([^%S\r\n]*)%S')
if not indent then return str
else
-- Remove indentation characters from the very beginning of line
-- in order to simplify regular expression below.
str = string.sub(str, #indent+1)
-- It is safe here to concatenate variable into the middle of
-- regular expression pattern as 'indent' could only contain
-- whitespaces.
return (string.gsub(str, "\n"..indent, "\n"))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment