summaryrefslogblamecommitdiff
path: root/src/library/scala/sys/process/ProcessBuilderImpl.scala
blob: 3e6d41b5b68f9e0c438305259059c78de4af94a3 (plain) (tree)



































































































































































































                                                                                                                                                 
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2010, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

package scala.sys
package process

import processAliases._
import java.io.{ FileInputStream, FileOutputStream }
import Uncloseable.protect

private[process] trait ProcessBuilderImpl {
  self: ProcessBuilder.type =>

  class Dummy(override val toString: String, exitValue: => Int) extends AbstractBuilder {
  	override def run(io: ProcessIO): Process = new DummyProcess(exitValue)
  	override def canPipeTo = true
  }

  private[process] class URLInput(url: URL) extends IStreamBuilder(url.openStream, url.toString)
  private[process] class FileInput(file: File) extends IStreamBuilder(new FileInputStream(file), file.getAbsolutePath)
  private[process] class FileOutput(file: File, append: Boolean) extends OStreamBuilder(new FileOutputStream(file, append), file.getAbsolutePath)

  private[process] class OStreamBuilder(
    stream: => OutputStream,
    label: String
  ) extends ThreadBuilder(label, _ writeInput protect(stream)) { }

  private[process] class IStreamBuilder(
    stream: => InputStream,
    label: String
  ) extends ThreadBuilder(label, _ processOutput protect(stream)) { }

  private[process] abstract class ThreadBuilder(
    override val toString: String,
    runImpl: ProcessIO => Unit
  ) extends AbstractBuilder {

  	override def run(io: ProcessIO): Process = {
  		val success = new SyncVar[Boolean]
  		success put false
  		val t = Spawn {
  		  runImpl(io)
  		  success set true
  		}

  		new ThreadProcess(t, success)
  	}
  }

  /** Represents a simple command without any redirection or combination. */
  private[process] class Simple(p: JProcessBuilder) extends AbstractBuilder {
  	override def run(io: ProcessIO): Process = {
  		val process = p.start() // start the external process
  		import io._

  		// spawn threads that process the input, output, and error streams using the functions defined in `io`
  		val inThread  = Spawn(writeInput(process.getOutputStream), true)
  		val outThread = Spawn(processOutput(process.getInputStream))
  		val errorThread =
  		  if (p.redirectErrorStream) Nil
  		  else List(Spawn(processError(process.getErrorStream)))

  		new SimpleProcess(process, inThread, outThread :: errorThread)
  	}
  	override def toString = p.command.toString
  	override def canPipeTo = true
  }

  private[process] abstract class AbstractBuilder extends ProcessBuilder with Sink with Source {
  	protected def toSource = this
  	protected def toSink = this

  	def #|(other: ProcessBuilder): ProcessBuilder  = {
  	  require(other.canPipeTo, "Piping to multiple processes is not supported.")
  	  new PipedBuilder(this, other, false)
  	}
  	def #||(other: ProcessBuilder): ProcessBuilder = new OrBuilder(this, other)
  	def #&&(other: ProcessBuilder): ProcessBuilder = new AndBuilder(this, other)
  	def ###(other: ProcessBuilder): ProcessBuilder = new SequenceBuilder(this, other)

  	def run(): Process                                          = run(false)
  	def run(connectInput: Boolean): Process                     = run(BasicIO.standard(connectInput))
  	def run(log: ProcessLogger): Process                        = run(log, false)
  	def run(log: ProcessLogger, connectInput: Boolean): Process = run(BasicIO(log, connectInput))

  	def !!                      = slurp(None, false)
  	def !!(log: ProcessLogger)  = slurp(Some(log), false)
  	def !!<                     = slurp(None, true)
  	def !!<(log: ProcessLogger) = slurp(Some(log), true)

  	def lines: Stream[String]                       = lines(false, true, None)
  	def lines(log: ProcessLogger): Stream[String]   = lines(false, true, Some(log))
  	def lines_! : Stream[String]                    = lines(false, false, None)
  	def lines_!(log: ProcessLogger): Stream[String] = lines(false, false, Some(log))

  	def !                      = run(false).exitValue()
  	def !(io: ProcessIO)       = run(io).exitValue()
  	def !(log: ProcessLogger)  = runBuffered(log, false)
  	def !<                     = run(true).exitValue()
  	def !<(log: ProcessLogger) = runBuffered(log, true)

  	private[this] def slurp(log: Option[ProcessLogger], withIn: Boolean): String = {
  		val buffer = new StringBuffer
  		val code   = this ! BasicIO(buffer, log, withIn)

  		if (code == 0) buffer.toString
  		else sys.error("Nonzero exit value: " + code)
  	}

  	private[this] def lines(
  	  withInput: Boolean,
  	  nonZeroException: Boolean,
  	  log: Option[ProcessLogger]
  	): Stream[String] = {
  		val streamed = Streamed[String](nonZeroException)
  		val process  = run(new ProcessIO(BasicIO.input(withInput), BasicIO.processFully(streamed.process), BasicIO.getErr(log)))

  		Spawn(streamed done process.exitValue())
  		streamed.stream()
  	}

  	private[this] def runBuffered(log: ProcessLogger, connectInput: Boolean) =
  	  log buffer run(log, connectInput).exitValue()

  	def canPipeTo = false
  }

  class URLImpl(url: URL) extends URLBuilder with Source {
  	protected def toSource = new URLInput(url)
  }
  class FileImpl(base: File) extends FileBuilder with Sink with Source {
  	protected def toSource = new FileInput(base)
  	protected def toSink   = new FileOutput(base, false)

  	def #<<(f: File): ProcessBuilder           = #<<(new FileInput(f))
  	def #<<(u: URL): ProcessBuilder            = #<<(new URLInput(u))
  	def #<<(s: => InputStream): ProcessBuilder = #<<(new IStreamBuilder(s, "<input stream>"))
  	def #<<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, new FileOutput(base, true), false)
  }

  private[process] abstract class BasicBuilder extends AbstractBuilder {
  	protected[this] def checkNotThis(a: ProcessBuilder) = require(a != this, "Compound process '" + a + "' cannot contain itself.")
  	final def run(io: ProcessIO): Process = {
  		val p = createProcess(io)
  		p.start()
  		p
  	}
  	protected[this] def createProcess(io: ProcessIO): BasicProcess
  }

  private[process] abstract class SequentialBuilder(
    a: ProcessBuilder,
    b: ProcessBuilder,
    operatorString: String
  ) extends BasicBuilder {

  	checkNotThis(a)
  	checkNotThis(b)
  	override def toString = " ( " + a + " " + operatorString + " " + b + " ) "
  }

  private[process] class PipedBuilder(
    first: ProcessBuilder,
    second: ProcessBuilder,
    toError: Boolean
  ) extends SequentialBuilder(first, second, if (toError) "#|!" else "#|") {

  	override def createProcess(io: ProcessIO) = new PipedProcesses(first, second, io, toError)
  }

  private[process] class AndBuilder(
    first: ProcessBuilder,
    second: ProcessBuilder
  ) extends SequentialBuilder(first, second, "#&&") {
  	override def createProcess(io: ProcessIO) = new AndProcess(first, second, io)
  }

  private[process] class OrBuilder(
    first: ProcessBuilder,
    second: ProcessBuilder
  ) extends SequentialBuilder(first, second, "#||") {
  	override def createProcess(io: ProcessIO) = new OrProcess(first, second, io)
  }

  private[process] class SequenceBuilder(
    first: ProcessBuilder,
    second: ProcessBuilder
  ) extends SequentialBuilder(first, second, "###") {
  	override def createProcess(io: ProcessIO) = new ProcessSequence(first, second, io)
  }
}