module system/posix/pipe-abstractions
imports 
  system/posix/file
  system/posix/process

strategies

// connecting and/or disconnecting parts of a pipe

  // close both file descriptors constituting a pipe
  close-pipe =
  where(
    ?Pipe(fd1, fd2)
    ; <close> fd1
    ; <close> fd2
  )

  // connect the input side (fd1) of the pipe to stdin
  stdin-from-pipe =
  where(
    ?Pipe(fd1, fd2)
    ; <dup2> (fd1, <STDIN_FILENO>)
    ; <close> fd2
  )

  // create a file that is connected to input side (fd1) of the pipe
  file-from-pipe :
    Pipe(fd1, fd2) -> file
    where <fdopen> (fd1, "r") => file
        ; <close> fd2

  // connect the output side (fd2) of the pipe to stdout
  stdout-to-pipe =
  where(
    ?Pipe(fd1, fd2)
    ; <dup2> (fd2, <STDOUT_FILENO>)
    ; <close> fd1
  )

  // create a file that is connected to output side (fd2) of the pipe
  file-to-pipe :
    Pipe(fd1, fd2) -> file
    where <fdopen> (fd2, "w") => file
        ; <close> fd1

  write-term-to-text-pipe =
  where(
    ?(p, t)
    ; <file-to-pipe> p => outfile
    ; <fprint> (outfile, [t])
    ; <fclose> outfile
  )

  read-term-from-pipe :
    p -> t
    where <file-from-pipe> p => file
        ; <ReadFromFile> file => t
        ; <fclose> file

strategies

// Programs that interact with pipes

  // execute prog and write the current term to its stdin
  write-to-prog(prog, args) =
  where(
    where(pipe => p)
    ; fork(<stdin-from-pipe> p
           ; <execvp> (<prog>, <args>))
    ; ?(pid, t)
    ; <write-term-to-text-pipe> (p, t)
    ; <waitpid> pid 
    ; (exited + signaled + stopped)
  )

  write-to-prog'(prog, args) =
  where(
    ?t
    ; pipe => p
    ; fork
    ; (?0 // child
          ; <stdin-from-pipe> p
          ; <execvp> (<prog>, <args>)
       <+ ?pid // parent
          ; <write-term-to-text-pipe> (p, t)
          ; <waitpid> pid 
          ; (exited + signaled + stopped))
     )

  // execute prog and read the term on its stdout
  read-from-prog(prog, args) =
    where(pipe => p)
    ; fork(<stdout-to-pipe> p; <execvp> (<prog>, <args>))
    ; ?(pid, _)
    ; <read-term-from-pipe> p => t
    ; where(<waitpid> pid 
            ; (exited + signaled + stopped))

  read-from-prog'(prog, args) =
  where(
    pipe => p
    ; fork
    ; (?0 // child
          ; <stdout-to-pipe> p
          ; <execvp> (<prog>, <args>)
       <+ ?pid // parent
          ; <read-term-from-pipe> p 
          ; where( <waitpid> pid 
                ; (exited + signaled + stopped)))
   )

strategies

// managing pipelines of filter programs

  setup-filter-pipe =
    pipe => p; !(p, [], p)

  // prog produces input for the pipe p
  pipe-source(prog, args) :
    (p, pids) -> pids
    where fork
        ; (?0; <stdout-to-pipe> p; <execvp> (<prog>, <args>)
           <+ ?pid)

  // prog consumes the output from pipe p
  pipe-sink(prog, args) :
    (p0, pids, p1) -> (p0, [pid | pids])
    where fork(<stdin-from-pipe> p1; <execvp> (<prog>, <args>)) => (pid, _)

  close-filter-pipe(s) :
    (p-in, pids, p-out) -> (p-in, [pid | pids])
    where <s> p-out => pid

  exec-filter-pipe(s) :
    (p-in, pids) -> ()
    where <s> p-in => pid
        ; <map(waitpid; (exited + signaled + stopped))> [pid | pids]
 
  spawn-filter-with-prog(prog, args) :
    (p0, pids, p-in) -> (p0, [pid | pids], p-out)
    where debug(<concat-strings> ["spawn-filter-with ", <prog>, ": "])
        ; pipe => p-out
        ; fork(<stdin-from-pipe> p-in
               ; <stdout-to-pipe> p-out
               ; <execvp> (<prog>, <args>)) => (pid, _)
      //; <close-pipe> p-in

/*
  file-to-file-filter(!infile, !outfile) :
    (p-in, pids, p-out) -> 
    where <fopen> infile 

  pipe-comm(prog, args, cont) :
    ?fd;
    
*/