diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright © 2009 Joachim Breitner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+module Main where
+import Distribution.Simple
+main = defaultMain
diff --git a/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,17 @@
+import System.Terminal.Concurrent
+import Control.Concurrent
+
+testThread co n1 n2 = do
+	t <- myThreadId
+	threadDelay $ n1 * 100000
+	writeConcurrent co ("Thread " ++ show t ++ ": ")
+	threadDelay $ n1 * 100000
+	writeConcurrent co "still working... "
+	threadDelay $ n2 * 100000
+	writeConcurrentDone co "done"
+
+main = do
+	co <- startConcurrentOutput
+	mapM forkIO  $ zipWith (testThread co) [0,0,3,4,3] [4,8,5,5,2]
+	threadDelay $ 15*100000
+	
diff --git a/concurrentoutput.cabal b/concurrentoutput.cabal
new file mode 100644
--- /dev/null
+++ b/concurrentoutput.cabal
@@ -0,0 +1,58 @@
+Name:            concurrentoutput
+version:         0.1
+Category:        User Interfaces
+author:          Joachim Breitner
+maintainer:      Joachim Breitner <mail@joachim-breitner.de>
+Synopsis:        Ungarble output from several threads 
+Description:     This library provides a simple interface to output status
+                 messages from more than one thread.
+		 .
+		 It will continue adding information (such as dots, or "done")
+		 to the correct line and continue scrolling when a line is done.
+		 .
+		 For example, this screen:
+		 .
+		 @Thread ThreadId 27: still working... done@
+		 .
+                 @Thread ThreadId 25: still working... @
+		 .
+                 @Thread ThreadId 26: still working... @
+	         .
+		 @_@
+		 .
+		 will, once thread 25 has finished, look like this
+		 .	
+                 @Thread ThreadId 25: still working... done@
+		 .
+		 @Thread ThreadId 27: still working... done@
+		 .
+                 @Thread ThreadId 26: still working... @
+	         .
+		 @_@
+		 .
+		 If standard output is not a terminal, it will only print
+		 complete lines.
+		 .
+		 At the moment, it can only handle lines that are shorter than
+		 the terminal. If they are not, output will be garbled again.
+		 .
+		 
+Cabal-Version:   >= 1.6
+License:         BSD3
+License-File:    LICENSE
+extra-source-files: Test.hs
+Build-Type:      Simple
+
+Library
+        hs-source-dirs: src
+        Exposed-Modules: System.Terminal.Concurrent
+        Build-Depends:  base > 3 && < 4
+
+source-repository head
+  type:     darcs
+  location: http://darcs.nomeata.de/concurrentoutput/
+
+source-repository this
+  type:     darcs
+  location: http://darcs.nomeata.de/darcswatch/
+  tag:      v0.1
diff --git a/src/System/Terminal/Concurrent.hs b/src/System/Terminal/Concurrent.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Terminal/Concurrent.hs
@@ -0,0 +1,97 @@
+-- | This library provides a simple interface to output status
+--   messages from more than one thread.
+--
+--   It will continue adding information (such as dots, or "done")
+--   to the correct line corresponding to the issuing thread and continue
+--   scrolling when a line is done.
+module System.Terminal.Concurrent 
+	( ConcurrentOutput
+	, startConcurrentOutput
+	, writeConcurrent
+	, writeConcurrentDone
+	) where
+
+import Control.Concurrent
+import Control.Monad
+import Data.List
+import System.IO
+
+data ConcurrentOutput = ConcurrentOutput { coChan :: Chan (ThreadId, Bool, String) }
+
+-- | Starts the thread responsible for gathering and formatting the outputs.
+--    
+--   You can not kill this thread, so only start one for your application.
+startConcurrentOutput :: IO ConcurrentOutput
+startConcurrentOutput = do
+	chan <- newChan
+	isTerm <- hIsTerminalDevice stdout
+	forkIO (receiverThread chan isTerm)
+	return (ConcurrentOutput chan)
+
+-- | Begin a new line of output for your thread or, if there already is one, append to it.
+--    
+--   Do not put newline characters in there, or it will break the output. This
+--   will also happen if your total line will be wider than the terminal.
+writeConcurrent :: ConcurrentOutput -> String -> IO ()
+writeConcurrent (ConcurrentOutput chan) s = do
+	threadId <- myThreadId
+	writeChan chan (threadId, False, s)
+
+-- | Finish your line of output with the given string.
+--    
+--   Do not put newline characters in there, or it will break the output. This
+--   will also happen if your total line will be wider than the terminal.
+writeConcurrentDone :: ConcurrentOutput -> String -> IO ()
+writeConcurrentDone (ConcurrentOutput chan) s = do
+	threadId <- myThreadId
+	writeChan chan (threadId, True, s)
+
+-- What threadid has written to what line, counting from below
+type OutputState = [(ThreadId,String)]
+
+receiverThread :: Chan (ThreadId, Bool, String) -> Bool -> IO ()
+receiverThread chan True = runner []
+	where runner os = do
+		(t,d,s) <- readChan chan
+
+	        let s' = maybe s (++s) (lookup t os)
+		let os'= if d then filter ((/=t).fst) os
+		              else replaceOrAppend t s' os
+		unless (null os) $ do
+			-- Go up some lines
+			putStr $ "\ESC[" ++ show (length os) ++ "A"
+			-- Clear everything
+			unless (null os) $ putStr "\ESC[0J"
+		-- Go up once more, to make it fit
+		putStr $ "\ESC[1A"
+
+		-- Write a new done line, if suitable
+		when d $ putLnStr s' 
+
+		-- Write the new not-yet-done lines
+		mapM_ (putLnStr) $ map snd os'
+
+		-- Go to the beginning of the next new line
+		putStrLn ""
+
+		runner os'
+-- We do not have a terminal, just output complete lines
+receiverThread chan False = runner []
+	where runner os = do
+		(t,d,s) <- readChan chan
+
+	        let s' = maybe s (++s) (lookup t os)
+		let os'= if d then filter ((/=t).fst) os
+		              else replaceOrAppend t s' os
+
+		-- Write a new done line, if suitable
+		when d $ putStrLn s' 
+
+		runner os'
+
+replaceOrAppend :: (Eq a) => a -> b -> [(a,b)] -> [(a,b)]
+replaceOrAppend a b []                       = [(a,b)]
+replaceOrAppend a b ((a',b'):xs) | a == a'   = (a,b):xs
+                                 | otherwise = (a',b') : replaceOrAppend a b xs
+
+putLnStr s = putStr ('\n':s)
