/**
 * Used for POSIX and POSIX+XSI
 */
module stratego/xtc/posix/Proc
strategies

  /**
   * Reads a term from a file. If the given file exists, it will be
   * treated as an ATerm and its content will be returned, as a term.
   *
   * @type FILE -> t
   */
  read-from :
    FILE(name) -> t
    where <file-exists; ReadFromFile> name => t

strategies

  // XTC-COMMAND
  // Executing an external process
  // a -> String :: List(String) -> List(String)

  /**
   * Invokes the XT component given by tool. The input term is
   * a list of command line arguments that will be passed to the
   * invoked component.
   * 
   * @param tool _ -> String
   * @type List(a) -> Int
   */
  xtc-command(tool) = 
      where(tool; xtc-find-warning => loc)
    ; log(|Debug(),<concat-strings>["Invoking tool ",<tool>," at location ",loc," with arguments: "],<id>)
    ; where(<call> (loc, <id>))

  // Transparent transformation of terms and files by external processes

  /**
   * Invokes the XT component given by tool to transform the current term. 
   * The tool strategy must give the name of the component to invoke. The
   * The current term will be serialized to the invoked component, and the
   * result of its execution will become the new current term. No command
   * line arguments are given.
   *
   * @param tool _ -> String
   * @type       t -> u 
   */
  xtc-transform(tool) =
    xtc-transform(tool, ![])

  /**
   * Invokes the XT component given by tool to transform the current term. 
   * The tool strategy must give the name of the component to invoke. The
   * The current term will be serialized to the invoked component, and the
   * result of its execution will become the new current term. The args
   * parameter must return a list of arguments that will be given to the
   * invoked component.
   *
   * @param tool _ -> String
   * @type       t -> u 
   */
  xtc-transform(tool, args) =
    (FILE(id) + stdin)
    < xtc-transform-file(tool, args)
    + xtc-transform-term(tool, args)


strategies

  xtc-transform-file =
    ?(tool, args, file)

  /**
   * Transforms an existing file with an external process into a new file.
   * The resulting file will be created automatically, and a handle will
   * be returned by this strategy. The tool strategy must give the name
   * of the component to invoke. No arguments will be passed to the
   * transforming component.
   *
   * @param tool       _ -> n 
   * @type       FILE(a) -> FILE(b)
   */
  xtc-transform-file(tool) =
    xtc-transform-file(tool, ![])

  /**
   * Transforms an existing file with an external process into a new file.
   * The resulting file will be created automatically, and a handle will
   * be returned by this strategy.
   *
   * The tool strategy must give the name of the component to invoke. The
   * args strategy must give a list of arguments to pass to the component.
   *
   * @param tool       _ -> n 
   * @param args       _ -> List(arg)
   * @type       FILE(a) -> FILE(b)
   */
  xtc-transform-file(tool, args) :
    FILE(f) -> FILE(g)
    where <xtc-new-file> f => g
        ; <conc; xtc-command(tool)> (<args>, ["-i", f, "-o", g])

  xtc-transform-file(tool, args) :
    stdin -> FILE(g)
    where xtc-new-file => g
        ; <conc; xtc-command(tool)> (<args>, ["-o", g])

  /**
   * Transforms the current term with an external process. The current
   * term will be serialized and handed off to the component identified
   * by the tool strategy argument. The result of the component will
   * be returned by this strategy.
   *
   * The tool strategy must give the name of the component to invoke. The 
   * args strategy must give a list of arguments to pass to the component.
   * @param tool       _ -> n 
   * @param args       _ -> List(arg)
   * @type             t -> u
   */
  xtc-transform-term(tool, args) = 
    write-to
    ; xtc-transform-file(tool, args)
    ; read-from

  /**
   * Transforms a file with an internal strategy s. This strategy
   * will open an external file, apply s to it, and write the
   * result back to the file.
   *
   * @param s    a -> b
   * @type    File -> File
   */
  xtc-io-transform(s) =
    read-from; s; write-to

  /**
   * Transforms a file with an internal strategy s into a text file.
   * This strategy will open an external file (which must be an ATerm),
   * apply s to it, and print the result as a string back into the
   * file.
   *
   * @param s    a -> b
   * @type    File -> File
   */
  xtc-io-transform-text(s) =
      read-from; s; print-to

  /**
   * Generates a new file using the component identified by tool. This
   * strategy will invoke the component given by tool with no arguments,
   * and write the resulting term to a new file. A handle to the new
   * file will be returned. No arguments will be passed on to the
   * invoked component (except the implicit -o).
   *
   * @param tool    _ -> name
   * @type          _ -> FILE(b)
   */
  xtc-generate(tool) =
    xtc-generate(tool, ![])

  /**
   * Generates a new file using the component identified by tool. This
   * strategy will invoke the component given by tool with no arguments,
   * and write the resulting term to a new file. A handle to the new
   * file will be returned. The strategy args must produce a list of
   * arguments which are passed to the invoked component.
   *
   * @param tool    _ -> name
   * @type          _ -> FILE(b)
   */
  xtc-generate(tool, args) :
    _ -> FILE(g)
    where <xtc-new-file> () => g
        ; <conc; xtc-command(tool)> (<args>, ["-o", g])

strategies

  /**
   * Opens a file, applies the strategy s the file descriptor,
   * and ensures that the file descriptor is closed on return
   * of s, regardless of outcome.  The strategy s will be 
   * handed a file descriptor for the the FILE given to
   * File-as-fd. The result of s will become the result
   * of File-as-fd, and the file descriptor will always be
   * closed, also when s fails.
   *
   * @param s  Int -> b 
   * @type    FILE -> b
   */
  File-as-fd(s) =
      xtc-open-fd => fd
    ; finally(s, where(try(<xtc-close-fd> fd)))

  /**
   * Opens a file and returns a file descriptor. This strategy
   * can be used to open any <tt>FILE</tt>s as well as
   * <tt>stdin</tt>, <tt>stdout</tt> and <tt>stderr</tt>. The
   * file descriptor should be closed with <tt>xtc-close-fd</tt>.
   *
   * @type FILE -> Int
   */
  xtc-open-fd =
      \ FILE(s)  -> <open> s \
    + \ stdin()  -> < STDIN_FILENO> () \
    + \ stdout() -> <STDOUT_FILENO> () \
    + \ stderr() -> <STDERR_FILENO> () \

  /**
   * Closes a file descriptor. This strategy can be used to close
   * any file opened by <tt>xtc-open-fd</tt>, and also the
   * standard files <tt>stdin</tt>, <tt>stdout</tt> and
   * <tt>stderr</tt>.
   *
   * @type FILE -> Int
   */
  xtc-close-fd = 
    finally(
      ( where(<eq> (<id>, < STDIN_FILENO> ()))
      + where(<eq> (<id>, <STDOUT_FILENO> ()))
      + where(<eq> (<id>, <STDERR_FILENO> ()))
      ) <+ close
    , !())

  /**
   * Generates a new file name, but does not create the file on
   * disk. As the file is not created on disk, another process
   * may theoretically create it (and thus own it), thus causing
   * a race condition. For this reason, you may prefer to us
   * <tt>xtc-new-file</tt> instead.
   * 
   * @type _ -> FILE
   */
  xtc-new-file-name =
    obsolete(!"xtc-new-file-name; use xtc-new-file")
    ; new-file => f
    ; where(<assert(!TempFiles)> (f, ()))
    ; !FILE(f)