summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-01-12 02:49:08 +0000
committerPaul Phillips <paulp@improving.org>2011-01-12 02:49:08 +0000
commit5bada810b4c7eda186aa40b94a78326520b3fa92 (patch)
treeeee2227bf7f53a6b04e9732d887961b403975ab5 /src/library
parent566fefb05abe31e90f765d1fb0a89b264302d9ce (diff)
downloadscala-5bada810b4c7eda186aa40b94a78326520b3fa92.tar.gz
scala-5bada810b4c7eda186aa40b94a78326520b3fa92.tar.bz2
scala-5bada810b4c7eda186aa40b94a78326520b3fa92.zip
Imported sbt.Process into trunk, in the guise o...
Imported sbt.Process into trunk, in the guise of package scala.sys.process. It is largely indistinguishable from the version in sbt, at least from the outside. Also, I renamed package system to sys. I wanted to do that from the beginning and the desire has only grown since then. Sometimes a short identifier is just critical to usability: with a function like error("") called from hundreds of places, the difference between system.error and sys.error is too big. sys.error and sys.exit have good vibes (at least as good as the vibes can be for functions which error and exit.) Note: this is just the first cut. I need to check this in to finish fixing partest. I will be going over it with a comb and writing documentation which will leave you enchanted, as well as removing other bits which are now redundant or inferior. No review.
Diffstat (limited to 'src/library')
-rw-r--r--src/library/scala/Predef.scala12
-rw-r--r--src/library/scala/collection/immutable/HashMap.scala2
-rw-r--r--src/library/scala/collection/immutable/HashSet.scala4
-rw-r--r--src/library/scala/collection/immutable/IntMap.scala10
-rw-r--r--src/library/scala/collection/immutable/LongMap.scala6
-rw-r--r--src/library/scala/collection/immutable/RedBlack.scala8
-rw-r--r--src/library/scala/collection/mutable/ArrayStack.scala2
-rw-r--r--src/library/scala/collection/mutable/OpenHashMap.scala4
-rw-r--r--src/library/scala/collection/parallel/mutable/ParArray.scala2
-rw-r--r--src/library/scala/collection/parallel/package.scala2
-rw-r--r--src/library/scala/mobile/Code.scala2
-rw-r--r--src/library/scala/sys/PropertiesMap.scala (renamed from src/library/scala/system/PropertiesMap.scala)2
-rw-r--r--src/library/scala/sys/ShutdownHookThread.scala (renamed from src/library/scala/system/ShutdownHookThread.scala)2
-rw-r--r--src/library/scala/sys/package.scala (renamed from src/library/scala/system/package.scala)6
-rw-r--r--src/library/scala/sys/process/BasicIO.scala81
-rw-r--r--src/library/scala/sys/process/Process.scala85
-rw-r--r--src/library/scala/sys/process/ProcessBuilder.scala113
-rw-r--r--src/library/scala/sys/process/ProcessBuilderImpl.scala196
-rw-r--r--src/library/scala/sys/process/ProcessIO.scala23
-rw-r--r--src/library/scala/sys/process/ProcessImpl.scala232
-rw-r--r--src/library/scala/sys/process/ProcessLogger.scala16
-rw-r--r--src/library/scala/sys/process/package.scala25
-rw-r--r--src/library/scala/util/parsing/combinator/Parsers.scala2
-rw-r--r--src/library/scala/xml/dtd/ContentModelParser.scala14
-rw-r--r--src/library/scala/xml/dtd/Scanner.scala4
-rw-r--r--src/library/scala/xml/parsing/MarkupParser.scala2
-rw-r--r--src/library/scala/xml/parsing/MarkupParserCommon.scala4
-rw-r--r--src/library/scala/xml/parsing/ValidatingMarkupHandler.scala2
28 files changed, 817 insertions, 46 deletions
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index e8768fd150..f07582c356 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -58,14 +58,14 @@ object Predef extends LowPriorityImplicits {
// Deprecated
- @deprecated("Use system.error(message) instead")
- def error(message: String): Nothing = system.error(message)
+ @deprecated("Use sys.error(message) instead")
+ def error(message: String): Nothing = sys.error(message)
- @deprecated("Use system.exit() instead")
- def exit(): Nothing = system.exit()
+ @deprecated("Use sys.exit() instead")
+ def exit(): Nothing = sys.exit()
- @deprecated("Use system.exit(status) instead")
- def exit(status: Int): Nothing = system.exit(status)
+ @deprecated("Use sys.exit(status) instead")
+ def exit(status: Int): Nothing = sys.exit(status)
@deprecated("Use formatString.format(args: _*) or arg.formatted(formatString) instead")
def format(text: String, xs: Any*) = augmentString(text).format(xs: _*)
diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala
index 26d7ff2eed..2f95c4fb80 100644
--- a/src/library/scala/collection/immutable/HashMap.scala
+++ b/src/library/scala/collection/immutable/HashMap.scala
@@ -452,7 +452,7 @@ time { mNew.iterator.foreach( p => ()) }
new HashTrieMap[A, B1](this.bitmap | that.bitmap, merged, totalelems)
case hm: HashMapCollision1[_, _] => that.merge0(this, level, merger)
case hm: HashMap[_, _] => this
- case _ => system.error("section supposed to be unreachable.")
+ case _ => sys.error("section supposed to be unreachable.")
}
}
diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala
index 1bf3b795f2..aa9f99b4cb 100644
--- a/src/library/scala/collection/immutable/HashSet.scala
+++ b/src/library/scala/collection/immutable/HashSet.scala
@@ -186,12 +186,12 @@ object HashSet extends ImmutableSetFactory[HashSet] {
// hash codes and remove the collision. however this is never called
// because no references to this class are ever handed out to client code
// and HashTrieSet serialization takes care of the situation
- system.error("cannot serialize an immutable.HashSet where all items have the same 32-bit hash code")
+ sys.error("cannot serialize an immutable.HashSet where all items have the same 32-bit hash code")
//out.writeObject(kvs)
}
private def readObject(in: java.io.ObjectInputStream) {
- system.error("cannot deserialize an immutable.HashSet where all items have the same 32-bit hash code")
+ sys.error("cannot deserialize an immutable.HashSet where all items have the same 32-bit hash code")
//kvs = in.readObject().asInstanceOf[ListSet[A]]
//hash = computeHash(kvs.)
}
diff --git a/src/library/scala/collection/immutable/IntMap.scala b/src/library/scala/collection/immutable/IntMap.scala
index 6baa05c21a..86b3b9f1e4 100644
--- a/src/library/scala/collection/immutable/IntMap.scala
+++ b/src/library/scala/collection/immutable/IntMap.scala
@@ -118,7 +118,7 @@ private[immutable] abstract class IntMapIterator[V, T](it : IntMap[V]) extends I
case t@IntMap.Tip(_, _) => valueOf(t);
// This should never happen. We don't allow IntMap.Nil in subtrees of the IntMap
// and don't return an IntMapIterator for IntMap.Nil.
- case IntMap.Nil => system.error("Empty maps not allowed as subtrees");
+ case IntMap.Nil => sys.error("Empty maps not allowed as subtrees");
}
}
@@ -254,8 +254,8 @@ sealed abstract class IntMap[+T] extends Map[Int, T] with MapLike[Int, T, IntMap
final override def apply(key : Int) : T = this match {
case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key);
- case IntMap.Tip(key2, value) => if (key == key2) value else system.error("Key not found");
- case IntMap.Nil => system.error("key not found");
+ case IntMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found");
+ case IntMap.Nil => sys.error("key not found");
}
def + [S >: T] (kv: (Int, S)): IntMap[S] = updated(kv._1, kv._2)
@@ -417,7 +417,7 @@ sealed abstract class IntMap[+T] extends Map[Int, T] with MapLike[Int, T, IntMap
final def firstKey : Int = this match {
case Bin(_, _, l, r) => l.firstKey;
case Tip(k, v) => k;
- case IntMap.Nil => system.error("Empty set")
+ case IntMap.Nil => sys.error("Empty set")
}
/**
@@ -426,6 +426,6 @@ sealed abstract class IntMap[+T] extends Map[Int, T] with MapLike[Int, T, IntMap
final def lastKey : Int = this match {
case Bin(_, _, l, r) => r.lastKey;
case Tip(k, v) => k;
- case IntMap.Nil => system.error("Empty set")
+ case IntMap.Nil => sys.error("Empty set")
}
}
diff --git a/src/library/scala/collection/immutable/LongMap.scala b/src/library/scala/collection/immutable/LongMap.scala
index f02c6fce66..0d454a4ba2 100644
--- a/src/library/scala/collection/immutable/LongMap.scala
+++ b/src/library/scala/collection/immutable/LongMap.scala
@@ -115,7 +115,7 @@ private[immutable] abstract class LongMapIterator[V, T](it : LongMap[V]) extends
case t@LongMap.Tip(_, _) => valueOf(t);
// This should never happen. We don't allow LongMap.Nil in subtrees of the LongMap
// and don't return an LongMapIterator for LongMap.Nil.
- case LongMap.Nil => system.error("Empty maps not allowed as subtrees");
+ case LongMap.Nil => sys.error("Empty maps not allowed as subtrees");
}
}
@@ -252,8 +252,8 @@ sealed abstract class LongMap[+T] extends Map[Long, T] with MapLike[Long, T, Lon
final override def apply(key : Long) : T = this match {
case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key);
- case LongMap.Tip(key2, value) => if (key == key2) value else system.error("Key not found");
- case LongMap.Nil => system.error("key not found");
+ case LongMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found");
+ case LongMap.Nil => sys.error("key not found");
}
def + [S >: T] (kv: (Long, S)): LongMap[S] = updated(kv._1, kv._2)
diff --git a/src/library/scala/collection/immutable/RedBlack.scala b/src/library/scala/collection/immutable/RedBlack.scala
index 1c2d4ba362..58a6982615 100644
--- a/src/library/scala/collection/immutable/RedBlack.scala
+++ b/src/library/scala/collection/immutable/RedBlack.scala
@@ -98,7 +98,7 @@ abstract class RedBlack[A] extends Serializable {
}
def subl(t: Tree[B]) = t match {
case BlackTree(x, xv, a, b) => RedTree(x, xv, a, b)
- case _ => system.error("Defect: invariance violation; expected black, got "+t)
+ case _ => sys.error("Defect: invariance violation; expected black, got "+t)
}
def balLeft(x: A, xv: B, tl: Tree[B], tr: Tree[B]) = (tl, tr) match {
case (RedTree(y, yv, a, b), c) =>
@@ -107,7 +107,7 @@ abstract class RedBlack[A] extends Serializable {
balance(x, xv, bl, RedTree(y, yv, a, b))
case (bl, RedTree(y, yv, BlackTree(z, zv, a, b), c)) =>
RedTree(z, zv, BlackTree(x, xv, bl, a), balance(y, yv, b, subl(c)))
- case _ => system.error("Defect: invariance violation at "+right)
+ case _ => sys.error("Defect: invariance violation at "+right)
}
def balRight(x: A, xv: B, tl: Tree[B], tr: Tree[B]) = (tl, tr) match {
case (a, RedTree(y, yv, b, c)) =>
@@ -116,7 +116,7 @@ abstract class RedBlack[A] extends Serializable {
balance(x, xv, RedTree(y, yv, a, b), bl)
case (RedTree(y, yv, a, BlackTree(z, zv, b, c)), bl) =>
RedTree(z, zv, balance(y, yv, subl(a), b), BlackTree(x, xv, c, bl))
- case _ => system.error("Defect: invariance violation at "+left)
+ case _ => sys.error("Defect: invariance violation at "+left)
}
def delLeft = left match {
case _: BlackTree[_] => balLeft(key, value, left.del(k), right)
@@ -237,7 +237,7 @@ abstract class RedBlack[A] extends Serializable {
case BlackTree(_, _, _, _) :: tail =>
if (depth == 1) zipper else findDepth(tail, depth - 1)
case _ :: tail => findDepth(tail, depth)
- case Nil => system.error("Defect: unexpected empty zipper while computing range")
+ case Nil => sys.error("Defect: unexpected empty zipper while computing range")
}
// Blackening the smaller tree avoids balancing problems on union;
diff --git a/src/library/scala/collection/mutable/ArrayStack.scala b/src/library/scala/collection/mutable/ArrayStack.scala
index 9de466e04c..3a38f831b2 100644
--- a/src/library/scala/collection/mutable/ArrayStack.scala
+++ b/src/library/scala/collection/mutable/ArrayStack.scala
@@ -114,7 +114,7 @@ extends Seq[T]
* @return the element on top of the stack
*/
def pop: T = {
- if (index == 0) system.error("Stack empty")
+ if (index == 0) sys.error("Stack empty")
index -= 1
val x = table(index).asInstanceOf[T]
table(index) = null
diff --git a/src/library/scala/collection/mutable/OpenHashMap.scala b/src/library/scala/collection/mutable/OpenHashMap.scala
index 29dfc3ad19..5bd220f81f 100644
--- a/src/library/scala/collection/mutable/OpenHashMap.scala
+++ b/src/library/scala/collection/mutable/OpenHashMap.scala
@@ -180,7 +180,7 @@ extends Map[Key, Value]
val initialModCount = modCount;
private[this] def advance {
- if (initialModCount != modCount) system.error("Concurrent modification");
+ if (initialModCount != modCount) sys.error("Concurrent modification");
while((index <= mask) && (table(index) == null || table(index).value == None)) index+=1;
}
@@ -217,7 +217,7 @@ extends Map[Key, Value]
override def foreach[U](f : ((Key, Value)) => U) {
val startModCount = modCount;
foreachUndeletedEntry(entry => {
- if (modCount != startModCount) system.error("Concurrent Modification")
+ if (modCount != startModCount) sys.error("Concurrent Modification")
f((entry.key, entry.value.get))}
);
}
diff --git a/src/library/scala/collection/parallel/mutable/ParArray.scala b/src/library/scala/collection/parallel/mutable/ParArray.scala
index 536976f5e3..ccd693a241 100644
--- a/src/library/scala/collection/parallel/mutable/ParArray.scala
+++ b/src/library/scala/collection/parallel/mutable/ParArray.scala
@@ -630,7 +630,7 @@ self =>
new ScanToArray(left, z, op, targetarr),
new ScanToArray(right, z, op, targetarr)
)
- case _ => system.error("Can only split scan tree internal nodes.")
+ case _ => sys.error("Can only split scan tree internal nodes.")
}
def shouldSplitFurther = tree match {
case ScanNode(_, _) => true
diff --git a/src/library/scala/collection/parallel/package.scala b/src/library/scala/collection/parallel/package.scala
index 757f5d2686..acced246da 100644
--- a/src/library/scala/collection/parallel/package.scala
+++ b/src/library/scala/collection/parallel/package.scala
@@ -210,7 +210,7 @@ package object parallel {
afterCombine(other)
this
- } else system.error("Unexpected combiner type.")
+ } else sys.error("Unexpected combiner type.")
} else this
}
diff --git a/src/library/scala/mobile/Code.scala b/src/library/scala/mobile/Code.scala
index e1d04eafa5..6f96130568 100644
--- a/src/library/scala/mobile/Code.scala
+++ b/src/library/scala/mobile/Code.scala
@@ -196,7 +196,7 @@ class Code(clazz: java.lang.Class[_]) {
if (cs.length > 0) {
cs(0).newInstance("").asInstanceOf[AnyRef]
} else {
- system.error("class " + clazz.getName() + " has no public constructor")
+ sys.error("class " + clazz.getName() + " has no public constructor")
}
}
}
diff --git a/src/library/scala/system/PropertiesMap.scala b/src/library/scala/sys/PropertiesMap.scala
index a01eee6931..e840f28467 100644
--- a/src/library/scala/system/PropertiesMap.scala
+++ b/src/library/scala/sys/PropertiesMap.scala
@@ -6,7 +6,7 @@
** |/ **
\* */
-package scala.system
+package scala.sys
import scala.collection.mutable
import scala.collection.JavaConverters._
diff --git a/src/library/scala/system/ShutdownHookThread.scala b/src/library/scala/sys/ShutdownHookThread.scala
index 17a5e398ef..af79d95a2a 100644
--- a/src/library/scala/system/ShutdownHookThread.scala
+++ b/src/library/scala/sys/ShutdownHookThread.scala
@@ -6,7 +6,7 @@
** |/ **
\* */
-package scala.system
+package scala.sys
/** A minimal Thread wrapper to enhance shutdown hooks. It knows
* how to unregister itself.
diff --git a/src/library/scala/system/package.scala b/src/library/scala/sys/package.scala
index 1e989f86ec..85deba62b6 100644
--- a/src/library/scala/system/package.scala
+++ b/src/library/scala/sys/package.scala
@@ -11,7 +11,7 @@ package scala
import scala.collection.immutable
import collection.JavaConverters._
-/** The package object `scala.system` contains methods for reading
+/** The package object `scala.sys` contains methods for reading
* and altering core aspects of the virtual machine as well as the
* world outside of it.
*
@@ -19,7 +19,7 @@ import collection.JavaConverters._
* @version 2.9
* @since 2.9
*/
-package object system {
+package object sys {
/** Throw a new RuntimeException with the supplied message.
*
* @return Nothing.
@@ -50,7 +50,7 @@ package object system {
/** A bidirectional, mutable Map representing the current system Properties.
*
* @return a PropertiesMap.
- * @see `scala.system.PropertiesMap`
+ * @see `scala.sys.PropertiesMap`
*/
def props: PropertiesMap = new PropertiesMap
diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala
new file mode 100644
index 0000000000..505a859377
--- /dev/null
+++ b/src/library/scala/sys/process/BasicIO.scala
@@ -0,0 +1,81 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+import processAliases._
+import java.io.{ BufferedReader, InputStreamReader }
+
+object BasicIO {
+ final val BufferSize = 8192
+ final val Newline = props("line.separator")
+
+ def apply(buffer: StringBuffer, log: Option[ProcessLogger], withIn: Boolean) =
+ new ProcessIO(input(withIn), processFully(buffer), getErr(log))
+ def apply(log: ProcessLogger, withIn: Boolean) =
+ new ProcessIO(input(withIn), processInfoFully(log), processErrFully(log))
+
+ def getErr(log: Option[ProcessLogger]) = log match {
+ case Some(lg) => processErrFully(lg)
+ case None => toStdErr
+ }
+
+ private def processErrFully(log: ProcessLogger) = processFully(log error _)
+ private def processInfoFully(log: ProcessLogger) = processFully(log info _)
+
+ def ignoreOut = (i: OutputStream) => ()
+
+ def close(c: Closeable) = try c.close() catch { case _: IOException => () }
+ def processFully(buffer: Appendable): InputStream => Unit = processFully(appendLine(buffer))
+ def processFully(processLine: String => Unit): InputStream => Unit = in => {
+ val reader = new BufferedReader(new InputStreamReader(in))
+ processLinesFully(processLine)(reader.readLine)
+ }
+
+ def processLinesFully(processLine: String => Unit)(readLine: () => String) {
+ def readFully() {
+ val line = readLine()
+ if (line != null) {
+ processLine(line)
+ readFully()
+ }
+ }
+ readFully()
+ }
+ def connectToIn(o: OutputStream): Unit = transferFully(System.in, o)
+ def input(connect: Boolean): OutputStream => Unit = if (connect) connectToIn else ignoreOut
+ def standard(connectInput: Boolean): ProcessIO = standard(input(connectInput))
+ def standard(in: OutputStream => Unit): ProcessIO = new ProcessIO(in, toStdOut, toStdErr)
+
+ def toStdErr = (in: InputStream) => transferFully(in, System.err)
+ def toStdOut = (in: InputStream) => transferFully(in, System.out)
+
+ def transferFully(in: InputStream, out: OutputStream): Unit =
+ try transferFullyImpl(in, out)
+ catch { case _: InterruptedException => () }
+
+ private[this] def appendLine(buffer: Appendable): String => Unit = line => {
+ buffer.append(line)
+ buffer.append(Newline)
+ }
+
+ private[this] def transferFullyImpl(in: InputStream, out: OutputStream) {
+ val buffer = new Array[Byte](BufferSize)
+ def loop() {
+ val byteCount = in.read(buffer)
+ if (byteCount > 0) {
+ out.write(buffer, 0, byteCount)
+ out.flush()
+ loop()
+ }
+ }
+ loop()
+ }
+}
+
diff --git a/src/library/scala/sys/process/Process.scala b/src/library/scala/sys/process/Process.scala
new file mode 100644
index 0000000000..6453002ae4
--- /dev/null
+++ b/src/library/scala/sys/process/Process.scala
@@ -0,0 +1,85 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+import processAliases._
+import ProcessBuilder._
+
+/** Represents a process that is running or has finished running.
+ * It may be a compound process with several underlying native processes (such as 'a #&& b`).
+ */
+trait Process {
+ /** Blocks until this process exits and returns the exit code.*/
+ def exitValue(): Int
+ /** Destroys this process. */
+ def destroy(): Unit
+}
+
+/** Methods for constructing simple commands that can then be combined. */
+object Process extends ProcessCreation { }
+
+trait ProcessCreation {
+ def apply(command: String): ProcessBuilder = apply(command, None)
+ def apply(command: Seq[String]): ProcessBuilder = apply(command, None)
+ def apply(command: String, arguments: Seq[String]): ProcessBuilder = apply(command +: arguments, None)
+
+ /** create ProcessBuilder with working dir set to File and extra environment variables */
+ def apply(command: String, cwd: File, extraEnv: (String, String)*): ProcessBuilder =
+ apply(command, Some(cwd), extraEnv: _*)
+
+ /** create ProcessBuilder with working dir set to File and extra environment variables */
+ def apply(command: Seq[String], cwd: File, extraEnv: (String, String)*): ProcessBuilder =
+ apply(command, Some(cwd), extraEnv: _*)
+
+ /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */
+ def apply(command: String, cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = {
+ apply(command.split("""\s+"""), cwd, extraEnv : _*)
+ // not smart to use this on windows, because CommandParser uses \ to escape ".
+ /*CommandParser.parse(command) match {
+ case Left(errorMsg) => error(errorMsg)
+ case Right((cmd, args)) => apply(cmd :: args, cwd, extraEnv : _*)
+ }*/
+ }
+
+ /** create ProcessBuilder with working dir optionaly set to File and extra environment variables */
+ def apply(command: Seq[String], cwd: Option[File], extraEnv: (String, String)*): ProcessBuilder = {
+ val jpb = new JProcessBuilder(command.toArray : _*)
+ cwd foreach (jpb directory _)
+ extraEnv foreach { case (k, v) => jpb.environment.put(k, v) }
+ apply(jpb)
+ }
+
+ def apply(builder: JProcessBuilder): ProcessBuilder = new Simple(builder)
+ def apply(file: File): FileBuilder = new FileImpl(file)
+ def apply(url: URL): URLBuilder = new URLImpl(url)
+ def apply(command: scala.xml.Elem): ProcessBuilder = apply(command.text.trim)
+ def apply(value: Boolean): ProcessBuilder = apply(value.toString, if (value) 0 else 1)
+
+ def apply(name: String, exitValue: => Int): ProcessBuilder = new Dummy(name, exitValue)
+ def applySeq[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = builders.map(convert)
+
+ def cat(file: Source, files: Source*): ProcessBuilder = cat(file +: files)
+ def cat(files: Seq[Source]): ProcessBuilder = {
+ require(files.nonEmpty)
+ files map (_.cat) reduceLeft (_ #&& _)
+ }
+}
+
+trait ProcessImplicits {
+ import Process._
+
+ implicit def buildersToProcess[T](builders: Seq[T])(implicit convert: T => Source): Seq[Source] = applySeq(builders)
+ implicit def builderToProcess(builder: JProcessBuilder): ProcessBuilder = apply(builder)
+ implicit def fileToProcess(file: File): FileBuilder = apply(file)
+ implicit def urlToProcess(url: URL): URLBuilder = apply(url)
+ implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command)
+ implicit def stringToProcess(command: String): ProcessBuilder = apply(command)
+ implicit def stringSeqToProcess(command: Seq[String]): ProcessBuilder = apply(command)
+}
diff --git a/src/library/scala/sys/process/ProcessBuilder.scala b/src/library/scala/sys/process/ProcessBuilder.scala
new file mode 100644
index 0000000000..798796b29c
--- /dev/null
+++ b/src/library/scala/sys/process/ProcessBuilder.scala
@@ -0,0 +1,113 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+import processAliases._
+import ProcessBuilder._
+
+/** Represents a runnable process. */
+trait ProcessBuilder extends Source with Sink {
+ /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is
+ * sent to the console. If the exit code is non-zero, an exception is thrown.*/
+ def !! : String
+ /** Starts the process represented by this builder, blocks until it exits, and returns the output as a String. Standard error is
+ * sent to the provided ProcessLogger. If the exit code is non-zero, an exception is thrown.*/
+ def !!(log: ProcessLogger): String
+ /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
+ * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value,
+ * the Stream will provide all lines up to termination and then throw an exception. */
+ def lines: Stream[String]
+ /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
+ * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value,
+ * the Stream will provide all lines up to termination but will not throw an exception. */
+ def lines(log: ProcessLogger): Stream[String]
+ /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
+ * but the process has not completed. Standard error is sent to the console. If the process exits with a non-zero value,
+ * the Stream will provide all lines up to termination but will not throw an exception. */
+ def lines_! : Stream[String]
+ /** Starts the process represented by this builder. The output is returned as a Stream that blocks when lines are not available
+ * but the process has not completed. Standard error is sent to the provided ProcessLogger. If the process exits with a non-zero value,
+ * the Stream will provide all lines up to termination but will not throw an exception. */
+ def lines_!(log: ProcessLogger): Stream[String]
+ /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
+ * sent to the console.*/
+ def ! : Int
+ /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
+ * sent to the given ProcessLogger.*/
+ def !(log: ProcessLogger): Int
+ /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
+ * sent to the console. The newly started process reads from standard input of the current process.*/
+ def !< : Int
+ /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
+ * sent to the given ProcessLogger. The newly started process reads from standard input of the current process.*/
+ def !<(log: ProcessLogger): Int
+ /** Starts the process represented by this builder. Standard output and error are sent to the console.*/
+ def run(): Process
+ /** Starts the process represented by this builder. Standard output and error are sent to the given ProcessLogger.*/
+ def run(log: ProcessLogger): Process
+ /** Starts the process represented by this builder. I/O is handled by the given ProcessIO instance.*/
+ def run(io: ProcessIO): Process
+ /** Starts the process represented by this builder. Standard output and error are sent to the console.
+ * The newly started process reads from standard input of the current process if `connectInput` is true.*/
+ def run(connectInput: Boolean): Process
+ /** Starts the process represented by this builder, blocks until it exits, and returns the exit code. Standard output and error are
+ * sent to the given ProcessLogger.
+ * The newly started process reads from standard input of the current process if `connectInput` is true.*/
+ def run(log: ProcessLogger, connectInput: Boolean): Process
+
+ /** Constructs a command that runs this command first and then `other` if this command succeeds.*/
+ def #&& (other: ProcessBuilder): ProcessBuilder
+ /** Constructs a command that runs this command first and then `other` if this command does not succeed.*/
+ def #|| (other: ProcessBuilder): ProcessBuilder
+ /** Constructs a command that will run this command and pipes the output to `other`. `other` must be a simple command.*/
+ def #| (other: ProcessBuilder): ProcessBuilder
+ /** Constructs a command that will run this command and then `other`. The exit code will be the exit code of `other`.*/
+ def ### (other: ProcessBuilder): ProcessBuilder
+
+ def canPipeTo: Boolean
+}
+
+object ProcessBuilder extends ProcessBuilderImpl {
+ trait URLBuilder extends Source {
+
+ }
+ trait FileBuilder extends Sink with Source {
+ def #<<(f: File): ProcessBuilder
+ def #<<(u: URL): ProcessBuilder
+ def #<<(i: => InputStream): ProcessBuilder
+ def #<<(p: ProcessBuilder): ProcessBuilder
+ }
+ trait Source {
+ protected def toSource: ProcessBuilder
+ /** Writes the output stream of this process to the given file. */
+ def #> (f: File): ProcessBuilder = toFile(f, false)
+ /** Appends the output stream of this process to the given file. */
+ def #>> (f: File): ProcessBuilder = toFile(f, true)
+ /** Writes the output stream of this process to the given OutputStream. The
+ * argument is call-by-name, so the stream is recreated, written, and closed each
+ * time this process is executed. */
+ def #>(out: => OutputStream): ProcessBuilder = #> (new OStreamBuilder(out, "<output stream>"))
+ def #>(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(toSource, b, false)
+ def cat = toSource
+ private def toFile(f: File, append: Boolean) = #> (new FileOutput(f, append))
+ }
+ trait Sink {
+ protected def toSink: ProcessBuilder
+ /** Reads the given file into the input stream of this process. */
+ def #< (f: File): ProcessBuilder = #< (new FileInput(f))
+ /** Reads the given URL into the input stream of this process. */
+ def #< (f: URL): ProcessBuilder = #< (new URLInput(f))
+ /** Reads the given InputStream into the input stream of this process. The
+ * argument is call-by-name, so the stream is recreated, read, and closed each
+ * time this process is executed. */
+ def #<(in: => InputStream): ProcessBuilder = #< (new IStreamBuilder(in, "<input stream>"))
+ def #<(b: ProcessBuilder): ProcessBuilder = new PipedBuilder(b, toSink, false)
+ }
+}
diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala
new file mode 100644
index 0000000000..3e6d41b5b6
--- /dev/null
+++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala
@@ -0,0 +1,196 @@
+/* __ *\
+** ________ ___ / / ___ 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)
+ }
+} \ No newline at end of file
diff --git a/src/library/scala/sys/process/ProcessIO.scala b/src/library/scala/sys/process/ProcessIO.scala
new file mode 100644
index 0000000000..b9ac156044
--- /dev/null
+++ b/src/library/scala/sys/process/ProcessIO.scala
@@ -0,0 +1,23 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+import processAliases._
+
+/** Each method will be called in a separate thread.*/
+final class ProcessIO(
+ val writeInput: OutputStream => Unit,
+ val processOutput: InputStream => Unit,
+ val processError: InputStream => Unit
+) {
+ def withOutput(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, process, processError)
+ def withError(process: InputStream => Unit): ProcessIO = new ProcessIO(writeInput, processOutput, process)
+ def withInput(write: OutputStream => Unit): ProcessIO = new ProcessIO(write, processOutput, processError)
+}
diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala
new file mode 100644
index 0000000000..aebce5ac94
--- /dev/null
+++ b/src/library/scala/sys/process/ProcessImpl.scala
@@ -0,0 +1,232 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+import processAliases._
+import java.io.{ FilterInputStream, FilterOutputStream, PipedInputStream, PipedOutputStream }
+import java.util.concurrent.LinkedBlockingQueue
+
+/** Runs provided code in a new Thread and returns the Thread instance. */
+private object Spawn {
+ def apply(f: => Unit): Thread = apply(f, false)
+ def apply(f: => Unit, daemon: Boolean): Thread = {
+ val thread = new Thread() { override def run() = { f } }
+ thread.setDaemon(daemon)
+ thread.start()
+ thread
+ }
+}
+private object Future {
+ def apply[T](f: => T): () => T = {
+ val result = new SyncVar[Either[Throwable, T]]
+ def run: Unit =
+ try result.set(Right(f))
+ catch { case e: Exception => result set Left(e) }
+
+ Spawn(run)
+
+ () => result.get match {
+ case Right(value) => value
+ case Left(exception) => throw exception
+ }
+ }
+}
+object Uncloseable {
+ def apply(in: InputStream): InputStream = new FilterInputStream(in) { override def close() { } }
+ def apply(out: OutputStream): OutputStream = new FilterOutputStream(out) { override def close() { } }
+ def protect(in: InputStream): InputStream = if (in eq System.in) Uncloseable(in) else in
+ def protect(out: OutputStream): OutputStream = if ((out eq System.out) || (out eq System.err)) Uncloseable(out) else out
+}
+
+private class AndProcess(
+ a: ProcessBuilder,
+ b: ProcessBuilder,
+ io: ProcessIO
+) extends SequentialProcess(a, b, io, _ == 0)
+
+private class OrProcess(
+ a: ProcessBuilder,
+ b: ProcessBuilder,
+ io: ProcessIO
+) extends SequentialProcess(a, b, io, _ != 0)
+
+private class ProcessSequence(
+ a: ProcessBuilder,
+ b: ProcessBuilder,
+ io: ProcessIO
+) extends SequentialProcess(a, b, io, _ => true)
+
+private class SequentialProcess(
+ a: ProcessBuilder,
+ b: ProcessBuilder,
+ io: ProcessIO,
+ evaluateSecondProcess: Int => Boolean
+) extends CompoundProcess {
+
+ protected[this] override def runAndExitValue() = {
+ val first = a.run(io)
+ runInterruptible(first.exitValue)(first.destroy()) flatMap { codeA =>
+ if (evaluateSecondProcess(codeA)) {
+ val second = b.run(io)
+ runInterruptible(second.exitValue)(second.destroy())
+ }
+ else Some(codeA)
+ }
+ }
+}
+
+private abstract class BasicProcess extends Process {
+ def start(): Unit
+}
+
+private abstract class CompoundProcess extends BasicProcess {
+ def destroy() = destroyer()
+ def exitValue() = getExitValue() getOrElse sys.error("No exit code: process destroyed.")
+ def start() = getExitValue
+
+ protected lazy val (getExitValue, destroyer) = {
+ val code = new SyncVar[Option[Int]]()
+ code set None
+ val thread = Spawn(code.set(runAndExitValue()))
+
+ (
+ Future { thread.join(); code.get },
+ () => thread.interrupt()
+ )
+ }
+
+ /** Start and block until the exit value is available and then return it in Some. Return None if destroyed (use 'run')*/
+ protected[this] def runAndExitValue(): Option[Int]
+
+ protected[this] def runInterruptible[T](action: => T)(destroyImpl: => Unit): Option[T] = {
+ try Some(action)
+ catch { case _: InterruptedException => destroyImpl; None }
+ }
+}
+
+private class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean) extends CompoundProcess {
+ protected[this] override def runAndExitValue() = {
+ val currentSource = new SyncVar[Option[InputStream]]
+ val pipeOut = new PipedOutputStream
+ val source = new PipeSource(currentSource, pipeOut, a.toString)
+ source.start()
+
+ val pipeIn = new PipedInputStream(pipeOut)
+ val currentSink = new SyncVar[Option[OutputStream]]
+ val sink = new PipeSink(pipeIn, currentSink, b.toString)
+ sink.start()
+
+ def handleOutOrError(fromOutput: InputStream) = currentSource put Some(fromOutput)
+
+ val firstIO =
+ if (toError)
+ defaultIO.withError(handleOutOrError)
+ else
+ defaultIO.withOutput(handleOutOrError)
+ val secondIO = defaultIO.withInput(toInput => currentSink put Some(toInput))
+
+ val second = b.run(secondIO)
+ val first = a.run(firstIO)
+ try {
+ runInterruptible {
+ first.exitValue
+ currentSource put None
+ currentSink put None
+ val result = second.exitValue
+ result
+ } {
+ first.destroy()
+ second.destroy()
+ }
+ }
+ finally {
+ BasicIO.close(pipeIn)
+ BasicIO.close(pipeOut)
+ }
+ }
+}
+private class PipeSource(currentSource: SyncVar[Option[InputStream]], pipe: PipedOutputStream, label: => String) extends Thread {
+ final override def run() {
+ currentSource.get match {
+ case Some(source) =>
+ try BasicIO.transferFully(source, pipe)
+ catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() }
+ finally {
+ BasicIO.close(source)
+ currentSource.unset()
+ }
+ run()
+ case None =>
+ currentSource.unset()
+ BasicIO.close(pipe)
+ }
+ }
+}
+private class PipeSink(pipe: PipedInputStream, currentSink: SyncVar[Option[OutputStream]], label: => String) extends Thread {
+ final override def run() {
+ currentSink.get match {
+ case Some(sink) =>
+ try BasicIO.transferFully(pipe, sink)
+ catch { case e: IOException => println("I/O error " + e.getMessage + " for process: " + label); e.printStackTrace() }
+ finally {
+ BasicIO.close(sink)
+ currentSink.unset()
+ }
+ run()
+ case None =>
+ currentSink.unset()
+ }
+ }
+}
+/** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O.
+* The implementation of `exitValue` waits until these threads die before returning. */
+private class DummyProcess(action: => Int) extends Process {
+ private[this] val exitCode = Future(action)
+ override def exitValue() = exitCode()
+ override def destroy() { }
+}
+/** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
+* output and error streams of the process. `inputThread` is the Thread created to write to the input stream of
+* the process.
+* The implementation of `exitValue` interrupts `inputThread` and then waits until all I/O threads die before
+* returning. */
+private class SimpleProcess(p: JProcess, inputThread: Thread, outputThreads: List[Thread]) extends Process {
+ override def exitValue() = {
+ try p.waitFor() // wait for the process to terminate
+ finally inputThread.interrupt() // we interrupt the input thread to notify it that it can terminate
+
+ outputThreads.foreach(_.join()) // this ensures that all output is complete before returning (waitFor does not ensure this)
+ p.exitValue()
+ }
+ override def destroy() = {
+ try p.destroy()
+ finally { inputThread.interrupt() }
+ }
+}
+private final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process {
+ override def exitValue() = {
+ thread.join()
+ if (success.get) 0 else 1
+ }
+ override def destroy() { thread.interrupt() }
+}
+
+private object Streamed {
+ def apply[T](nonzeroException: Boolean): Streamed[T] = {
+ val q = new LinkedBlockingQueue[Either[Int, T]]
+ def next(): Stream[T] = q.take match {
+ case Left(0) => Stream.empty
+ case Left(code) => if (nonzeroException) error("Nonzero exit code: " + code) else Stream.empty
+ case Right(s) => Stream.cons(s, next)
+ }
+ new Streamed((s: T) => q.put(Right(s)), code => q.put(Left(code)), () => next())
+ }
+}
+private final class Streamed[T](val process: T => Unit, val done: Int => Unit, val stream: () => Stream[T]) extends NotNull \ No newline at end of file
diff --git a/src/library/scala/sys/process/ProcessLogger.scala b/src/library/scala/sys/process/ProcessLogger.scala
new file mode 100644
index 0000000000..107921d66a
--- /dev/null
+++ b/src/library/scala/sys/process/ProcessLogger.scala
@@ -0,0 +1,16 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+package process
+
+trait ProcessLogger {
+ def info(s: => String): Unit
+ def error(s: => String): Unit
+ def buffer[T](f: => T): T
+}
diff --git a/src/library/scala/sys/process/package.scala b/src/library/scala/sys/process/package.scala
new file mode 100644
index 0000000000..659c883d39
--- /dev/null
+++ b/src/library/scala/sys/process/package.scala
@@ -0,0 +1,25 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.sys
+
+package object process extends ProcessImplicits {
+ // These are in a nested object instead of at the package level
+ // due to the issues described in tickets #3160 and #3836.
+ private[process] object processAliases {
+ type Closeable = java.io.Closeable
+ type File = java.io.File
+ type IOException = java.io.IOException
+ type InputStream = java.io.InputStream
+ type JProcess = java.lang.Process
+ type JProcessBuilder = java.lang.ProcessBuilder
+ type OutputStream = java.io.OutputStream
+ type SyncVar[T] = scala.concurrent.SyncVar[T]
+ type URL = java.net.URL
+ }
+}
diff --git a/src/library/scala/util/parsing/combinator/Parsers.scala b/src/library/scala/util/parsing/combinator/Parsers.scala
index 9e69d73f44..7e1f815bbb 100644
--- a/src/library/scala/util/parsing/combinator/Parsers.scala
+++ b/src/library/scala/util/parsing/combinator/Parsers.scala
@@ -137,7 +137,7 @@ trait Parsers {
def flatMapWithNext[U](f: Nothing => Input => ParseResult[U]): ParseResult[U]
= this
- def get: Nothing = system.error("No result when parsing failed")
+ def get: Nothing = sys.error("No result when parsing failed")
}
/** An extractor so NoSuccess(msg, next) can be used in matches.
*/
diff --git a/src/library/scala/xml/dtd/ContentModelParser.scala b/src/library/scala/xml/dtd/ContentModelParser.scala
index d98b031744..b6df4d47e1 100644
--- a/src/library/scala/xml/dtd/ContentModelParser.scala
+++ b/src/library/scala/xml/dtd/ContentModelParser.scala
@@ -21,10 +21,10 @@ object ContentModelParser extends Scanner { // a bit too permissive concerning #
def accept(tok: Int) = {
if (token != tok) {
if ((tok == STAR) && (token == END)) // common mistake
- system.error("in DTDs, \n"+
+ sys.error("in DTDs, \n"+
"mixed content models must be like (#PCDATA|Name|Name|...)*");
else
- system.error("expected "+token2string(tok)+
+ sys.error("expected "+token2string(tok)+
", got unexpected token:"+token2string(token));
}
nextToken
@@ -45,7 +45,7 @@ object ContentModelParser extends Scanner { // a bit too permissive concerning #
case NAME => value match {
case "ANY" => ANY
case "EMPTY" => EMPTY
- case _ => system.error("expected ANY, EMPTY or '(' instead of " + value );
+ case _ => sys.error("expected ANY, EMPTY or '(' instead of " + value );
}
case LPAREN =>
@@ -65,12 +65,12 @@ object ContentModelParser extends Scanner { // a bit too permissive concerning #
accept( STAR );
res
case _ =>
- system.error("unexpected token:" + token2string(token) );
+ sys.error("unexpected token:" + token2string(token) );
}
}
case _ =>
- system.error("unexpected token:" + token2string(token) );
+ sys.error("unexpected token:" + token2string(token) );
}
// sopt ::= S?
def sOpt = if( token == S ) nextToken;
@@ -118,12 +118,12 @@ object ContentModelParser extends Scanner { // a bit too permissive concerning #
def particle = token match {
case LPAREN => nextToken; sOpt; regexp;
case NAME => val a = Letter(ElemName(value)); nextToken; maybeSuffix(a)
- case _ => system.error("expected '(' or Name, got:"+token2string(token));
+ case _ => sys.error("expected '(' or Name, got:"+token2string(token));
}
// atom ::= name
def atom = token match {
case NAME => val a = Letter(ElemName(value)); nextToken; a
- case _ => system.error("expected Name, got:"+token2string(token));
+ case _ => sys.error("expected Name, got:"+token2string(token));
}
}
diff --git a/src/library/scala/xml/dtd/Scanner.scala b/src/library/scala/xml/dtd/Scanner.scala
index 1ac3c0f24d..9355848e02 100644
--- a/src/library/scala/xml/dtd/Scanner.scala
+++ b/src/library/scala/xml/dtd/Scanner.scala
@@ -44,7 +44,7 @@ class Scanner extends Tokens with parsing.TokenTests {
final def next = if (it.hasNext) c = it.next else c = ENDCH
final def acc(d: Char) {
- if (c == d) next else system.error("expected '"+d+"' found '"+c+"' !");
+ if (c == d) next else sys.error("expected '"+d+"' found '"+c+"' !");
}
final def accS(ds: Seq[Char]) { ds foreach acc }
@@ -65,7 +65,7 @@ class Scanner extends Tokens with parsing.TokenTests {
case ENDCH => END
case _ =>
if (isNameStart(c)) name; // NAME
- else system.error("unexpected character:" + c)
+ else sys.error("unexpected character:" + c)
}
final def name = {
diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala
index c1cb5a197a..24f3d39e4c 100644
--- a/src/library/scala/xml/parsing/MarkupParser.scala
+++ b/src/library/scala/xml/parsing/MarkupParser.scala
@@ -866,7 +866,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests
new PublicID(pubID, sysID);
} else {
reportSyntaxError("PUBLIC or SYSTEM expected");
- system.error("died parsing notationdecl")
+ sys.error("died parsing notationdecl")
}
xSpaceOpt
xToken('>')
diff --git a/src/library/scala/xml/parsing/MarkupParserCommon.scala b/src/library/scala/xml/parsing/MarkupParserCommon.scala
index 3b31f9976e..49200045d8 100644
--- a/src/library/scala/xml/parsing/MarkupParserCommon.scala
+++ b/src/library/scala/xml/parsing/MarkupParserCommon.scala
@@ -24,7 +24,7 @@ import MarkupParserCommon._
* All members should be accessed through those.
*/
private[scala] trait MarkupParserCommon extends TokenTests {
- protected def unreachable = system.error("Cannot be reached.")
+ protected def unreachable = sys.error("Cannot be reached.")
// type HandleType // MarkupHandler, SymbolicXMLBuilder
type InputType // Source, CharArrayReader
@@ -85,7 +85,7 @@ private[scala] trait MarkupParserCommon extends TokenTests {
case `end` => return buf.toString
case ch => buf append ch
}
- system.error("Expected '%s'".format(end))
+ sys.error("Expected '%s'".format(end))
}
/** [42] '<' xmlEndTag ::= '<' '/' Name S? '>'
diff --git a/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala b/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala
index 12d0205469..d63a612017 100644
--- a/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala
+++ b/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala
@@ -35,7 +35,7 @@ abstract class ValidatingMarkupHandler extends MarkupHandler with Logged {
val res = decl.contentModel.validate(ns);
Console.println("res = "+res);
if(!res)
- //system.error("invalid!");
+ //sys.error("invalid!");
}
*/