Skip to content

Instantly share code, notes, and snippets.

@fmitha
Created October 21, 2021 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fmitha/44781cbe8a9c655a0d67f3c511405a1e to your computer and use it in GitHub Desktop.
Save fmitha/44781cbe8a9c655a0d67f3c511405a1e to your computer and use it in GitHub Desktop.
/* -- https://chat.stackexchange.com/transcript/message/59383566#59383566 */
/* -- You may use tput. Examples: tput >/dev/null (status == 2, usage */
/* -- error), TERM=foo tput >/dev/null (status == 3, no information for */
/* -- the terminal type), tput foo >/dev/null (status == 4, unknown */
/* -- capability). It can also return 1, which POSIX says is */
/* -- "unspecified" and historically used to tell the terminal doesn't */
/* -- have the specified capability (e.g. TERM=vt100 tput setaf */
/* -- >/dev/null); notably, in this case the exit status is ≠ 0, but no */
/* -- error message is printed out. */
/* local posix = require "posix" */
/* -- Thread at https://stackoverflow.com/q/1242572/350713 */
/* require("posix") */
/* -- */
/* -- Simple popen3() implementation */
/* -- */
/* function popen3(path, ...) */
/* local r1, w1 = posix.pipe() */
/* local r2, w2 = posix.pipe() */
/* local r3, w3 = posix.pipe() */
/* assert((r1 ~= nil or r2 ~= nil or r3 ~= nil), "pipe() failed") */
/* local pid, err = posix.fork() */
/* assert(pid ~= nil, "fork() failed") */
/* if pid == 0 then -- child */
/* posix.close(w1) */
/* posix.close(r2) */
/* posix.dup2(r1, posix.fileno(io.stdin)) */
/* posix.dup2(w2, posix.fileno(io.stdout)) */
/* posix.dup2(w3, posix.fileno(io.stderr)) */
/* posix.close(r1) */
/* posix.close(w2) */
/* posix.close(w3) */
/* local ret, err = posix.execp(path, unpack({...})) */
/* assert(ret ~= nil, "execp() failed") */
/* posix._exit(1) */
/* return */
/* end */
/* posix.close(r1) */
/* posix.close(w2) */
/* posix.close(w3) */
/* return pid, w1, r2, r3 */
/* end */
/* popen3("/bin/lsx -la") */
/* --[[ */
/* -- */
/* -- Pipe input into cmd + optional arguments and wait for completion */
/* -- and then return status code, stdout and stderr from cmd. */
/* -- */
/* function pipe_simple(input, cmd, ...) */
/* -- */
/* -- Launch child process */
/* -- */
/* local pid, w, r, e = popen3(cmd, unpack({...})) */
/* assert(pid ~= nil, "filter() unable to popen3()") */
/* -- */
/* -- Write to popen3's stdin, important to close it as some (most?) proccess */
/* -- block until the stdin pipe is closed */
/* -- */
/* posix.write(w, input) */
/* posix.close(w) */
/* local bufsize = 4096 */
/* -- */
/* -- Read popen3's stdout via Posix file handle */
/* -- */
/* local stdout = {} */
/* local i = 1 */
/* while true do */
/* buf = posix.read(r, bufsize) */
/* if buf == nil or #buf == 0 then break end */
/* stdout[i] = buf */
/* i = i + 1 */
/* end */
/* -- */
/* -- Read popen3's stderr via Posix file handle */
/* -- */
/* local stderr = {} */
/* local i = 1 */
/* while true do */
/* buf = posix.read(e, bufsize) */
/* if buf == nil or #buf == 0 then break end */
/* stderr[i] = buf */
/* i = i + 1 */
/* end */
/* -- */
/* -- Clean-up child (no zombies) and get return status */
/* -- */
/* local wait_pid, wait_cause, wait_status = posix.wait(pid) */
/* return wait_status, table.concat(stdout), table.concat(stderr) */
/* end */
/* -- */
/* -- Example usage */
/* -- */
/* local my_in = io.stdin:read("*all") */
/* --local my_cmd = "wc" */
/* --local my_args = {"-l"} */
/* local my_cmd = "spamc" */
/* local my_args = {} -- no arguments */
/* local my_status, my_out, my_err = pipe_simple(my_in, my_cmd, unpack(my_args)) */
/* -- Obviously not interleaved as they would have been if printed in realtime */
/* io.stdout:write(my_out) */
/* io.stderr:write(my_err) */
/* os.exit(my_status) */
/* --]] */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
void popen3(void)
{
/* local r1, w1 = posix.pipe(); */
/* local r2, w2 = posix.pipe(); */
/* local r3, w3 = posix.pipe(); */
int fda[2], fdb[2], fdc[2];
/* pipe(fd1); */
/* pipe(fd2); */
/* pipe(fd3); */
// Returns: 0 if OK, −1 on error
assert((pipe(fda) == 0) && (pipe(fdb) == 0) && (pipe(fdc) == 0));
int r1 = fda[0]; int w1 = fda[1];
int r2 = fdb[0]; int w2 = fdb[1];
int r3 = fdc[0]; int w3 = fdc[1];
/* assert((r1 ~= nil or r2 ~= nil or r3 ~= nil), "pipe() failed") */
// assert((pipe(fd1) < 0) || (pipe(fd2) < 0) || (pipe(fd3) < 0));
// assert((pipe(fd1) > 0));
// assert((pipe(fd1) == 0));
/* local pid, err = posix.fork() */
/* assert(pid ~= nil, "fork() failed") */
//pid_t parent = getpid();
pid_t pid = fork();
if (pid == -1) // fork failed
{
; // error, failed to fork()
}
else if (pid > 0) // parent process
{
int status;
waitpid(pid, &status, 0);
if(WIFEXITED(status))
printf("Child's exit code is %d\n", WEXITSTATUS(status));
else
printf("Child did not terminate with exit\n");
}
else //
{
/* posix.close(w1) */
/* posix.close(r2) */
/* posix.dup2(r1, posix.fileno(io.stdin)) */
/* posix.dup2(w2, posix.fileno(io.stdout)) */
/* posix.dup2(w3, posix.fileno(io.stderr)) */
/* posix.close(r1) */
/* posix.close(w2) */
/* posix.close(w3) */
close(w1);
close(r2);
close(r3);
// stdin, stdout, stderr defined in "5.3 Standard Input,
// Standard Output, and Standard Error"
dup2(r1, fileno(stdin));
dup2(w2, fileno(stdout));
dup2(w3, fileno(stderr));
close(r1);
close(w2);
close(w3);
/* local ret, err = posix.execp(path, unpack({...})) */
/* assert(ret ~= nil, "execp() failed") */
//int ret, err = execlp(path, unpack({...})) */
execlp("lsx", "lsx", "-la", "zarko.tex", (char *)NULL);
_exit(EXIT_FAILURE); // exec never returns
}
/* posix._exit(1) */
/* return */
/* end */
}
int main(void)
{
popen3();
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment