module system/posix/process-abstractions
imports 
  system/posix/process
  system/posix/signal
  util/log

/**
 * Fork abstractions
 */
strategies   

  /**
   * Executes "child" in the child process and returns a tuple
   * of the pid of the child process and the current term in the
   * parent process.
   */
  fork(child) =
    fork(child, id)

  fork(child, parent) =
    ?t; fork; ?pid; (?0 < <child> t + <parent> (pid, t))

  fork-and-wait(child) =
    fork(child, ?(<waitpid>, t); warn-ifsignaled; ?WaitStatus(0,_,_); !t)

/**
 * Call abstractions
 */
strategies

  /**
   * Call a program with list of string arguments.
   *
   * @type (String, List(String)) -> (String, List(String))
   */
  call =
    call(id)

  call(init-child) =
    ?(prog,args)
    ; fork-and-wait(init-child; <execvp> (prog, args); <exit> 1)

  /**
   * Call program with a list of strings arguments and print
   * information to stderr.
   *
   * @type (String, List(String)) -> (String, List(String))
   */
  call-noisy = 
    ?(prog,args)
    ; fork-and-wait(<debug; execvp> (prog, args))

/**
 * Termination abstractions
 */
strategies 
   
  /**
   * Succeeds if the process of this WaitStatus exited.
   *
   * @type WaitStatus -> WaitStatus
   */
  exited =
    ?WaitStatus(s,_,_)
    ; where(<not(eq)> (s, -1))
    ; if-verbose3(debug(!"the process exited: "))

  /**
   * Succeeds if the process of this WaitStatus was signaled.
   *
   * @type WaitStatus -> WaitStatus
   */
  signaled =
    ?WaitStatus(_,s,_)
    ; where(<not(eq)> (s, -1))
    ; if-verbose2(debug(!"the process was signaled"))
     
  /**
   * Succeeds if the process of this WaitStatus was stopped.
   *
   * @type WaitStatus -> WaitStatus
   */
  stopped = 
    ?WaitStatus(_,_,s)
    ; where(<not(eq)> (s, -1))
    ; if-verbose3(debug(!"the process stopped"))

  /**
   * Prints a warning if the process of this WaitStatus was signaled.
   *
   * @type WaitStatus -> WaitStatus
   */
  warn-ifsignaled =
    where(try(
      ?WaitStatus(_, <id>, _)
    ; not(?-1)
    ; signal-from-number
    ; signal-to-descr
    ; log(|Warning(),<concat-strings>["process signaled: ",<id>])
    ))

  /**
   * Execute program in a new process with list of string args, 
   * with the specified exec strategy.
   *
   * Optionally you can define file descriptors to use for stdin, stdout,
   * stderr.
   *
   * @param  opt-fdin   Option(FileDescr)
   * @param  opt-fdout  Option(FileDescr)
   * @param  opt-fderr  Option(FileDescr)
   *
   * @type  (String, List(String)) -> ()
   */
  call(|opt-fdin, opt-fdout, opt-fderr) =
      ?(prog, args)
      ; fork-and-wait(
          <option(<dup2> (<id>, <STDIN_FILENO> ))> opt-fdin
          ; <option(<dup2> (<id>, <STDOUT_FILENO>))> opt-fdout
          ; <option(<dup2> (<id>, <STDERR_FILENO>))> opt-fderr
          ; <execvp> (prog, args)
          ; <exit> 1
        )
      ; !()