summary refs log tree commit diff stats
path: root/lib/impure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/impure')
-rw-r--r--lib/impure/db_mysql.nim182
-rw-r--r--lib/impure/db_postgres.nim139
-rw-r--r--lib/impure/db_sqlite.nim185
-rw-r--r--lib/impure/dialogs.nim226
-rw-r--r--lib/impure/graphics.nim575
-rw-r--r--lib/impure/nre.nim686
-rw-r--r--lib/impure/nre/.gitignore9
-rw-r--r--lib/impure/nre/private/util.nim63
-rw-r--r--lib/impure/rdstdin.nim28
-rw-r--r--lib/impure/re.nim167
-rw-r--r--lib/impure/ssl.nim39
-rw-r--r--lib/impure/zipfiles.nim169
12 files changed, 1203 insertions, 1265 deletions
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index b8180cd87..7f7511264 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -7,40 +7,75 @@
 #    distribution, for details about the copyright.
 #
 
-## A higher level `mySQL`:idx: database wrapper. The same interface is 
+## A higher level `mySQL`:idx: database wrapper. The same interface is
 ## implemented for other databases too.
+##
+## Example:
+##
+## .. code-block:: Nim
+##
+##  import db_mysql, math
+##
+##  let theDb = open("localhost", "nim", "nim", "test")
+##
+##  theDb.exec(sql"Drop table if exists myTestTbl")
+##  theDb.exec(sql("create table myTestTbl (" &
+##      " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
+##      " Name  VARCHAR(50) NOT NULL, " &
+##      " i     INT(11), " &
+##      " f     DECIMAL(18,10))"))
+##
+##  theDb.exec(sql"START TRANSACTION")
+##  for i in 1..1000:
+##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##          "Item#" & $i, i, sqrt(i.float))
+##  theDb.exec(sql"COMMIT")
+##
+##  for x in theDb.fastRows(sql"select * from myTestTbl"):
+##    echo x
+##
+##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##          "Item#1001", 1001, sqrt(1001.0))
+##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
+##
+##  theDb.close()
+
 
 import strutils, mysql
 
 type
-  TDbConn* = PMySQL    ## encapsulates a database connection
-  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
+  DbConn* = PMySQL    ## encapsulates a database connection
+  Row* = seq[string]   ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = tuple[row: cstringArray, len: int]  ## a handle that can be
+                                                    ## used to get a row's
+                                                    ## column text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
 
-  TSqlQuery* = distinct string ## an SQL query string
+  SqlQuery* = distinct string ## an SQL query string
 
   FDb* = object of IOEffect ## effect that denotes a database operation
   FReadDb* = object of FDb   ## effect that denotes a read operation
   FWriteDb* = object of FDb  ## effect that denotes a write operation
+{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
 
-proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
-  ## constructs a TSqlQuery from the string `query`. This is supposed to be 
+proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
+  ## constructs a SqlQuery from the string `query`. This is supposed to be
   ## used as a raw-string-literal modifier:
   ## ``sql"update user set counter = counter + 1"``
   ##
-  ## If assertions are turned off, it does nothing. If assertions are turned 
+  ## If assertions are turned off, it does nothing. If assertions are turned
   ## on, later versions will check the string for valid syntax.
-  result = TSqlQuery(query)
+  result = SqlQuery(query)
 
-proc dbError(db: TDbConn) {.noreturn.} = 
+proc dbError(db: DbConn) {.noreturn.} =
   ## raises an EDb exception.
   var e: ref EDb
   new(e)
   e.msg = $mysql.error(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} = 
+proc dbError*(msg: string) {.noreturn.} =
   ## raises an EDb exception with message `msg`.
   var e: ref EDb
   new(e)
@@ -48,12 +83,12 @@ proc dbError*(msg: string) {.noreturn.} =
   raise e
 
 when false:
-  proc dbQueryOpt*(db: TDbConn, query: string, args: varargs[string, `$`]) =
+  proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
     var stmt = mysql_stmt_init(db)
     if stmt == nil: dbError(db)
-    if mysql_stmt_prepare(stmt, query, len(query)) != 0: 
+    if mysql_stmt_prepare(stmt, query, len(query)) != 0:
       dbError(db)
-    var 
+    var
       binding: seq[MYSQL_BIND]
     discard mysql_stmt_close(stmt)
 
@@ -65,7 +100,7 @@ proc dbQuote*(s: string): string =
     else: add(result, c)
   add(result, '\'')
 
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
+proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   result = ""
   var a = 0
   for c in items(string(formatstr)):
@@ -75,39 +110,43 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
       else:
         add(result, dbQuote(args[a]))
       inc(a)
-    else: 
+    else:
       add(result, c)
-  
-proc tryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {.
+
+proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
   tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   return mysql.realQuery(db, q, q.len) == 0'i32
 
-proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
+proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
   var q = dbFormat(query, args)
   if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
 
-proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
+proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
   tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
   if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
-    
-proc newRow(L: int): TRow = 
+
+proc newRow(L: int): Row =
   newSeq(result, L)
   for i in 0..L-1: result[i] = ""
-  
-proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =  
+
+proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
   if row != nil:
     while mysql.fetchRow(sqlres) != nil: discard
   mysql.freeResult(sqlres)
-  
-iterator fastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
-  ## executes the query and iterates over the result dataset. This is very 
-  ## fast, but potenially dangerous: If the for-loop-body executes another
-  ## query, the results can be undefined. For MySQL this is the case!.
+
+iterator fastRows*(db: DbConn, query: SqlQuery,
+                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+  ## executes the query and iterates over the result dataset.
+  ##
+  ## This is very fast, but potentially dangerous.  Use this iterator only
+  ## if you require **ALL** the rows.
+  ##
+  ## Breaking the fastRows() iterator during a loop will cause the next
+  ## database query to raise an [EDb] exception ``Commands out of sync``.
   rawExec(db, query, args)
   var sqlres = mysql.useResult(db)
   if sqlres != nil:
@@ -117,7 +156,7 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
     while true:
       row = mysql.fetchRow(sqlres)
       if row == nil: break
-      for i in 0..L-1: 
+      for i in 0..L-1:
         setLen(result[i], 0)
         if row[i] == nil:
           result[i] = nil
@@ -126,18 +165,42 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
       yield result
     properFreeResult(sqlres, row)
 
-proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the interator body.
+  rawExec(db, query, args)
+  var sqlres = mysql.useResult(db)
+  if sqlres != nil:
+    let L = int(mysql.numFields(sqlres))
+    var row: cstringArray
+    while true:
+      row = mysql.fetchRow(sqlres)
+      if row == nil: break
+      yield (row: row, len: L)
+    properFreeResult(sqlres, row)
+
+proc `[]`*(row: InstantRow, col: int): string {.inline.} =
+  ## returns text for given column of the row
+  $row.row[col]
+
+proc len*(row: InstantRow): int {.inline.} =
+  ## returns number of columns in the row
+  row.len
+
+proc getRow*(db: DbConn, query: SqlQuery,
+             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a TRow with empty strings for each column.
+  ## will return a Row with empty strings for each column.
   rawExec(db, query, args)
   var sqlres = mysql.useResult(db)
   if sqlres != nil:
     var L = int(mysql.numFields(sqlres))
     result = newRow(L)
     var row = mysql.fetchRow(sqlres)
-    if row != nil: 
-      for i in 0..L-1: 
+    if row != nil:
+      for i in 0..L-1:
         setLen(result[i], 0)
         if row[i] == nil:
           result[i] = nil
@@ -145,8 +208,8 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
           add(result[i], row[i])
     properFreeResult(sqlres, row)
 
-proc getAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
+proc getAllRows*(db: DbConn, query: SqlQuery,
+                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   rawExec(db, query, args)
@@ -168,70 +231,67 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery,
       inc(j)
     mysql.freeResult(sqlres)
 
-iterator rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+iterator rows*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## same as `fastRows`, but slower and safe.
   for r in items(getAllRows(db, query, args)): yield r
 
-proc getValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string {.tags: [FReadDB].} = 
+proc getValue*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): string {.tags: [FReadDB].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
-  result = ""
-  for row in fastRows(db, query, args): 
-    result = row[0]
-    break
+  result = getRow(db, query, args)[0]
 
-proc tryInsertId*(db: TDbConn, query: TSqlQuery, 
+proc tryInsertId*(db: DbConn, query: SqlQuery,
                   args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
-  ## executes the query (typically "INSERT") and returns the 
+  ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
-  if mysql.realQuery(db, q, q.len) != 0'i32: 
+  if mysql.realQuery(db, q, q.len) != 0'i32:
     result = -1'i64
   else:
     result = mysql.insertId(db)
-  
-proc insertId*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = 
-  ## executes the query (typically "INSERT") and returns the 
+
+proc insertId*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+  ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row.
   result = tryInsertID(db, query, args)
   if result < 0: dbError(db)
 
-proc execAffectedRows*(db: TDbConn, query: TSqlQuery, 
+proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.
-                       tags: [FReadDB, FWriteDb].} = 
+                       tags: [FReadDB, FWriteDb].} =
   ## runs the query (typically "UPDATE") and returns the
   ## number of affected rows
   rawExec(db, query, args)
   result = mysql.affectedRows(db)
 
-proc close*(db: TDbConn) {.tags: [FDb].} = 
+proc close*(db: DbConn) {.tags: [FDb].} =
   ## closes the database connection.
   if db != nil: mysql.close(db)
 
-proc open*(connection, user, password, database: string): TDbConn {.
+proc open*(connection, user, password, database: string): DbConn {.
   tags: [FDb].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   result = mysql.init(nil)
-  if result == nil: dbError("could not open database connection") 
+  if result == nil: dbError("could not open database connection")
   let
     colonPos = connection.find(':')
     host = if colonPos < 0: connection
            else: substr(connection, 0, colonPos-1)
     port: int32 = if colonPos < 0: 0'i32
                   else: substr(connection, colonPos+1).parseInt.int32
-  if mysql.realConnect(result, host, user, password, database, 
+  if mysql.realConnect(result, host, user, password, database,
                        port, nil, 0) == nil:
     var errmsg = $mysql.error(result)
     db_mysql.close(result)
     dbError(errmsg)
 
-proc setEncoding*(connection: TDbConn, encoding: string): bool {.
+proc setEncoding*(connection: DbConn, encoding: string): bool {.
   tags: [FDb].} =
-  ## sets the encoding of a database connection, returns true for 
+  ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
-  result = mysql.set_character_set(connection, encoding) == 0
\ No newline at end of file
+  result = mysql.set_character_set(connection, encoding) == 0
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index ffb8bbcda..b75915a72 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -7,34 +7,39 @@
 #    distribution, for details about the copyright.
 #
 
-## A higher level `PostgreSQL`:idx: database wrapper. This interface 
+## A higher level `PostgreSQL`:idx: database wrapper. This interface
 ## is implemented for other databases too.
 
 import strutils, postgres
 
 type
-  TDbConn* = PPGconn   ## encapsulates a database connection
-  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
+  DbConn* = PPGconn   ## encapsulates a database connection
+  Row* = seq[string]  ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = tuple[res: PPGresult, line: int32]  ## a handle that can be
+                                                    ## used to get a row's
+                                                    ## column text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
-  
-  TSqlQuery* = distinct string ## an SQL query string
-  TSqlPrepared* = distinct string ## a identifier for the prepared queries
+
+  SqlQuery* = distinct string ## an SQL query string
+  SqlPrepared* = distinct string ## a identifier for the prepared queries
 
   FDb* = object of IOEffect ## effect that denotes a database operation
   FReadDb* = object of FDb   ## effect that denotes a read operation
   FWriteDb* = object of FDb  ## effect that denotes a write operation
+{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
+              TSqlPrepared: SqlPrepared].}
 
-proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
-  ## constructs a TSqlQuery from the string `query`. This is supposed to be 
+proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
+  ## constructs a SqlQuery from the string `query`. This is supposed to be
   ## used as a raw-string-literal modifier:
   ## ``sql"update user set counter = counter + 1"``
   ##
-  ## If assertions are turned off, it does nothing. If assertions are turned 
+  ## If assertions are turned off, it does nothing. If assertions are turned
   ## on, later versions will check the string for valid syntax.
-  result = TSqlQuery(query)
- 
-proc dbError*(db: TDbConn) {.noreturn.} =
+  result = SqlQuery(query)
+
+proc dbError*(db: DbConn) {.noreturn.} =
   ## raises an EDb exception.
   var e: ref EDb
   new(e)
@@ -56,7 +61,7 @@ proc dbQuote*(s: string): string =
     else: add(result, c)
   add(result, '\'')
 
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
+proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   result = ""
   var a = 0
   for c in items(string(formatstr)):
@@ -68,8 +73,8 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
       inc(a)
     else:
       add(result, c)
-  
-proc tryExec*(db: TDbConn, query: TSqlQuery,
+
+proc tryExec*(db: DbConn, query: SqlQuery,
               args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var arr = allocCStringArray(args)
@@ -79,7 +84,7 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
   result = pqresultStatus(res) == PGRES_COMMAND_OK
   pqclear(res)
 
-proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
+proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
   tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   var arr = allocCStringArray(args)
@@ -89,7 +94,7 @@ proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
   if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
   pqclear(res)
 
-proc exec*(db: TDbConn, stmtName: TSqlPrepared,
+proc exec*(db: DbConn, stmtName: SqlPrepared,
           args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
   var arr = allocCStringArray(args)
   var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@@ -98,11 +103,11 @@ proc exec*(db: TDbConn, stmtName: TSqlPrepared,
   if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
   pqclear(res)
 
-proc newRow(L: int): TRow =
+proc newRow(L: int): Row =
   newSeq(result, L)
   for i in 0..L-1: result[i] = ""
-  
-proc setupQuery(db: TDbConn, query: TSqlQuery,
+
+proc setupQuery(db: DbConn, query: SqlQuery,
                 args: varargs[string]): PPGresult =
   var arr = allocCStringArray(args)
   result = pqexecParams(db, query.string, int32(args.len), nil, arr,
@@ -110,7 +115,7 @@ proc setupQuery(db: TDbConn, query: TSqlQuery,
   deallocCStringArray(arr)
   if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
 
-proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
+proc setupQuery(db: DbConn, stmtName: SqlPrepared,
                  args: varargs[string]): PPGresult =
   var arr = allocCStringArray(args)
   result = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
@@ -118,13 +123,13 @@ proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
   deallocCStringArray(arr)
   if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
 
-proc prepare*(db: TDbConn; stmtName: string, query: TSqlQuery;
-              nParams: int): TSqlPrepared =
+proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
+              nParams: int): SqlPrepared =
   var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
   if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  return TSqlPrepared(stmtName)
-   
-proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
+  return SqlPrepared(stmtName)
+
+proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
   for col in 0..cols-1:
     setLen(r[col], 0)
     let x = pqgetvalue(res, line, col)
@@ -133,9 +138,9 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
     else:
       add(r[col], x)
 
-iterator fastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
-  ## executes the query and iterates over the result dataset. This is very 
+iterator fastRows*(db: DbConn, query: SqlQuery,
+                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
+  ## executes the query and iterates over the result dataset. This is very
   ## fast, but potenially dangerous: If the for-loop-body executes another
   ## query, the results can be undefined. For Postgres it is safe though.
   var res = setupQuery(db, query, args)
@@ -146,8 +151,8 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
     yield result
   pqclear(res)
 
-iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
-                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
+                   args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## executes the prepared query and iterates over the result dataset.
   var res = setupQuery(db, stmtName, args)
   var L = pqNfields(res)
@@ -157,74 +162,92 @@ iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
     yield result
   pqClear(res)
 
-proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within interator body.
+  var res = setupQuery(db, query, args)
+  for i in 0..pqNtuples(res)-1:
+    yield (res: res, line: i)
+  pqClear(res)
+
+proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
+  ## returns text for given column of the row
+  $pqgetvalue(row.res, row.line, col)
+
+proc len*(row: InstantRow): int32 {.inline.} =
+  ## returns number of columns in the row
+  pqNfields(row.res)
+
+proc getRow*(db: DbConn, query: SqlQuery,
+             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a TRow with empty strings for each column.
+  ## will return a Row with empty strings for each column.
   var res = setupQuery(db, query, args)
   var L = pqnfields(res)
   result = newRow(L)
   setRow(res, result, 0, L)
   pqclear(res)
 
-proc getRow*(db: TDbConn, stmtName: TSqlPrepared,
-             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+proc getRow*(db: DbConn, stmtName: SqlPrepared,
+             args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   var res = setupQuery(db, stmtName, args)
   var L = pqNfields(res)
   result = newRow(L)
   setRow(res, result, 0, L)
   pqClear(res)
 
-proc getAllRows*(db: TDbConn, query: TSqlQuery,
-                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
+proc getAllRows*(db: DbConn, query: SqlQuery,
+                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, query, args):
     result.add(r)
 
-proc getAllRows*(db: TDbConn, stmtName: TSqlPrepared,
-                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
+proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
+                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
   ## executes the prepared query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, stmtName, args):
     result.add(r)
 
-iterator rows*(db: TDbConn, query: TSqlQuery,
-               args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
+iterator rows*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## same as `fastRows`, but slower and safe.
   for r in items(getAllRows(db, query, args)): yield r
 
-proc getValue*(db: TDbConn, query: TSqlQuery,
+proc getValue*(db: DbConn, query: SqlQuery,
                args: varargs[string, `$`]): string {.tags: [FReadDB].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
   var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
   result = if isNil(x): "" else: $x
-  
-proc tryInsertID*(db: TDbConn, query: TSqlQuery,
+
+proc tryInsertID*(db: DbConn, query: SqlQuery,
                   args: varargs[string, `$`]): int64  {.tags: [FWriteDb].}=
-  ## executes the query (typically "INSERT") and returns the 
+  ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``. 
-  var x = pqgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"), 
+  ## named ``id``.
+  var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
     args), 0, 0)
   if not isNil(x):
     result = parseBiggestInt($x)
   else:
     result = -1
 
-proc insertID*(db: TDbConn, query: TSqlQuery,
+proc insertID*(db: DbConn, query: SqlQuery,
                args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
-  ## executes the query (typically "INSERT") and returns the 
+  ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``. 
+  ## named ``id``.
   result = tryInsertID(db, query, args)
   if result < 0: dbError(db)
-  
-proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
+
+proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.tags: [
                        FReadDB, FWriteDb].} =
   ## executes the query (typically "UPDATE") and returns the
@@ -235,11 +258,11 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
   result = parseBiggestInt($pqcmdTuples(res))
   pqclear(res)
 
-proc close*(db: TDbConn) {.tags: [FDb].} =
+proc close*(db: DbConn) {.tags: [FDb].} =
   ## closes the database connection.
   if db != nil: pqfinish(db)
 
-proc open*(connection, user, password, database: string): TDbConn {.
+proc open*(connection, user, password, database: string): DbConn {.
   tags: [FDb].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
@@ -261,8 +284,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
   result = pqsetdbLogin(nil, nil, nil, nil, database, user, password)
   if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
 
-proc setEncoding*(connection: TDbConn, encoding: string): bool {.
+proc setEncoding*(connection: DbConn, encoding: string): bool {.
   tags: [FDb].} =
-  ## sets the encoding of a database connection, returns true for 
+  ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
-  return pqsetClientEncoding(connection, encoding) == 0
\ No newline at end of file
+  return pqsetClientEncoding(connection, encoding) == 0
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 4be692f39..8366fdadc 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -7,40 +7,73 @@
 #    distribution, for details about the copyright.
 #
 
-## A higher level `SQLite`:idx: database wrapper. This interface 
+## A higher level `SQLite`:idx: database wrapper. This interface
 ## is implemented for other databases too.
+##
+## Example:
+##
+## .. code-block:: nim
+##
+##  import db_sqlite, math
+##
+##  let theDb = open("mytest.db", nil, nil, nil)
+##
+##  theDb.exec(sql"Drop table if exists myTestTbl")
+##  theDb.exec(sql("""create table myTestTbl (
+##       Id    INTEGER PRIMARY KEY,
+##       Name  VARCHAR(50) NOT NULL,
+##       i     INT(11),
+##       f     DECIMAL(18,10))"""))
+##
+##  theDb.exec(sql"BEGIN")
+##  for i in 1..1000:
+##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##          "Item#" & $i, i, sqrt(i.float))
+##  theDb.exec(sql"COMMIT")
+##
+##  for x in theDb.fastRows(sql"select * from myTestTbl"):
+##    echo x
+##
+##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
+##        "Item#1001", 1001, sqrt(1001.0))
+##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
+##
+##  theDb.close()
 
 import strutils, sqlite3
 
 type
-  TDbConn* = PSqlite3  ## encapsulates a database connection
-  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
+  DbConn* = PSqlite3  ## encapsulates a database connection
+  Row* = seq[string]  ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = Pstmt  ## a handle that can be used to get a row's column
+                       ## text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
-  
-  TSqlQuery* = distinct string ## an SQL query string
-  
+
+  SqlQuery* = distinct string ## an SQL query string
+
   FDb* = object of IOEffect ## effect that denotes a database operation
   FReadDb* = object of FDb   ## effect that denotes a read operation
   FWriteDb* = object of FDb  ## effect that denotes a write operation
-  
-proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
-  ## constructs a TSqlQuery from the string `query`. This is supposed to be 
+{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
+
+proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
+  ## constructs a SqlQuery from the string `query`. This is supposed to be
   ## used as a raw-string-literal modifier:
   ## ``sql"update user set counter = counter + 1"``
   ##
-  ## If assertions are turned off, it does nothing. If assertions are turned 
+  ## If assertions are turned off, it does nothing. If assertions are turned
   ## on, later versions will check the string for valid syntax.
-  result = TSqlQuery(query)
- 
-proc dbError(db: TDbConn) {.noreturn.} = 
+  result = SqlQuery(query)
+
+proc dbError(db: DbConn) {.noreturn.} =
   ## raises an EDb exception.
   var e: ref EDb
   new(e)
   e.msg = $sqlite3.errmsg(db)
   raise e
 
-proc dbError*(msg: string) {.noreturn.} = 
+proc dbError*(msg: string) {.noreturn.} =
   ## raises an EDb exception with message `msg`.
   var e: ref EDb
   new(e)
@@ -55,7 +88,7 @@ proc dbQuote(s: string): string =
     else: add(result, c)
   add(result, '\'')
 
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
+proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   result = ""
   var a = 0
   for c in items(string(formatstr)):
@@ -64,8 +97,8 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
       inc(a)
     else:
       add(result, c)
-  
-proc tryExec*(db: TDbConn, query: TSqlQuery, 
+
+proc tryExec*(db: DbConn, query: SqlQuery,
               args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
@@ -74,72 +107,94 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
     if step(stmt) == SQLITE_DONE:
       result = finalize(stmt) == SQLITE_OK
 
-proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`])  {.
+proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`])  {.
   tags: [FReadDb, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   if not tryExec(db, query, args): dbError(db)
-  
-proc newRow(L: int): TRow =
+
+proc newRow(L: int): Row =
   newSeq(result, L)
   for i in 0..L-1: result[i] = ""
-  
-proc setupQuery(db: TDbConn, query: TSqlQuery, 
-                args: varargs[string]): Pstmt = 
+
+proc setupQuery(db: DbConn, query: SqlQuery,
+                args: varargs[string]): Pstmt =
   var q = dbFormat(query, args)
   if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
-  
-proc setRow(stmt: Pstmt, r: var TRow, cols: cint) =
+
+proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
   for col in 0..cols-1:
     setLen(r[col], column_bytes(stmt, col)) # set capacity
     setLen(r[col], 0)
     let x = column_text(stmt, col)
     if not isNil(x): add(r[col], x)
-  
-iterator fastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow  {.tags: [FReadDb].} =
-  ## executes the query and iterates over the result dataset. This is very 
-  ## fast, but potenially dangerous: If the for-loop-body executes another
-  ## query, the results can be undefined. For Sqlite it is safe though.
+
+iterator fastRows*(db: DbConn, query: SqlQuery,
+                   args: varargs[string, `$`]): Row  {.tags: [FReadDb].} =
+  ## Executes the query and iterates over the result dataset.
+  ##
+  ## This is very fast, but potentially dangerous.  Use this iterator only
+  ## if you require **ALL** the rows.
+  ##
+  ## Breaking the fastRows() iterator during a loop will cause the next
+  ## database query to raise an [EDb] exception ``unable to close due to ...``.
   var stmt = setupQuery(db, query, args)
   var L = (column_count(stmt))
   var result = newRow(L)
-  while step(stmt) == SQLITE_ROW: 
+  while step(stmt) == SQLITE_ROW:
     setRow(stmt, result, L)
     yield result
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
-proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow {.tags: [FReadDb].} =
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within the interator body.
+  var stmt = setupQuery(db, query, args)
+  while step(stmt) == SQLITE_ROW:
+    yield stmt
+  if finalize(stmt) != SQLITE_OK: dbError(db)
+
+proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
+  ## returns text for given column of the row
+  $column_text(row, col)
+
+proc len*(row: InstantRow): int32 {.inline.} =
+  ## returns number of columns in the row
+  column_count(row)
+
+proc getRow*(db: DbConn, query: SqlQuery,
+             args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a TRow with empty strings for each column.
+  ## will return a Row with empty strings for each column.
   var stmt = setupQuery(db, query, args)
   var L = (column_count(stmt))
   result = newRow(L)
-  if step(stmt) == SQLITE_ROW: 
+  if step(stmt) == SQLITE_ROW:
     setRow(stmt, result, L)
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
-proc getAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDb].} =
+proc getAllRows*(db: DbConn, query: SqlQuery,
+                 args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in fastRows(db, query, args):
     result.add(r)
 
-iterator rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow {.tags: [FReadDb].} =
+iterator rows*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
   ## same as `FastRows`, but slower and safe.
   for r in fastRows(db, query, args): yield r
 
-proc getValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string {.tags: [FReadDb].} = 
+proc getValue*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): string {.tags: [FReadDb].} =
   ## executes the query and returns the first column of the first row of the
   ## result dataset. Returns "" if the dataset contains no rows or the database
   ## value is NULL.
   var stmt = setupQuery(db, query, args)
   if step(stmt) == SQLITE_ROW:
     let cb = column_bytes(stmt, 0)
-    if cb == 0: 
+    if cb == 0:
       result = ""
     else:
       result = newStringOfCap(cb)
@@ -147,12 +202,12 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
   else:
     result = ""
   if finalize(stmt) != SQLITE_OK: dbError(db)
-  
-proc tryInsertID*(db: TDbConn, query: TSqlQuery, 
+
+proc tryInsertID*(db: DbConn, query: SqlQuery,
                   args: varargs[string, `$`]): int64
                   {.tags: [FWriteDb], raises: [].} =
-  ## executes the query (typically "INSERT") and returns the 
-  ## generated ID for the row or -1 in case of an error. 
+  ## executes the query (typically "INSERT") and returns the
+  ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   result = -1
@@ -162,50 +217,50 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
     if finalize(stmt) != SQLITE_OK:
       result = -1
 
-proc insertID*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = 
-  ## executes the query (typically "INSERT") and returns the 
+proc insertID*(db: DbConn, query: SqlQuery,
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
+  ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``. 
+  ## named ``id``.
   result = tryInsertID(db, query, args)
   if result < 0: dbError(db)
-  
-proc execAffectedRows*(db: TDbConn, query: TSqlQuery, 
+
+proc execAffectedRows*(db: DbConn, query: SqlQuery,
                        args: varargs[string, `$`]): int64 {.
-                       tags: [FReadDb, FWriteDb].} = 
+                       tags: [FReadDb, FWriteDb].} =
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   exec(db, query, args)
   result = changes(db)
 
-proc close*(db: TDbConn) {.tags: [FDb].} = 
+proc close*(db: DbConn) {.tags: [FDb].} =
   ## closes the database connection.
   if sqlite3.close(db) != SQLITE_OK: dbError(db)
-    
-proc open*(connection, user, password, database: string): TDbConn {.
+
+proc open*(connection, user, password, database: string): DbConn {.
   tags: [FDb].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established. Only the ``connection`` parameter is used for ``sqlite``.
-  var db: TDbConn
+  var db: DbConn
   if sqlite3.open(connection, db) == SQLITE_OK:
     result = db
   else:
     dbError(db)
 
-proc setEncoding*(connection: TDbConn, encoding: string): bool {.
+proc setEncoding*(connection: DbConn, encoding: string): bool {.
   tags: [FDb].} =
-  ## sets the encoding of a database connection, returns true for 
+  ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
   ##
   ## Note that the encoding cannot be changed once it's been set.
-  ## According to SQLite3 documentation, any attempt to change 
-  ## the encoding after the database is created will be silently 
+  ## According to SQLite3 documentation, any attempt to change
+  ## the encoding after the database is created will be silently
   ## ignored.
   exec(connection, sql"PRAGMA encoding = ?", [encoding])
   result = connection.getValue(sql"PRAGMA encoding") == encoding
 
-when isMainModule:
+when not defined(testing) and isMainModule:
   var db = open("db.sql", "", "", "")
   exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
   exec(db, sql"insert into tbl1 values('hello!',10)", [])
@@ -215,5 +270,7 @@ when isMainModule:
   #db.query("insert into tbl1 values('goodbye', 20)")
   for r in db.rows(sql"select * from tbl1", []):
     echo(r[0], r[1])
-  
+  for r in db.instantRows(sql"select * from tbl1", []):
+    echo(r[0], r[1])
+
   db_sqlite.close(db)
diff --git a/lib/impure/dialogs.nim b/lib/impure/dialogs.nim
deleted file mode 100644
index 4ea66a6e6..000000000
--- a/lib/impure/dialogs.nim
+++ /dev/null
@@ -1,226 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-
-## This module implements portable dialogs for Nim; the implementation
-## builds on the GTK interface. On Windows, native dialogs are shown instead.
-
-import
-  glib2, gtk2
-
-when defined(Windows):
-  import windows, ShellAPI, os
-
-proc info*(window: PWindow, msg: string) =
-  ## Shows an information message to the user. The process waits until the
-  ## user presses the OK button.
-  when defined(Windows):
-    discard MessageBoxA(0, msg, "Information", MB_OK or MB_ICONINFORMATION)
-  else:
-    var dialog = message_dialog_new(window,
-                DIALOG_MODAL or DIALOG_DESTROY_WITH_PARENT,
-                MESSAGE_INFO, BUTTONS_OK, "%s", cstring(msg))
-    setTitle(dialog, "Information")
-    discard run(dialog)
-    destroy(PWidget(dialog))
-
-proc warning*(window: PWindow, msg: string) =
-  ## Shows a warning message to the user. The process waits until the user
-  ## presses the OK button.
-  when defined(Windows):
-    discard MessageBoxA(0, msg, "Warning", MB_OK or MB_ICONWARNING)
-  else:
-    var dialog = DIALOG(message_dialog_new(window,
-                DIALOG_MODAL or DIALOG_DESTROY_WITH_PARENT,
-                MESSAGE_WARNING, BUTTONS_OK, "%s", cstring(msg)))
-    setTitle(dialog, "Warning")
-    discard run(dialog)
-    destroy(PWidget(dialog))
-
-proc error*(window: PWindow, msg: string) =
-  ## Shows an error message to the user. The process waits until the user
-  ## presses the OK button.
-  when defined(Windows):
-    discard MessageBoxA(0, msg, "Error", MB_OK or MB_ICONERROR)
-  else:
-    var dialog = DIALOG(message_dialog_new(window,
-                DIALOG_MODAL or DIALOG_DESTROY_WITH_PARENT,
-                MESSAGE_ERROR, BUTTONS_OK, "%s", cstring(msg)))
-    setTitle(dialog, "Error")
-    discard run(dialog)
-    destroy(PWidget(dialog))
-
-
-proc chooseFileToOpen*(window: PWindow, root: string = ""): string =
-  ## Opens a dialog that requests a filename from the user. Returns ""
-  ## if the user closed the dialog without selecting a file. On Windows,
-  ## the native dialog is used, else the GTK dialog is used.
-  when defined(Windows):
-    var
-      opf: TOPENFILENAME
-      buf: array [0..2047, char]
-    opf.lStructSize = sizeof(opf).int32
-    if root.len > 0:
-      opf.lpstrInitialDir = root
-    opf.lpstrFilter = "All Files\0*.*\0\0"
-    opf.flags = OFN_FILEMUSTEXIST
-    opf.lpstrFile = buf
-    opf.nMaxFile = sizeof(buf).int32
-    var res = GetOpenFileName(addr(opf))
-    if res != 0:
-      result = $buf
-    else:
-      result = ""
-  else:
-    var chooser = file_chooser_dialog_new("Open File", window,
-                FILE_CHOOSER_ACTION_OPEN, 
-                STOCK_CANCEL, RESPONSE_CANCEL,
-                STOCK_OPEN, RESPONSE_OK, nil)
-    if root.len > 0:
-      discard set_current_folder(chooser, root)
-    if run(chooser) == cint(RESPONSE_OK):
-      var x = get_filename(chooser)
-      result = $x
-      g_free(x)
-    else:
-      result = ""
-    destroy(PWidget(chooser))
-
-proc chooseFilesToOpen*(window: PWindow, root: string = ""): seq[string] =
-  ## Opens a dialog that requests filenames from the user. Returns ``@[]``
-  ## if the user closed the dialog without selecting a file. On Windows,
-  ## the native dialog is used, else the GTK dialog is used.
-  when defined(Windows):
-    var
-      opf: TOPENFILENAME
-      buf: array [0..2047*4, char]
-    opf.lStructSize = sizeof(opf).int32
-    if root.len > 0:
-      opf.lpstrInitialDir = root
-    opf.lpstrFilter = "All Files\0*.*\0\0"
-    opf.flags = OFN_FILEMUSTEXIST or OFN_ALLOWMULTISELECT or OFN_EXPLORER
-    opf.lpstrFile = buf
-    opf.nMaxFile = sizeof(buf).int32
-    var res = GetOpenFileName(addr(opf))
-    result = @[]
-    if res != 0:
-      # parsing the result is horrible:
-      var
-        i = 0
-        s: string
-        path = ""
-      while buf[i] != '\0':
-        add(path, buf[i])
-        inc(i)
-      inc(i)
-      if buf[i] != '\0':
-        while true:
-          s = ""
-          while buf[i] != '\0':
-            add(s, buf[i])
-            inc(i)
-          add(result, s)
-          inc(i)
-          if buf[i] == '\0': break
-        for i in 0..result.len-1: result[i] = os.joinPath(path, result[i])
-      else:
-        # only one file selected --> gosh, what an ungly thing 
-        # the windows API is
-        add(result, path) 
-  else:
-    var chooser = file_chooser_dialog_new("Open Files", window,
-                FILE_CHOOSER_ACTION_OPEN,
-                STOCK_CANCEL, RESPONSE_CANCEL,
-                STOCK_OPEN, RESPONSE_OK, nil)
-    if root.len > 0:
-      discard set_current_folder(chooser, root)
-    set_select_multiple(chooser, true)
-    result = @[]
-    if run(chooser) == cint(RESPONSE_OK):
-      var L = get_filenames(chooser)
-      var it = L
-      while it != nil:
-        add(result, $cast[cstring](it.data))
-        g_free(it.data)
-        it = it.next
-      free(L)
-    destroy(PWidget(chooser))
-
-
-proc chooseFileToSave*(window: PWindow, root: string = ""): string =
-  ## Opens a dialog that requests a filename to save to from the user.
-  ## Returns "" if the user closed the dialog without selecting a file.
-  ## On Windows, the native dialog is used, else the GTK dialog is used.
-  when defined(Windows):
-    var
-      opf: TOPENFILENAME
-      buf: array [0..2047, char]
-    opf.lStructSize = sizeof(opf).int32
-    if root.len > 0:
-      opf.lpstrInitialDir = root
-    opf.lpstrFilter = "All Files\0*.*\0\0"
-    opf.flags = OFN_OVERWRITEPROMPT
-    opf.lpstrFile = buf
-    opf.nMaxFile = sizeof(buf).int32
-    var res = GetSaveFileName(addr(opf))
-    if res != 0:
-      result = $buf
-    else:
-      result = ""
-  else:
-    var chooser = file_chooser_dialog_new("Save File", window,
-                FILE_CHOOSER_ACTION_SAVE,
-                STOCK_CANCEL, RESPONSE_CANCEL,
-                STOCK_SAVE, RESPONSE_OK, nil)
-    if root.len > 0:
-      discard set_current_folder(chooser, root)
-    set_do_overwrite_confirmation(chooser, true)
-    if run(chooser) == cint(RESPONSE_OK):
-      var x = get_filename(chooser)
-      result = $x
-      g_free(x)
-    else:
-      result = ""
-    destroy(PWidget(chooser))
-
-
-proc chooseDir*(window: PWindow, root: string = ""): string =
-  ## Opens a dialog that requests a directory from the user.
-  ## Returns "" if the user closed the dialog without selecting a directory.
-  ## On Windows, the native dialog is used, else the GTK dialog is used.
-  when defined(Windows):
-    var
-      lpItemID: PItemIDList
-      BrowseInfo: TBrowseInfo
-      DisplayName: array [0..MAX_PATH, char]
-      TempPath: array [0..MAX_PATH, char]
-    result = ""
-    #BrowseInfo.hwndOwner = Application.Handle
-    BrowseInfo.pszDisplayName = DisplayName
-    BrowseInfo.ulFlags = 1 #BIF_RETURNONLYFSDIRS
-    lpItemID = SHBrowseForFolder(cast[LPBrowseInfo](addr(BrowseInfo)))
-    if lpItemId != nil:
-      discard SHGetPathFromIDList(lpItemID, TempPath)
-      result = $TempPath
-      discard GlobalFreePtr(lpItemID)
-  else:
-    var chooser = file_chooser_dialog_new("Select Directory", window,
-                FILE_CHOOSER_ACTION_SELECT_FOLDER,
-                STOCK_CANCEL, RESPONSE_CANCEL,
-                STOCK_OPEN, RESPONSE_OK, nil)
-    if root.len > 0:
-      discard set_current_folder(chooser, root)
-    if run(chooser) == cint(RESPONSE_OK):
-      var x = get_filename(chooser)
-      result = $x
-      g_free(x)
-    else:
-      result = ""
-    destroy(PWidget(chooser))
-
diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim
deleted file mode 100644
index dfadb46ee..000000000
--- a/lib/impure/graphics.nim
+++ /dev/null
@@ -1,575 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements graphical output for Nim; the current
-## implementation uses SDL but the interface is meant to support multiple
-## backends some day. There is no need to init SDL as this module does that 
-## implicitly.
-
-import colors, math
-from sdl import PSurface # Bug
-from sdl_ttf import openFont, closeFont
-
-type
-  TRect* = tuple[x, y, width, height: int]
-  TPoint* = tuple[x, y: int]
-
-  PSurface* = ref TSurface ## a surface to draw onto
-  TSurface* {.pure, final.} = object
-    w*, h*: Natural
-    s*: sdl.PSurface
-  
-  EGraphics* = object of IOError
-
-  TFont {.pure, final.} = object
-    f: sdl_ttf.PFont
-    color: sdl.TColor
-  PFont* = ref TFont ## represents a font
-
-proc toSdlColor*(c: Color): sdl.TColor =
-  ## Convert colors.TColor to sdl.TColor
-  var x = c.extractRGB  
-  result.r = x.r and 0xff
-  result.g = x.g and 0xff
-  result.b = x.b and 0xff
-
-proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
-  ## Creates a color using ``sdl.MapRGBA``.
-  var x = c.extractRGB
-  return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, 
-                     x.b and 0xff, alpha and 0xff)
-
-proc toSdlRect*(r: TRect): sdl.TRect =
-  ## Convert ``graphics.TRect`` to ``sdl.TRect``.
-  result.x = int16(r.x)
-  result.y = int16(r.y)
-  result.w = uint16(r.width)
-  result.h = uint16(r.height)
-
-proc raiseEGraphics = 
-  raise newException(EGraphics, $sdl.getError())
-  
-proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
-  
-proc newSurface*(width, height: int): PSurface =
-  ## creates a new surface.
-  new(result, surfaceFinalizer)
-  result.w = width
-  result.h = height
-  result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, 
-      32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
-  if result.s == nil:
-    raiseEGraphics()
-  
-  assert(not sdl.mustLock(result.s))
-
-proc fontFinalizer(f: PFont) = closeFont(f.f)
-
-proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =  
-  ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
-  new(result, fontFinalizer)
-  result.f = openFont(name, size.cint)
-  if result.f == nil:
-    raise newException(IOError, "Could not open font file: " & name)
-  result.color = toSdlColor(color)
-
-var
-  defaultFont*: PFont ## default font that is used; this needs to initialized
-                      ## by the client!
-
-proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) = 
-  ## initializes the `defaultFont` var.
-  defaultFont = newFont(name, size, color)
-
-proc newScreenSurface*(width, height: int): PSurface =
-  ## Creates a new screen surface
-  new(result, surfaceFinalizer)
-  result.w = width
-  result.h = height
-  result.s = sdl.setVideoMode(width, height, 0, 0)
-  if result.s == nil:
-    raiseEGraphics()
-
-proc writeToBMP*(sur: PSurface, filename: string) =
-  ## Saves the contents of the surface `sur` to the file `filename` as a 
-  ## BMP file.
-  if sdl.saveBMP(sur.s, filename) != 0:
-    raise newException(IOError, "cannot write: " & filename)
-
-type
-  TPixels = array[0..1000_000-1, int32]
-  PPixels = ptr TPixels
-
-template setPix(video, pitch, x, y, col: expr): stmt =
-  video[y * pitch + x] = int32(col)
-
-template getPix(video, pitch, x, y: expr): expr = 
-  colors.Color(video[y * pitch + x])
-
-const
-  ColSize = 4
-
-proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
-  assert x <% sur.w
-  assert y <% sur.h
-  result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize, 
-                  x, y)
-
-proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
-  assert x <% sur.w
-  assert y <% sur.h
-  var pixs = cast[PPixels](sur.s.pixels)
-  #pixs[y * (sur.s.pitch div colSize) + x] = int(col)
-  setPix(pixs, sur.s.pitch.int div ColSize, x, y, col)
-
-proc `[]`*(sur: PSurface, p: TPoint): Color =
-  ## get pixel at position `p`. No range checking is done!
-  result = getPixel(sur, p.x, p.y)
-
-proc `[]`*(sur: PSurface, x, y: int): Color =
-  ## get pixel at position ``(x, y)``. No range checking is done!
-  result = getPixel(sur, x, y)
-
-proc `[]=`*(sur: PSurface, p: TPoint, col: Color) =
-  ## set the pixel at position `p`. No range checking is done!
-  setPixel(sur, p.x, p.y, col)
-
-proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
-  ## set the pixel at position ``(x, y)``. No range checking is done!
-  setPixel(sur, x, y, col)
-
-proc blit*(destSurf: PSurface, destRect: TRect, srcSurf: PSurface, 
-           srcRect: TRect) =
-  ## Copies ``srcSurf`` into ``destSurf``
-  var destTRect, srcTRect: sdl.TRect
-
-  destTRect.x = int16(destRect.x)
-  destTRect.y = int16(destRect.y)
-  destTRect.w = uint16(destRect.width)
-  destTRect.h = uint16(destRect.height)
-
-  srcTRect.x = int16(srcRect.x)
-  srcTRect.y = int16(srcRect.y)
-  srcTRect.w = uint16(srcRect.width)
-  srcTRect.h = uint16(srcRect.height)
-
-  if sdl.blitSurface(srcSurf.s, addr(srcTRect), destSurf.s, addr(destTRect)) != 0:
-    raiseEGraphics()
-
-proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] =
-  var w, h: cint
-  if sdl_ttf.sizeUTF8(font.f, text, w, h) < 0: raiseEGraphics()
-  result.width = int(w)
-  result.height = int(h)
-
-proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
-  ## Draws text with a transparent background, at location ``p`` with the given
-  ## font.
-  var textSur: PSurface # This surface will have the text drawn on it
-  new(textSur, surfaceFinalizer)
-  
-  # Render the text
-  textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
-  # Merge the text surface with sur
-  sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
-
-proc drawText*(sur: PSurface, p: TPoint, text: string,
-               bg: Color, font = defaultFont) =
-  ## Draws text, at location ``p`` with font ``font``. ``bg`` 
-  ## is the background color.
-  var textSur: PSurface # This surface will have the text drawn on it
-  new(textSur, surfaceFinalizer)
-  textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
-  # Merge the text surface with sur
-  sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
-  
-proc drawCircle*(sur: PSurface, p: TPoint, r: Natural, color: Color) =
-  ## draws a circle with center `p` and radius `r` with the given color
-  ## onto the surface `sur`.
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-  var a = 1 - r
-  var py = r
-  var px = 0
-  var x = p.x
-  var y = p.y
-  while px <= py + 1:
-    if x+px <% sur.w:
-      if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
-      if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
-    
-    if x-px <% sur.w:
-      if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
-      if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
-
-    if x+py <% sur.w:
-      if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
-      if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
-      
-    if x-py <% sur.w:
-      if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
-      if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
-
-    if a < 0:
-      a = a + (2 * px + 3)
-    else:
-      a = a + (2 * (px - py) + 5)
-      py = py - 1
-    px = px + 1
-
-proc `>-<`(val: int, s: PSurface): int {.inline.} = 
-  return if val < 0: 0 elif val >= s.w: s.w-1 else: val
-
-proc `>|<`(val: int, s: PSurface): int {.inline.} = 
-  return if val < 0: 0 elif val >= s.h: s.h-1 else: val
-
-proc drawLine*(sur: PSurface, p1, p2: TPoint, color: Color) =
-  ## draws a line between the two points `p1` and `p2` with the given color
-  ## onto the surface `sur`.
-  var stepx, stepy: int = 0
-  var x0 = p1.x >-< sur
-  var x1 = p2.x >-< sur
-  var y0 = p1.y >|< sur
-  var y1 = p2.y >|< sur
-  var dy = y1 - y0
-  var dx = x1 - x0
-  if dy < 0:
-    dy = -dy 
-    stepy = -1
-  else:
-    stepy = 1
-  if dx < 0:
-    dx = -dx
-    stepx = -1
-  else:
-    stepx = 1
-  dy = dy * 2 
-  dx = dx * 2
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-  setPix(video, pitch, x0, y0, color)
-  if dx > dy:
-    var fraction = dy - (dx div 2)
-    while x0 != x1:
-      if fraction >= 0:
-        y0 = y0 + stepy
-        fraction = fraction - dx
-      x0 = x0 + stepx
-      fraction = fraction + dy
-      setPix(video, pitch, x0, y0, color)
-  else:
-    var fraction = dx - (dy div 2)
-    while y0 != y1:
-      if fraction >= 0:
-        x0 = x0 + stepx
-        fraction = fraction - dy
-      y0 = y0 + stepy
-      fraction = fraction + dx
-      setPix(video, pitch, x0, y0, color)
-
-proc drawHorLine*(sur: PSurface, x, y, w: Natural, color: Color) =
-  ## draws a horizontal line from (x,y) to (x+w-1, y).
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-
-  if y >= 0 and y <= sur.s.h:
-    for i in 0 .. min(sur.s.w-x, w)-1:
-      setPix(video, pitch, x + i, y, color)
-
-proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) =
-  ## draws a vertical line from (x,y) to (x, y+h-1).
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-
-  if x >= 0 and x <= sur.s.w:
-    for i in 0 .. min(sur.s.h-y, h)-1:
-      setPix(video, pitch, x, y + i, color)
-
-proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
-  ## draws a circle with center `p` and radius `r` with the given color
-  ## onto the surface `sur` and fills it.
-  var a = 1 - r
-  var py: int = r
-  var px = 0
-  var x = p.x
-  var y = p.y
-  while px <= py:
-    # Fill up the middle half of the circle
-    drawVerLine(s, x + px, y, py + 1, color)
-    drawVerLine(s, x + px, y - py, py, color)
-    if px != 0:
-      drawVerLine(s, x - px, y, py + 1, color)
-      drawVerLine(s, x - px, y - py, py, color)
-    if a < 0:
-      a = a + (2 * px + 3)
-    else:
-      a = a + (2 * (px - py) + 5)
-      py = py - 1
-      # Fill up the left/right half of the circle
-      if py >= px:
-        drawVerLine(s, x + py + 1, y, px + 1, color)
-        drawVerLine(s, x + py + 1, y - px, px, color)
-        drawVerLine(s, x - py - 1, y, px + 1, color)
-        drawVerLine(s, x - py - 1, y - px,  px, color)
-    px = px + 1
-
-proc drawRect*(sur: PSurface, r: TRect, color: Color) =
-  ## draws a rectangle.
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-  if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
-    var minW = min(sur.s.w - r.x, r.width)
-    var minH = min(sur.s.h - r.y, r.height)
-    
-    # Draw Top
-    for i in 0 .. minW - 1:
-      setPix(video, pitch, r.x + i, r.y, color)
-      setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
-      
-    # Draw left side
-    for i in 0 .. minH - 1:
-      setPix(video, pitch, r.x, r.y + i, color)
-      setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
-    
-proc fillRect*(sur: PSurface, r: TRect, col: Color) =
-  ## Fills a rectangle using sdl's ``FillRect`` function.
-  var rect = toSdlRect(r)
-  if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1:
-    raiseEGraphics()
-
-proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
-  var video = cast[PPixels](sur.s.pixels)
-  var pitch = sur.s.pitch.int div ColSize
-  if cx+x <= sur.s.w-1:
-    if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
-    if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)    
-  if cx-x <= sur.s.w-1:
-    if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
-    if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
-
-proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural, 
-                  col: Color) =
-  ## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the 
-  ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
-  ## of the ellipse.
-  var 
-    x, y: Natural
-    xChange, yChange: int
-    ellipseError: Natural
-    twoASquare, twoBSquare: Natural
-    stoppingX, stoppingY: Natural
-    
-  twoASquare = 2 * xRadius * xRadius
-  twoBSquare = 2 * yRadius * yRadius
-  x = xRadius
-  y = 0
-  xChange = yRadius * yRadius * (1 - 2 * xRadius)
-  yChange = xRadius * xRadius
-  ellipseError = 0
-  stoppingX = twoBSquare * xRadius
-  stoppingY = 0
-  
-  while stoppingX >=  stoppingY: # 1st set of points, y` > - 1
-    sur.plot4EllipsePoints(cx, cy, x, y, col)
-    inc(y)
-    inc(stoppingY, twoASquare)
-    inc(ellipseError, yChange)
-    inc(yChange, twoASquare)
-    if (2 * ellipseError + xChange) > 0 :
-      dec(x)
-      dec(stoppingX, twoBSquare)
-      inc(ellipseError, xChange)
-      inc(xChange, twoBSquare)
-      
-  # 1st point set is done; start the 2nd set of points
-  x = 0
-  y = yRadius
-  xChange = yRadius * yRadius
-  yChange = xRadius * xRadius * (1 - 2 * yRadius)
-  ellipseError = 0
-  stoppingX = 0
-  stoppingY = twoASquare * yRadius
-  while stoppingX <= stoppingY:
-    sur.plot4EllipsePoints(cx, cy, x, y, col)
-    inc(x)
-    inc(stoppingX, twoBSquare)
-    inc(ellipseError, xChange)
-    inc(xChange,twoBSquare)
-    if (2 * ellipseError + yChange) > 0:
-      dec(y)
-      dec(stoppingY, twoASquare)
-      inc(ellipseError, yChange)
-      inc(yChange,twoASquare)
-  
-
-proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
-  if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
-    var video = cast[PPixels](sur.s.pixels)
-    var pitch = sur.s.pitch.int div ColSize
-
-    var pixColor = getPix(video, pitch, x, y)
-
-    setPix(video, pitch, x, y,
-           pixColor.intensity(1.0 - c) + color.intensity(c))
- 
-
-template ipart(x: expr): expr = floor(x) 
-template cround(x: expr): expr = ipart(x + 0.5)
-template fpart(x: expr): expr = x - ipart(x)
-template rfpart(x: expr): expr = 1.0 - fpart(x)
-
-proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: Color) =
-  ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's 
-  ## line algorithm
-  var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), 
-                          p1.y.toFloat(), p2.y.toFloat())
-  var dx = x2 - x1
-  var dy = y2 - y1
-  
-  var ax = dx
-  if ax < 0'f64:
-    ax = 0'f64 - ax
-  var ay = dy
-  if ay < 0'f64:
-    ay = 0'f64 - ay
-  
-  if ax < ay:
-    swap(x1, y1)
-    swap(x2, y2)
-    swap(dx, dy)
-  
-  template doPlot(x, y: int, c: float, color: Color): stmt =
-    if ax < ay:
-      sur.plotAA(y, x, c, color)
-    else:
-      sur.plotAA(x, y, c, color)
-  
-  if x2 < x1:
-    swap(x1, x2)
-    swap(y1, y2)
-  
-  var gradient = dy / dx
-  # handle first endpoint
-  var xend = cround(x1)
-  var yend = y1 + gradient * (xend - x1)
-  var xgap = rfpart(x1 + 0.5)
-  var xpxl1 = int(xend) # this will be used in the main loop
-  var ypxl1 = int(ipart(yend))
-  doPlot(xpxl1, ypxl1, rfpart(yend)*xgap, color)
-  doPlot(xpxl1, ypxl1 + 1, fpart(yend)*xgap, color)
-  var intery = yend + gradient # first y-intersection for the main loop
-
-  # handle second endpoint
-  xend = cround(x2)
-  yend = y2 + gradient * (xend - x2)
-  xgap = fpart(x2 + 0.5)
-  var xpxl2 = int(xend) # this will be used in the main loop
-  var ypxl2 = int(ipart(yend))
-  doPlot(xpxl2, ypxl2, rfpart(yend) * xgap, color)
-  doPlot(xpxl2, ypxl2 + 1, fpart(yend) * xgap, color)
-
-  # main loop
-  var x = xpxl1 + 1
-  while x <= xpxl2-1:
-    doPlot(x, int(ipart(intery)), rfpart(intery), color)
-    doPlot(x, int(ipart(intery)) + 1, fpart(intery), color)
-    intery = intery + gradient
-    inc(x)
-
-proc fillSurface*(sur: PSurface, color: Color) =
-  ## Fills the entire surface with ``color``.
-  if sdl.fillRect(sur.s, nil, sur.createSdlColor(color)) == -1:
-    raiseEGraphics()
-
-template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
-  immediate.} =
-  ## Simple template which creates an event loop. ``Event`` is the name of the
-  ## variable containing the TEvent object.
-  while true:
-    var event: sdl.TEvent
-    if sdl.waitEvent(addr(event)) == 1:
-      actions
-
-if sdl.init(sdl.INIT_VIDEO) < 0: raiseEGraphics()
-if sdl_ttf.init() < 0: raiseEGraphics()
-
-when isMainModule:
-  var surf = newScreenSurface(800, 600)
-
-  surf.fillSurface(colWhite)
-
-  # Draw the shapes
-  surf.drawLineAA((150, 170), (400, 471), colTan)
-  surf.drawLine((100, 170), (400, 471), colRed)
-  
-  surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
-  surf.drawHorLine(1, 300, 400, colViolet) 
-  # Check if the ellipse is the size it's suppose to be.
-  surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
-  
-  surf.drawEllipse(400, 300, 300, 300, colOrange)
-  surf.drawEllipse(5, 5, 5, 5, colGreen)
-  
-  surf.drawHorLine(5, 5, 900, colRed)
-  surf.drawVerLine(5, 60, 800, colRed)
-  surf.drawCircle((600, 500), 60, colRed)
-  
-  surf.fillRect((50, 50, 100, 100), colFuchsia)
-  surf.fillRect((150, 50, 100, 100), colGreen)
-  surf.drawRect((50, 150, 100, 100), colGreen)
-  surf.drawRect((150, 150, 100, 100), colAqua)
-  surf.drawRect((250, 150, 100, 100), colBlue)
-  surf.drawHorLine(250, 150, 100, colRed)
-
-  surf.drawLineAA((592, 160), (592, 280), colPurple)
-  
-  #surf.drawText((300, 300), "TEST", colMidnightBlue)
-  #var textSize = textBounds("TEST")
-  #surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
-  #  $textSize.height, colDarkGreen)
-  
-  var mouseStartX = -1
-  var mouseStartY = -1
-  withEvents(surf, event):
-    var eventp = addr(event)
-    case event.kind:
-    of sdl.QUITEV:
-      break
-    of sdl.KEYDOWN:
-      var evk = sdl.evKeyboard(eventp)
-      if evk.keysym.sym == sdl.K_LEFT:
-        surf.drawHorLine(395, 300, 50, colBlack)
-        echo("Drawing")
-      elif evk.keysym.sym == sdl.K_ESCAPE:
-        break
-      else:
-        echo(evk.keysym.sym)
-    of sdl.MOUSEBUTTONDOWN:
-      var mbd = sdl.evMouseButton(eventp)
-      if mouseStartX == -1 or mouseStartY == -1:
-        mouseStartX = int(mbd.x)
-        mouseStartY = int(mbd.y)
-      else:
-        surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
-        mouseStartX = -1
-        mouseStartY = -1
-        
-    of sdl.MOUSEMOTION:
-      var mm = sdl.evMouseMotion(eventp)
-      if mouseStartX != -1 and mouseStartY != -1:
-        surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
-      #echo(mm.x, " ", mm.y, " ", mm.yrel)
-    
-    else:
-      discard "echo(event.kind)"
-      
-    sdl.updateRect(surf.s, 0, 0, 800, 600)
-    
-  surf.writeToBMP("test.bmp")
-  sdl.quit()
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
new file mode 100644
index 000000000..973f1f2ee
--- /dev/null
+++ b/lib/impure/nre.nim
@@ -0,0 +1,686 @@
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Nim Contributers
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+from pcre import nil
+import nre.private.util
+import tables
+import unsigned
+from strutils import toLower, `%`
+from math import ceil
+import options
+from unicode import runeLenAt
+
+
+## What is NRE?
+## ============
+##
+## A regular expression library for Nim using PCRE to do the hard work.
+##
+## Licencing
+## ---------
+##
+## PCRE has some additional terms that you must comply with if you use this module.::
+##
+## > Copyright (c) 1997-2001 University of Cambridge
+## >
+## > Permission is granted to anyone to use this software for any purpose on any
+## > computer system, and to redistribute it freely, subject to the following
+## > restrictions:
+## >
+## > 1. This software is distributed in the hope that it will be useful,
+## >    but WITHOUT ANY WARRANTY; without even the implied warranty of
+## >    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+## >
+## > 2. The origin of this software must not be misrepresented, either by
+## >    explicit claim or by omission. In practice, this means that if you use
+## >    PCRE in software that you distribute to others, commercially or
+## >    otherwise, you must put a sentence like this
+## >
+## >      Regular expression support is provided by the PCRE library package,
+## >      which is open source software, written by Philip Hazel, and copyright
+## >      by the University of Cambridge, England.
+## >
+## >    somewhere reasonably visible in your documentation and in any relevant
+## >    files or online help data or similar. A reference to the ftp site for
+## >    the source, that is, to
+## >
+## >      ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
+## >
+## >    should also be given in the documentation. However, this condition is not
+## >    intended to apply to whole chains of software. If package A includes PCRE,
+## >    it must acknowledge it, but if package B is software that includes package
+## >    A, the condition is not imposed on package B (unless it uses PCRE
+## >    independently).
+## >
+## > 3. Altered versions must be plainly marked as such, and must not be
+## >    misrepresented as being the original software.
+## >
+## > 4. If PCRE is embedded in any software that is released under the GNU
+## >    General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL),
+## >    then the terms of that licence shall supersede any condition above with
+## >    which it is incompatible.
+
+
+# Type definitions {{{
+type
+  Regex* = ref object
+    ## Represents the pattern that things are matched against, constructed with
+    ## ``re(string)``. Examples: ``re"foo"``, ``re(r"(*ANYCRLF)(?x)foo #
+    ## comment".``
+    ##
+    ## ``pattern: string``
+    ##     the string that was used to create the pattern.
+    ##
+    ## ``captureCount: int``
+    ##     the number of captures that the pattern has.
+    ##
+    ## ``captureNameId: Table[string, int]``
+    ##     a table from the capture names to their numeric id.
+    ##
+    ##
+    ## Options
+    ## .......
+    ##
+    ## The following options may appear anywhere in the pattern, and they affect
+    ## the rest of it.
+    ##
+    ## -  ``(?i)`` - case insensitive
+    ## -  ``(?m)`` - multi-line: ``^`` and ``$`` match the beginning and end of
+    ##    lines, not of the subject string
+    ## -  ``(?s)`` - ``.`` also matches newline (*dotall*)
+    ## -  ``(?U)`` - expressions are not greedy by default. ``?`` can be added
+    ##    to a qualifier to make it greedy
+    ## -  ``(?x)`` - whitespace and comments (``#``) are ignored (*extended*)
+    ## -  ``(?X)`` - character escapes without special meaning (``\w`` vs.
+    ##    ``\a``) are errors (*extra*)
+    ##
+    ## One or a combination of these options may appear only at the beginning
+    ## of the pattern:
+    ##
+    ## -  ``(*UTF8)`` - treat both the pattern and subject as UTF-8
+    ## -  ``(*UCP)`` - Unicode character properties; ``\w`` matches ``я``
+    ## -  ``(*U)`` - a combination of the two options above
+    ## -  ``(*FIRSTLINE*)`` - fails if there is not a match on the first line
+    ## -  ``(*NO_AUTO_CAPTURE)`` - turn off auto-capture for groups;
+    ##    ``(?<name>...)`` can be used to capture
+    ## -  ``(*CR)`` - newlines are separated by ``\r``
+    ## -  ``(*LF)`` - newlines are separated by ``\n`` (UNIX default)
+    ## -  ``(*CRLF)`` - newlines are separated by ``\r\n`` (Windows default)
+    ## -  ``(*ANYCRLF)`` - newlines are separated by any of the above
+    ## -  ``(*ANY)`` - newlines are separated by any of the above and Unicode
+    ##    newlines:
+    ##
+    ##     single characters VT (vertical tab, U+000B), FF (form feed, U+000C),
+    ##     NEL (next line, U+0085), LS (line separator, U+2028), and PS
+    ##     (paragraph separator, U+2029). For the 8-bit library, the last two
+    ##     are recognized only in UTF-8 mode.
+    ##     —  man pcre
+    ##
+    ## -  ``(*JAVASCRIPT_COMPAT)`` - JavaScript compatibility
+    ## -  ``(*NO_STUDY)`` - turn off studying; study is enabled by default
+    ##
+    ## For more details on the leading option groups, see the `Option
+    ## Setting <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#OPTION_SETTING>`__
+    ## and the `Newline
+    ## Convention <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#NEWLINE_CONVENTION>`__
+    ## sections of the `PCRE syntax
+    ## manual <http://man7.org/linux/man-pages/man3/pcresyntax.3.html>`__.
+    pattern*: string  ## not nil
+    pcreObj: ptr pcre.Pcre  ## not nil
+    pcreExtra: ptr pcre.ExtraData  ## nil
+
+    captureNameToId: Table[string, int]
+
+  RegexMatch* = object
+    ## Usually seen as Option[RegexMatch], it represents the result of an
+    ## execution. On failure, it is none, on success, it is some.
+    ##
+    ## ``pattern: Regex``
+    ##     the pattern that is being matched
+    ##
+    ## ``str: string``
+    ##     the string that was matched against
+    ##
+    ## ``captures[]: string``
+    ##     the string value of whatever was captured at that id. If the value
+    ##     is invalid, then behavior is undefined. If the id is ``-1``, then
+    ##     the whole match is returned. If the given capture was not matched,
+    ##     ``nil`` is returned.
+    ##
+    ##     -  ``"abc".match(re"(\w)").captures[0] == "a"``
+    ##     -  ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"``
+    ##     -  ``"abc".match(re"(\w)\w").captures[-1] == "ab"``
+    ##
+    ## ``captureBounds[]: Option[Slice[int]]``
+    ##     gets the bounds of the given capture according to the same rules as
+    ##     the above. If the capture is not filled, then ``None`` is returned.
+    ##     The bounds are both inclusive.
+    ##
+    ##     -  ``"abc".match(re"(\w)").captureBounds[0] == 0 .. 0``
+    ##     -  ``"abc".match(re"").captureBounds[-1] == 0 .. -1``
+    ##     -  ``"abc".match(re"abc").captureBounds[-1] == 0 .. 2``
+    ##
+    ## ``match: string``
+    ##     the full text of the match.
+    ##
+    ## ``matchBounds: Slice[int]``
+    ##     the bounds of the match, as in ``captureBounds[]``
+    ##
+    ## ``(captureBounds|captures).toTable``
+    ##     returns a table with each named capture as a key.
+    ##
+    ## ``(captureBounds|captures).toSeq``
+    ##     returns all the captures by their number.
+    ##
+    ## ``$: string``
+    ##     same as ``match``
+    pattern*: Regex  ## The regex doing the matching.
+                     ## Not nil.
+    str*: string  ## The string that was matched against.
+                  ## Not nil.
+    pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match
+                                      ## Other items are the captures
+                                      ## `a` is inclusive start, `b` is exclusive end
+
+  Captures* = distinct RegexMatch
+  CaptureBounds* = distinct RegexMatch
+
+  RegexError* = ref object of Exception
+
+  RegexInternalError* = ref object of RegexError
+    ## Internal error in the module, this probably means that there is a bug
+
+  InvalidUnicodeError* = ref object of RegexError
+    ## Thrown when matching fails due to invalid unicode in strings
+    pos*: int  ## the location of the invalid unicode in bytes
+
+  SyntaxError* = ref object of RegexError
+    ## Thrown when there is a syntax error in the
+    ## regular expression string passed in
+    pos*: int  ## the location of the syntax error in bytes
+    pattern*: string  ## the pattern that caused the problem
+
+  StudyError* = ref object of RegexError
+    ## Thrown when studying the regular expression failes
+    ## for whatever reason. The message contains the error
+    ## code.
+# }}}
+
+proc getinfo[T](pattern: Regex, opt: cint): T =
+  let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
+
+  if retcode < 0:
+    # XXX Error message that doesn't expose implementation details
+    raise newException(FieldError, "Invalid getinfo for $1, errno $2" % [$opt, $retcode])
+
+# Regex accessors {{{
+proc captureCount*(pattern: Regex): int =
+  return getinfo[cint](pattern, pcre.INFO_CAPTURECOUNT)
+
+proc captureNameId*(pattern: Regex): Table[string, int] =
+  return pattern.captureNameToId
+
+proc matchesCrLf(pattern: Regex): bool =
+  let flags = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS))
+  let newlineFlags = flags and (pcre.NEWLINE_CRLF or
+                                pcre.NEWLINE_ANY or
+                                pcre.NEWLINE_ANYCRLF)
+  if newLineFlags > 0u32:
+    return true
+
+  # get flags from build config
+  var confFlags: cint
+  if pcre.config(pcre.CONFIG_NEWLINE, addr confFlags) != 0:
+    assert(false, "CONFIG_NEWLINE apparently got screwed up")
+
+  case confFlags
+  of 13: return false
+  of 10: return false
+  of (13 shl 8) or 10: return true
+  of -2: return true
+  of -1: return true
+  else: return false
+# }}}
+
+# Capture accessors {{{
+proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
+
+proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
+
+proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] =
+  let pattern = RegexMatch(pattern)
+  if pattern.pcreMatchBounds[i + 1].a != -1:
+    let bounds = pattern.pcreMatchBounds[i + 1]
+    return some(int(bounds.a) .. int(bounds.b-1))
+  else:
+    return none(Slice[int])
+
+proc `[]`*(pattern: Captures, i: int): string =
+  let pattern = RegexMatch(pattern)
+  let bounds = pattern.captureBounds[i]
+
+  if bounds.isSome:
+    let bounds = bounds.get
+    return pattern.str.substr(bounds.a, bounds.b)
+  else:
+    return nil
+
+proc match*(pattern: RegexMatch): string =
+  return pattern.captures[-1]
+
+proc matchBounds*(pattern: RegexMatch): Slice[int] =
+  return pattern.captureBounds[-1].get
+
+proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] =
+  let pattern = RegexMatch(pattern)
+  return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
+
+proc `[]`*(pattern: Captures, name: string): string =
+  let pattern = RegexMatch(pattern)
+  return pattern.captures[pattern.pattern.captureNameToId.fget(name)]
+
+template toTableImpl(cond: bool): stmt {.immediate, dirty.} =
+  for key in RegexMatch(pattern).pattern.captureNameId.keys:
+    let nextVal = pattern[key]
+    if cond:
+      result[key] = default
+    else:
+      result[key] = nextVal
+
+proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
+  result = initTable[string, string]()
+  toTableImpl(nextVal == nil)
+
+proc toTable*(pattern: CaptureBounds, default = none(Slice[int])):
+    Table[string, Option[Slice[int]]] =
+  result = initTable[string, Option[Slice[int]]]()
+  toTableImpl(nextVal.isNone)
+
+template itemsImpl(cond: bool): stmt {.immediate, dirty.} =
+  for i in 0 .. <RegexMatch(pattern).pattern.captureCount:
+    let nextVal = pattern[i]
+    # done in this roundabout way to avoid multiple yields (potential code
+    # bloat)
+    let nextYieldVal = if cond: default else: nextVal
+    yield nextYieldVal
+
+
+iterator items*(pattern: CaptureBounds, default = none(Slice[int])): Option[Slice[int]] =
+  itemsImpl(nextVal.isNone)
+
+iterator items*(pattern: Captures, default: string = nil): string =
+  itemsImpl(nextVal == nil)
+
+proc toSeq*(pattern: CaptureBounds, default = none(Slice[int])): seq[Option[Slice[int]]] =
+  accumulateResult(pattern.items(default))
+
+proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
+  accumulateResult(pattern.items(default))
+
+proc `$`*(pattern: RegexMatch): string =
+  return pattern.captures[-1]
+
+proc `==`*(a, b: Regex): bool =
+  if not a.isNil and not b.isNil:
+    return a.pattern   == b.pattern and
+           a.pcreObj   == b.pcreObj and
+           a.pcreExtra == b.pcreExtra
+  else:
+    return system.`==`(a, b)
+
+proc `==`*(a, b: RegexMatch): bool =
+  return a.pattern == b.pattern and
+         a.str     == b.str
+# }}}
+
+# Creation & Destruction {{{
+# PCRE Options {{{
+const PcreOptions = {
+  "NEVER_UTF": pcre.NEVER_UTF,
+  "ANCHORED": pcre.ANCHORED,
+  "DOLLAR_ENDONLY": pcre.DOLLAR_ENDONLY,
+  "FIRSTLINE": pcre.FIRSTLINE,
+  "NO_AUTO_CAPTURE": pcre.NO_AUTO_CAPTURE,
+  "JAVASCRIPT_COMPAT": pcre.JAVASCRIPT_COMPAT,
+  "U": pcre.UTF8 or pcre.UCP
+}.toTable
+
+# Options that are supported inside regular expressions themselves
+const SkipOptions = [
+  "LIMIT_MATCH=", "LIMIT_RECURSION=", "NO_AUTO_POSSESS", "NO_START_OPT",
+  "UTF8", "UTF16", "UTF32", "UTF", "UCP",
+  "CR", "LF", "CRLF", "ANYCRLF", "ANY", "BSR_ANYCRLF", "BSR_UNICODE"
+]
+
+proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study: bool] =
+  result = ("", 0, true)
+
+  var optionStart = 0
+  var equals = false
+  for i, c in pattern:
+    if optionStart == i:
+      if c != '(':
+        break
+      optionStart = i
+
+    elif optionStart == i-1:
+      if c != '*':
+        break
+
+    elif c == ')':
+      let name = pattern[optionStart+2 .. i-1]
+      if equals or name in SkipOptions:
+        result.pattern.add pattern[optionStart .. i]
+      elif PcreOptions.hasKey name:
+        result.flags = result.flags or PcreOptions[name]
+      elif name == "NO_STUDY":
+        result.study = false
+      else:
+        break
+      optionStart = i+1
+      equals = false
+
+    elif not equals:
+      if c == '=':
+        equals = true
+        if pattern[optionStart+2 .. i] notin SkipOptions:
+          break
+      elif c notin {'A'..'Z', '0'..'9', '_'}:
+        break
+
+  result.pattern.add pattern[optionStart .. pattern.high]
+
+# }}}
+
+type UncheckedArray {.unchecked.}[T] = array[0 .. 0, T]
+
+proc destroyRegex(pattern: Regex) =
+  pcre.free_substring(cast[cstring](pattern.pcreObj))
+  pattern.pcreObj = nil
+  if pattern.pcreExtra != nil:
+    pcre.free_study(pattern.pcreExtra)
+
+proc getNameToNumberTable(pattern: Regex): Table[string, int] =
+  let entryCount = getinfo[cint](pattern, pcre.INFO_NAMECOUNT)
+  let entrySize = getinfo[cint](pattern, pcre.INFO_NAMEENTRYSIZE)
+  let table = cast[ptr UncheckedArray[uint8]](
+                getinfo[int](pattern, pcre.INFO_NAMETABLE))
+
+  result = initTable[string, int]()
+
+  for i in 0 .. <entryCount:
+    let pos = i * entrySize
+    let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1
+    var name = ""
+
+    var idx = 2
+    while table[pos + idx] != 0:
+      name.add(char(table[pos + idx]))
+      idx += 1
+
+    result[name] = num
+
+proc initRegex(pattern: string, flags: int, study = true): Regex =
+  new(result, destroyRegex)
+  result.pattern = pattern
+
+  var errorMsg: cstring
+  var errOffset: cint
+
+  result.pcreObj = pcre.compile(cstring(pattern),
+                                # better hope int is at least 4 bytes..
+                                cint(flags), addr errorMsg,
+                                addr errOffset, nil)
+  if result.pcreObj == nil:
+    # failed to compile
+    raise SyntaxError(msg: $errorMsg, pos: errOffset, pattern: pattern)
+
+  if study:
+    # XXX investigate JIT
+    result.pcreExtra = pcre.study(result.pcreObj, 0x0, addr errorMsg)
+    if errorMsg != nil:
+      raise StudyError(msg: $errorMsg)
+
+  result.captureNameToId = result.getNameToNumberTable()
+
+proc re*(pattern: string): Regex =
+  let (pattern, flags, study) = extractOptions(pattern)
+  initRegex(pattern, flags, study)
+# }}}
+
+# Operations {{{
+proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] =
+  var myResult = RegexMatch(pattern : pattern, str : str)
+  # See PCRE man pages.
+  # 2x capture count to make room for start-end pairs
+  # 1x capture count as slack space for PCRE
+  let vecsize = (pattern.captureCount() + 1) * 3
+  # div 2 because each element is 2 cints long
+  myResult.pcreMatchBounds = newSeq[Slice[cint]](ceil(vecsize / 2).int)
+  myResult.pcreMatchBounds.setLen(vecsize div 3)
+
+  let strlen = if endpos == int.high: str.len else: endpos+1
+  doAssert(strlen <= str.len)  # don't want buffer overflows
+
+  let execRet = pcre.exec(pattern.pcreObj,
+                          pattern.pcreExtra,
+                          cstring(str),
+                          cint(strlen),
+                          cint(start),
+                          cint(flags),
+                          cast[ptr cint](addr myResult.pcreMatchBounds[0]),
+                          cint(vecsize))
+  if execRet >= 0:
+    return some(myResult)
+
+  case execRet:
+    of pcre.ERROR_NOMATCH:
+      return none(RegexMatch)
+    of pcre.ERROR_NULL:
+      raise newException(AccessViolationError, "Expected non-null parameters")
+    of pcre.ERROR_BADOPTION:
+      raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " &
+        "outdated PCRE.")
+    of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET:
+      raise InvalidUnicodeError(msg : "Invalid unicode byte sequence",
+        pos : myResult.pcreMatchBounds[0].a)
+    else:
+      raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
+
+proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
+  ## Like ```find(...)`` <#proc-find>`__, but anchored to the start of the
+  ## string. This means that ``"foo".match(re"f") == true``, but
+  ## ``"foo".match(re"o") == false``.
+  return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
+
+iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
+  ## Works the same as ```find(...)`` <#proc-find>`__, but finds every
+  ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
+  ## ``"22", "22", "22"``.
+  ##
+  ## Arguments are the same as ```find(...)`` <#proc-find>`__
+  ##
+  ## Variants:
+  ##
+  ## -  ``proc findAll(...)`` returns a ``seq[string]``
+  # see pcredemo for explaination
+  let matchesCrLf = pattern.matchesCrLf()
+  let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and
+    pcre.UTF8) > 0u32
+  let strlen = if endpos == int.high: str.len else: endpos+1
+
+  var offset = start
+  var match: Option[RegexMatch]
+  while true:
+    var flags = 0
+
+    if match.isSome and
+       match.get.matchBounds.a > match.get.matchBounds.b:
+      # 0-len match
+      flags = pcre.NOTEMPTY_ATSTART
+
+    match = str.matchImpl(pattern, offset, endpos, flags)
+
+    if match.isNone:
+      # either the end of the input or the string
+      # cannot be split here
+      if offset >= strlen:
+        break
+
+      if matchesCrLf and offset < (str.len - 1) and
+         str[offset] == '\r' and str[offset + 1] == '\L':
+        # if PCRE treats CrLf as newline, skip both at the same time
+        offset += 2
+      elif unicode:
+        # XXX what about invalid unicode?
+        offset += str.runeLenAt(offset)
+        assert(offset <= strlen)
+      else:
+        offset += 1
+    else:
+      offset = match.get.matchBounds.b + 1
+
+      yield match.get
+
+
+proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
+  ## Finds the given pattern in the string between the end and start
+  ## positions.
+  ##
+  ## ``start``
+  ##     The start point at which to start matching. ``|abc`` is ``0``;
+  ##     ``a|bc`` is ``1``
+  ##
+  ## ``endpos``
+  ##     The maximum index for a match; ``int.high`` means the end of the
+  ##     string, otherwise it’s an inclusive upper bound.
+  return str.matchImpl(pattern, start, endpos, 0)
+
+proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[string] =
+  result = @[]
+  for match in str.findIter(pattern, start, endpos):
+    result.add(match.match)
+
+proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] =
+  ## Splits the string with the given regex. This works according to the
+  ## rules that Perl and Javascript use:
+  ##
+  ## -  If the match is zero-width, then the string is still split:
+  ##    ``"123".split(r"") == @["1", "2", "3"]``.
+  ##
+  ## -  If the pattern has a capture in it, it is added after the string
+  ##    split: ``"12".split(re"(\d)") == @["", "1", "", "2", ""]``.
+  ##
+  ## -  If ``maxsplit != -1``, then the string will only be split
+  ##    ``maxsplit - 1`` times. This means that there will be ``maxsplit``
+  ##    strings in the output seq.
+  ##    ``"1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]``
+  ##
+  ## ``start`` behaves the same as in ```find(...)`` <#proc-find>`__.
+  result = @[]
+  var lastIdx = start
+  var splits = 0
+  var bounds = 0 .. -1
+  var never_ran = true
+
+  for match in str.findIter(pattern, start = start):
+    never_ran = false
+
+    # bounds are inclusive:
+    #
+    # 0123456
+    #  ^^^
+    # (1, 3)
+    bounds = match.matchBounds
+
+    # "12".split("") would be @["", "1", "2"], but
+    # if we skip an empty first match, it's the correct
+    # @["1", "2"]
+    if bounds.a <= bounds.b or bounds.a > start:
+      result.add(str.substr(lastIdx, bounds.a - 1))
+      splits += 1
+
+    lastIdx = bounds.b + 1
+
+    for cap in match.captures:
+      # if there are captures, include them in the result
+      result.add(cap)
+
+    if splits == maxSplit - 1:
+      break
+
+  # "12".split("\b") would be @["1", "2", ""], but
+  # if we skip an empty last match, it's the correct
+  # @["1", "2"]
+  # If matches were never found, then the input string is the result
+  if bounds.a <= bounds.b or bounds.b < str.high or never_ran:
+    # last match: Each match takes the previous substring,
+    # but "1 2".split(/ /) needs to return @["1", "2"].
+    # This handles "2"
+    result.add(str.substr(bounds.b + 1, str.high))
+
+template replaceImpl(str: string, pattern: Regex,
+                     replacement: expr): stmt {.immediate, dirty.} =
+  # XXX seems very similar to split, maybe I can reduce code duplication
+  # somehow?
+  result = ""
+  var lastIdx = 0
+  for match {.inject.} in str.findIter(pattern):
+    let bounds = match.matchBounds
+    result.add(str.substr(lastIdx, bounds.a - 1))
+    let nextVal = replacement
+    assert(nextVal != nil)
+    result.add(nextVal)
+
+    lastIdx = bounds.b + 1
+
+  result.add(str.substr(lastIdx, str.len - 1))
+  return result
+
+proc replace*(str: string, pattern: Regex,
+              subproc: proc (match: RegexMatch): string): string =
+  ## Replaces each match of Regex in the string with ``sub``, which should
+  ## never be or return ``nil``.
+  ##
+  ## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with
+  ## each match and the return value is the replacement value.
+  ##
+  ## If ``sub`` is a ``proc (string): string``, then it is executed with the
+  ## full text of the match and and the return value is the replacement
+  ## value.
+  ##
+  ## If ``sub`` is a string, the syntax is as follows:
+  ##
+  ## -  ``$$`` - literal ``$``
+  ## -  ``$123`` - capture number ``123``
+  ## -  ``$foo`` - named capture ``foo``
+  ## -  ``${foo}`` - same as above
+  ## -  ``$1$#`` - first and second captures
+  ## -  ``$#`` - first capture
+  ## -  ``$0`` - full match
+  ##
+  ## If a given capture is missing, a ``ValueError`` exception is thrown.
+  replaceImpl(str, pattern, subproc(match))
+
+proc replace*(str: string, pattern: Regex,
+              subproc: proc (match: string): string): string =
+  replaceImpl(str, pattern, subproc(match.match))
+
+proc replace*(str: string, pattern: Regex, sub: string): string =
+  # - 1 because the string numbers are 0-indexed
+  replaceImpl(str, pattern,
+    formatStr(sub, match.captures[name], match.captures[id - 1]))
+
+# }}}
+
+let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])"
+proc escapeRe*(str: string): string =
+  ## Escapes the string so it doesn’t match any special characters.
+  ## Incompatible with the Extra flag (``X``).
+  str.replace(SpecialCharMatcher, "\\$1")
diff --git a/lib/impure/nre/.gitignore b/lib/impure/nre/.gitignore
new file mode 100644
index 000000000..3d647a25e
--- /dev/null
+++ b/lib/impure/nre/.gitignore
@@ -0,0 +1,9 @@
+# all executables
+*
+!*/
+!*.*
+*.exe
+
+# Wildcard patterns.
+*.swp
+nimcache
diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim
new file mode 100644
index 000000000..253bfada7
--- /dev/null
+++ b/lib/impure/nre/private/util.nim
@@ -0,0 +1,63 @@
+## INTERNAL FILE FOR USE ONLY BY nre.nim.
+import tables
+
+proc fget*[K, V](self: Table[K, V], key: K): V =
+  if self.hasKey(key):
+    return self[key]
+  else:
+    raise newException(KeyError, "Key does not exist in table: " & $key)
+
+const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
+const StartIdent = Ident - {'0'..'9'}
+
+proc checkNil(arg: string): string =
+  if arg == nil:
+    raise newException(ValueError, "Cannot use nil capture")
+  else:
+    return arg
+
+template formatStr*(howExpr, namegetter, idgetter: expr): expr =
+  let how = howExpr
+  var val = newStringOfCap(how.len)
+  var i = 0
+  var lastNum = 1
+
+  while i < how.len:
+    if how[i] != '$':
+      val.add(how[i])
+      i += 1
+    else:
+      if how[i + 1] == '$':
+        val.add('$')
+        i += 2
+      elif how[i + 1] == '#':
+        var id {.inject.} = lastNum
+        val.add(checkNil(idgetter))
+        lastNum += 1
+        i += 2
+      elif how[i + 1] in {'0'..'9'}:
+        i += 1
+        var id {.inject.} = 0
+        while i < how.len and how[i] in {'0'..'9'}:
+          id += (id * 10) + (ord(how[i]) - ord('0'))
+          i += 1
+        val.add(checkNil(idgetter))
+        lastNum = id + 1
+      elif how[i + 1] in StartIdent:
+        i += 1
+        var name {.inject.} = ""
+        while i < how.len and how[i] in Ident:
+          name.add(how[i])
+          i += 1
+        val.add(checkNil(namegetter))
+      elif how[i + 1] == '{':
+        i += 2
+        var name {.inject.} = ""
+        while i < how.len and how[i] != '}':
+          name.add(how[i])
+          i += 1
+        i += 1
+        val.add(checkNil(namegetter))
+      else:
+        raise newException(Exception, "Syntax error in format string at " & $i)
+  val
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index 55f8c5d32..b373859f4 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -7,8 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains code for reading from `stdin`:idx:. On UNIX the GNU
-## readline library is wrapped and set up to provide default key bindings
+## This module contains code for reading from `stdin`:idx:. On UNIX the
+## linenoise library is wrapped and set up to provide default key bindings
 ## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
 ## is used. This suffices because Windows' console already provides the
 ## wanted functionality.
@@ -94,40 +94,34 @@ when defined(Windows):
         while i < password.len:
           x = runeLenAt(password, i)
           inc i, x
-        password.setLen(password.len - x)
+        password.setLen(max(password.len - x, 0))
       else:
         password.add(toUTF8(c.Rune))
     stdout.write "\n"
 
 else:
-  import readline, history, termios, unsigned
+  import linenoise, termios, unsigned
 
   proc readLineFromStdin*(prompt: string): TaintedString {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
-    var buffer = readline.readLine(prompt)
+    var buffer = linenoise.readLine(prompt)
     if isNil(buffer): quit(0)
     result = TaintedString($buffer)
     if result.string.len > 0:
-      add_history(buffer)
-    readline.free(buffer)
+      historyAdd(buffer)
+    linenoise.free(buffer)
 
   proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
-    var buffer = readline.readLine(prompt)
+    var buffer = linenoise.readLine(prompt)
     if isNil(buffer): quit(0)
     line = TaintedString($buffer)
     if line.string.len > 0:
-      add_history(buffer)
-    readline.free(buffer)
+      historyAdd(buffer)
+    linenoise.free(buffer)
     # XXX how to determine CTRL+D?
     result = true
 
-  # initialization:
-  # disable auto-complete:
-  proc doNothing(a, b: cint): cint {.cdecl, procvar.} = discard
-
-  discard readline.bind_key('\t'.ord, doNothing)
-
   proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
                               bool {.tags: [ReadIOEffect, WriteIOEffect].} =
     password.setLen(0)
@@ -135,7 +129,7 @@ else:
     var cur, old: Termios
     discard fd.tcgetattr(cur.addr)
     old = cur
-    cur.lflag = cur.lflag and not Tcflag(ECHO)
+    cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
     discard fd.tcsetattr(TCSADRAIN, cur.addr)
     stdout.write prompt
     result = stdin.readLine(password)
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index c24734f89..30081bb19 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -7,8 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-## Regular expression support for Nim. Consider using the pegs module
-## instead.
+## Regular expression support for Nim. Deprecated. Consider using the ``nre``
+## or ``pegs`` modules instead.
 ##
 ## **Note:** The 're' proc defaults to the **extended regular expression
 ## syntax** which lets you use whitespace freely to make your regexes readable.
@@ -36,31 +36,31 @@ const
 type
   RegexFlag* = enum     ## options for regular expressions
     reIgnoreCase = 0,    ## do caseless matching
-    reMultiLine = 1,     ## ``^`` and ``$`` match newlines within data 
+    reMultiLine = 1,     ## ``^`` and ``$`` match newlines within data
     reDotAll = 2,        ## ``.`` matches anything including NL
     reExtended = 3,      ## ignore whitespace and ``#`` comments
     reStudy = 4          ## study the expression (may be omitted if the
                          ## expression will be used only once)
-    
-  RegexDesc = object 
-    h: PPcre
-    e: ptr TExtra
-    
-  Regex* = ref RegexDesc ## a compiled regular expression
-    
+
+  RegexDesc = object
+    h: ptr Pcre
+    e: ptr ExtraData
+
+  Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
+
   RegexError* = object of ValueError
     ## is raised if the pattern is no valid regular expression.
 
 {.deprecated: [TRegexFlag: RegexFlag, TRegexDesc: RegexDesc, TRegex: Regex,
     EInvalidRegEx: RegexError].}
 
-proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} = 
+proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
   var e: ref RegexError
   new(e)
   e.msg = msg
   raise e
 
-proc rawCompile(pattern: string, flags: cint): PPcre =
+proc rawCompile(pattern: string, flags: cint): ptr Pcre =
   var
     msg: cstring
     offset: cint
@@ -68,15 +68,15 @@ proc rawCompile(pattern: string, flags: cint): PPcre =
   if result == nil:
     raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
 
-proc finalizeRegEx(x: Regex) = 
+proc finalizeRegEx(x: Regex) =
   # XXX This is a hack, but PCRE does not export its "free" function properly.
   # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``).
-  # Fortunately the implementation is unlikely to change. 
+  # Fortunately the implementation is unlikely to change.
   pcre.free_substring(cast[cstring](x.h))
   if not isNil(x.e):
     pcre.free_substring(cast[cstring](x.e))
 
-proc re*(s: string, flags = {reExtended, reStudy}): Regex =
+proc re*(s: string, flags = {reExtended, reStudy}): Regex {.deprecated.} =
   ## Constructor of regular expressions. Note that Nim's
   ## extended raw string literals support this syntax ``re"[abc]"`` as
   ## a short form for ``re(r"[abc]")``.
@@ -84,7 +84,7 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
   result.h = rawCompile(s, cast[cint](flags - {reStudy}))
   if reStudy in flags:
     var msg: cstring
-    result.e = pcre.study(result.h, 0, msg)
+    result.e = pcre.study(result.h, 0, addr msg)
     if not isNil(msg): raiseInvalidRegex($msg)
 
 proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
@@ -101,10 +101,10 @@ proc matchOrFind(s: string, pattern: Regex, matches: var openArray[string],
     if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
     else: matches[i-1] = nil
   return rawMatches[1] - rawMatches[0]
-  
+
 proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
                  start = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of `pattern` in `s` 
+  ## returns the starting position and end position of `pattern` in `s`
   ## and the captured
   ## substrings in the array `matches`. If it does not match, nothing
   ## is written into `matches` and ``(-1,0)`` is returned.
@@ -120,12 +120,12 @@ proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
     if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
     else: matches[i-1] = nil
   return (rawMatches[0].int, rawMatches[1].int - 1)
-  
-proc findBounds*(s: string, pattern: Regex, 
+
+proc findBounds*(s: string, pattern: Regex,
                  matches: var openArray[tuple[first, last: int]],
                  start = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of ``pattern`` in ``s`` 
-  ## and the captured substrings in the array `matches`. 
+  ## returns the starting position and end position of ``pattern`` in ``s``
+  ## and the captured substrings in the array `matches`.
   ## If it does not match, nothing is written into `matches` and
   ## ``(-1,0)`` is returned.
   var
@@ -141,10 +141,10 @@ proc findBounds*(s: string, pattern: Regex,
     else: matches[i-1] = (-1,0)
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
-proc findBounds*(s: string, pattern: Regex, 
+proc findBounds*(s: string, pattern: Regex,
                  start = 0): tuple[first, last: int] =
-  ## returns the starting position of `pattern` in `s`. If it does not
-  ## match, ``(-1,0)`` is returned.
+  ## returns the starting position and end position of ``pattern`` in ``s``.
+  ## If it does not match, ``(-1,0)`` is returned.
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
@@ -152,7 +152,7 @@ proc findBounds*(s: string, pattern: Regex,
       cast[ptr cint](rawMatches), 3)
   if res < 0'i32: return (int(res), 0)
   return (int(rawMatches[0]), int(rawMatches[1]-1))
-  
+
 proc matchOrFind(s: string, pattern: Regex, start, flags: cint): cint =
   var
     rtarray = initRtArray[cint](3)
@@ -172,7 +172,7 @@ proc matchLen*(s: string, pattern: Regex, matches: var openArray[string],
 proc matchLen*(s: string, pattern: Regex, start = 0): int =
   ## the same as ``match``, but it returns the length of the match,
   ## if there is no match, -1 is returned. Note that a match length
-  ## of zero can happen. 
+  ## of zero can happen.
   return matchOrFind(s, pattern, start.cint, pcre.ANCHORED)
 
 proc match*(s: string, pattern: Regex, start = 0): bool =
@@ -216,7 +216,7 @@ proc find*(s: string, pattern: Regex, start = 0): int =
   if res < 0'i32: return res
   return rawMatches[0]
 
-iterator findAll*(s: string, pattern: Regex, start = 0): string = 
+iterator findAll*(s: string, pattern: Regex, start = 0): string =
   ## Yields all matching *substrings* of `s` that match `pattern`.
   ##
   ## Note that since this is an iterator you should not modify the string you
@@ -231,10 +231,11 @@ iterator findAll*(s: string, pattern: Regex, start = 0): string =
     if res < 0'i32: break
     let a = rawMatches[0]
     let b = rawMatches[1]
+    if a == b and a == i: break
     yield substr(s, int(a), int(b)-1)
     i = b
 
-proc findAll*(s: string, pattern: Regex, start = 0): seq[string] = 
+proc findAll*(s: string, pattern: Regex, start = 0): seq[string] =
   ## returns all matching *substrings* of `s` that match `pattern`.
   ## If it does not match, @[] is returned.
   accumulateResult(findAll(s, pattern, start))
@@ -242,13 +243,13 @@ proc findAll*(s: string, pattern: Regex, start = 0): seq[string] =
 when not defined(nimhygiene):
   {.pragma: inject.}
 
-template `=~` *(s: string, pattern: Regex): expr = 
-  ## This calls ``match`` with an implicit declared ``matches`` array that 
-  ## can be used in the scope of the ``=~`` call: 
-  ## 
+template `=~` *(s: string, pattern: Regex): expr =
+  ## This calls ``match`` with an implicit declared ``matches`` array that
+  ## can be used in the scope of the ``=~`` call:
+  ##
   ## .. code-block:: nim
   ##
-  ##   if line =~ re"\s*(\w+)\s*\=\s*(\w+)": 
+  ##   if line =~ re"\s*(\w+)\s*\=\s*(\w+)":
   ##     # matches a key=value pair:
   ##     echo("Key: ", matches[0])
   ##     echo("Value: ", matches[1])
@@ -260,9 +261,9 @@ template `=~` *(s: string, pattern: Regex): expr =
   ##   else:
   ##     echo("syntax error")
   ##
-  bind MaxSubPatterns
+  bind MaxSubpatterns
   when not declaredInScope(matches):
-    var matches {.inject.}: array[0..MaxSubpatterns-1, string]
+    var matches {.inject.}: array[MaxSubpatterns, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
@@ -286,7 +287,7 @@ proc endsWith*(s: string, suffix: Regex): bool =
     if matchLen(s, suffix, i) == s.len - i: return true
 
 proc replace*(s: string, sub: Regex, by = ""): string =
-  ## Replaces `sub` in `s` by the string `by`. Captures cannot be 
+  ## Replaces `sub` in `s` by the string `by`. Captures cannot be
   ## accessed in `by`. Examples:
   ##
   ## .. code-block:: nim
@@ -306,7 +307,7 @@ proc replace*(s: string, sub: Regex, by = ""): string =
     add(result, by)
     prev = match.last + 1
   add(result, substr(s, prev))
-  
+
 proc replacef*(s: string, sub: Regex, by: string): string =
   ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
   ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
@@ -320,7 +321,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
   ##
   ## "var1<-keykey; val2<-key2key2"
   result = ""
-  var caps: array[0..MaxSubpatterns-1, string]
+  var caps: array[MaxSubpatterns, string]
   var prev = 0
   while true:
     var match = findBounds(s, sub, caps, prev)
@@ -338,7 +339,7 @@ proc parallelReplace*(s: string, subs: openArray[
   ## applied in parallel.
   result = ""
   var i = 0
-  var caps: array[0..MaxSubpatterns-1, string]
+  var caps: array[MaxSubpatterns, string]
   while i < s.len:
     block searchSubs:
       for j in 0..high(subs):
@@ -359,7 +360,7 @@ proc transformFile*(infile, outfile: string,
   ## error occurs. This is supposed to be used for quick scripting.
   var x = readFile(infile).string
   writeFile(outfile, x.parallelReplace(subs))
-  
+
 iterator split*(s: string, sep: Regex): string =
   ## Splits the string `s` into substrings.
   ##
@@ -368,69 +369,78 @@ iterator split*(s: string, sep: Regex): string =
   ##
   ## .. code-block:: nim
   ##   for word in split("00232this02939is39an22example111", re"\d+"):
-  ##     writeln(stdout, word)
+  ##     writeLine(stdout, word)
   ##
   ## Results in:
   ##
   ## .. code-block:: nim
+  ##   ""
   ##   "this"
   ##   "is"
   ##   "an"
   ##   "example"
+  ##   ""
   ##
   var
-    first = 0
-    last = 0
+    first = -1
+    last = -1
   while last < len(s):
     var x = matchLen(s, sep, last)
     if x > 0: inc(last, x)
     first = last
+    if x == 0: inc(last)
     while last < len(s):
-      inc(last)
       x = matchLen(s, sep, last)
-      if x > 0: break
-    if first < last:
+      if x >= 0: break
+      inc(last)
+    if first <= last:
       yield substr(s, first, last-1)
 
 proc split*(s: string, sep: Regex): seq[string] =
   ## Splits the string `s` into substrings.
   accumulateResult(split(s, sep))
-  
-proc escapeRe*(s: string): string = 
-  ## escapes `s` so that it is matched verbatim when used as a regular 
+
+proc escapeRe*(s: string): string =
+  ## escapes `s` so that it is matched verbatim when used as a regular
   ## expression.
   result = ""
   for c in items(s):
     case c
     of 'a'..'z', 'A'..'Z', '0'..'9', '_':
       result.add(c)
-    else: 
+    else:
       result.add("\\x")
       result.add(toHex(ord(c), 2))
-  
+
 const ## common regular expressions
-  reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"  ## describes an identifier
-  reNatural* = r"\b\d+\b" ## describes a natural number
-  reInteger* = r"\b[-+]?\d+\b" ## describes an integer
-  reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
-  reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
-  reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
-  reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
+  reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
+    ## describes an identifier
+  reNatural* {.deprecated.} = r"\b\d+\b"
+    ## describes a natural number
+  reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
+    ## describes an integer
+  reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
+    ## describes a hexadecimal number
+  reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
+    ## describes a binary number (example: 0b11101)
+  reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
+    ## describes an octal number (example: 0o777)
+  reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
     ## describes a floating point number
-  reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
-             r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
-             r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
-             r"(?:[a-zA-Z]{2}|com|org|" &
-             r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
+  reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
+                            r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
+                            r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
+                            r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
+                            r"info|mobi|name|aero|jobs|museum)\b"
     ## describes a common email address
-  reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
-           r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
+  reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
+                          r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
     ## describes an URL
 
 when isMainModule:
   assert match("(a b c)", re"\( .* \)")
   assert match("WHiLe", re("while", {reIgnoreCase}))
-  
+
   assert "0158787".match(re"\d+")
   assert "ABC 0232".match(re"\w+\s+\d+")
   assert "ABC".match(re"\d+ | \w+")
@@ -439,21 +449,21 @@ when isMainModule:
 
   var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
   assert matchLen("key1=  cal9", pattern) == 11
-  
+
   assert find("_____abc_______", re"abc") == 5
-  
-  var matches: array[0..5, string]
-  if match("abcdefg", re"c(d)ef(g)", matches, 2): 
+
+  var matches: array[6, string]
+  if match("abcdefg", re"c(d)ef(g)", matches, 2):
     assert matches[0] == "d"
     assert matches[1] == "g"
   else:
     assert false
-  
+
   if "abc" =~ re"(a)bcxyz|(\w+)":
     assert matches[1] == "abc"
   else:
     assert false
-  
+
   if "abc" =~ re"(cba)?.*":
     assert matches[0] == nil
   else: assert false
@@ -461,7 +471,7 @@ when isMainModule:
   if "abc" =~ re"().*":
     assert matches[0] == ""
   else: assert false
-    
+
   assert "var1=key; var2=key2".endsWith(re"\w+=\w+")
   assert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
          "var1<-keykey; var2<-key2key2")
@@ -471,7 +481,12 @@ when isMainModule:
   var accum: seq[string] = @[]
   for word in split("00232this02939is39an22example111", re"\d+"):
     accum.add(word)
-  assert(accum == @["this", "is", "an", "example"])
+  assert(accum == @["", "this", "is", "an", "example", ""])
+
+  accum = @[]
+  for word in split("AAA :   : BBB", re"\s*:\s*"):
+    accum.add(word)
+  assert(accum == @["AAA", "", "BBB"])
 
   for x in findAll("abcdef", re"^{.}", 3):
     assert x == "d"
@@ -484,7 +499,7 @@ when isMainModule:
   assert("XYZ".match(re"^\d*") == true)
 
   block:
-    var matches: array[0..15, string]
+    var matches: array[16, string]
     if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches):
       for i in 0..matches.high:
         assert matches[i] == $chr(i + 'a'.ord)
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
index bb7cfc0d3..721e5ce51 100644
--- a/lib/impure/ssl.nim
+++ b/lib/impure/ssl.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## This module provides an easy to use sockets-style 
+## This module provides an easy to use sockets-style
 ## nim interface to the OpenSSL library.
 
 {.deprecated.}
@@ -15,44 +15,45 @@
 import openssl, strutils, os
 
 type
-  TSecureSocket* = object
+  SecureSocket* = object
     ssl: SslPtr
     bio: BIO
+{.deprecated: [TSecureSocket: SecureSocket].}
 
-proc connect*(sock: var TSecureSocket, address: string, 
+proc connect*(sock: var SecureSocket, address: string,
     port: int): int =
   ## Connects to the specified `address` on the specified `port`.
   ## Returns the result of the certificate validation.
   SslLoadErrorStrings()
   ERR_load_BIO_strings()
-  
+
   if SSL_library_init() != 1:
     raiseOSError(osLastError())
-  
+
   var ctx = SSL_CTX_new(SSLv23_client_method())
   if ctx == nil:
     ERR_print_errors_fp(stderr)
     raiseOSError(osLastError())
-    
-  #if SSL_CTX_load_verify_locations(ctx, 
+
+  #if SSL_CTX_load_verify_locations(ctx,
   #   "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
   #  echo("Failed load verify locations")
   #  ERR_print_errors_fp(stderr)
-  
+
   sock.bio = BIO_new_ssl_connect(ctx)
   if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
     raiseOSError(osLastError())
 
   if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
     raiseOSError(osLastError())
-  
+
   if BIO_do_connect(sock.bio) <= 0:
     ERR_print_errors_fp(stderr)
     raiseOSError(osLastError())
-  
+
   result = SSL_get_verify_result(sock.ssl)
 
-proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
+proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
   ## Acts in a similar fashion to the `recvLine` in the sockets module.
   ## Returns false when no data is available to be read.
   ## `Line` must be initialized and not nil!
@@ -71,26 +72,26 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
     add(line.string, c)
 
 
-proc send*(sock: TSecureSocket, data: string) =
+proc send*(sock: SecureSocket, data: string) =
   ## Writes `data` to the socket.
   if BIO_write(sock.bio, data, data.len.cint) <= 0:
     raiseOSError(osLastError())
 
-proc close*(sock: TSecureSocket) =
+proc close*(sock: SecureSocket) =
   ## Closes the socket
   if BIO_free(sock.bio) <= 0:
     ERR_print_errors_fp(stderr)
     raiseOSError(osLastError())
 
-when isMainModule:
-  var s: TSecureSocket
+when not defined(testing) and isMainModule:
+  var s: SecureSocket
   echo connect(s, "smtp.gmail.com", 465)
-  
+
   #var buffer: array[0..255, char]
   #echo BIO_read(bio, buffer, buffer.len)
   var buffer: string = ""
-  
+
   echo s.recvLine(buffer)
-  echo buffer 
+  echo buffer
   echo buffer.len
-  
+
diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
deleted file mode 100644
index c22294061..000000000
--- a/lib/impure/zipfiles.nim
+++ /dev/null
@@ -1,169 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements a zip archive creator/reader/modifier.
-
-import 
-  streams, libzip, times, os
-
-type
-  TZipArchive* = object of RootObj ## represents a zip archive
-    mode: FileMode
-    w: PZip
-
-
-proc zipError(z: var TZipArchive) = 
-  var e: ref IOError
-  new(e)
-  e.msg = $zip_strerror(z.w)
-  raise e
-  
-proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
-  ## Opens a zip file for reading, writing or appending. All file modes are 
-  ## supported. Returns true iff successful, false otherwise.
-  var err, flags: int32
-  case mode
-  of fmRead, fmReadWriteExisting, fmAppend: flags = 0
-  of fmWrite:
-    if existsFile(filename): removeFile(filename)
-    flags = ZIP_CREATE or ZIP_EXCL
-  of fmReadWrite: flags = ZIP_CREATE
-  z.w = zip_open(filename, flags, addr(err))
-  z.mode = mode
-  result = z.w != nil
-
-proc close*(z: var TZipArchive) =
-  ## Closes a zip file.
-  zip_close(z.w)
- 
-proc createDir*(z: var TZipArchive, dir: string) = 
-  ## Creates a directory within the `z` archive. This does not fail if the
-  ## directory already exists. Note that for adding a file like 
-  ## ``"path1/path2/filename"`` it is not necessary
-  ## to create the ``"path/path2"`` subdirectories - it will be done 
-  ## automatically by ``addFile``. 
-  assert(z.mode != fmRead) 
-  discard zip_add_dir(z.w, dir)
-  zip_error_clear(z.w)
-
-proc addFile*(z: var TZipArchive, dest, src: string) = 
-  ## Adds the file `src` to the archive `z` with the name `dest`. `dest`
-  ## may contain a path that will be created. 
-  assert(z.mode != fmRead) 
-  if not fileExists(src):
-    raise newException(IOError, "File '" & src & "' does not exist")
-  var zipsrc = zip_source_file(z.w, src, 0, -1)
-  if zipsrc == nil:
-    #echo("Dest: " & dest)
-    #echo("Src: " & src)
-    zipError(z)
-  if zip_add(z.w, dest, zipsrc) < 0'i32:
-    zip_source_free(zipsrc)
-    zipError(z)
-
-proc addFile*(z: var TZipArchive, file: string) = 
-  ## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
-  ## the name of the destination.
-  addFile(z, file, file)
-  
-proc mySourceCallback(state, data: pointer, len: int, 
-                      cmd: TZipSourceCmd): int {.cdecl.} = 
-  var src = cast[Stream](state)
-  case cmd
-  of ZIP_SOURCE_OPEN: 
-    if src.setPositionImpl != nil: setPosition(src, 0) # reset
-  of ZIP_SOURCE_READ:
-    result = readData(src, data, len)
-  of ZIP_SOURCE_CLOSE: close(src)
-  of ZIP_SOURCE_STAT: 
-    var stat = cast[PZipStat](data)
-    zip_stat_init(stat)
-    stat.size = high(int32)-1 # we don't know the size
-    stat.mtime = getTime()
-    result = sizeof(TZipStat)
-  of ZIP_SOURCE_ERROR:
-    var err = cast[ptr array[0..1, cint]](data)
-    err[0] = ZIP_ER_INTERNAL
-    err[1] = 0
-    result = 2*sizeof(cint)
-  of constZIP_SOURCE_FREE: GC_unref(src)
-  else: assert(false)
-  
-proc addFile*(z: var TZipArchive, dest: string, src: Stream) = 
-  ## Adds a file named with `dest` to the archive `z`. `dest`
-  ## may contain a path. The file's content is read from the `src` stream.
-  assert(z.mode != fmRead)
-  GC_ref(src)
-  var zipsrc = zip_source_function(z.w, mySourceCallback, cast[pointer](src))
-  if zipsrc == nil: zipError(z)
-  if zip_add(z.w, dest, zipsrc) < 0'i32:
-    zip_source_free(zipsrc)
-    zipError(z)
-  
-# -------------- zip file stream ---------------------------------------------
-
-type
-  TZipFileStream = object of StreamObj
-    f: PZipFile
-
-  PZipFileStream* = 
-    ref TZipFileStream ## a reader stream of a file within a zip archive 
-
-proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f)
-proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = 
-  result = zip_fread(PZipFileStream(s).f, buffer, bufLen)
-
-proc newZipFileStream(f: PZipFile): PZipFileStream = 
-  new(result)
-  result.f = f
-  result.closeImpl = fsClose
-  result.readDataImpl = fsReadData
-  # other methods are nil!
-
-# ----------------------------------------------------------------------------
-  
-proc getStream*(z: var TZipArchive, filename: string): PZipFileStream = 
-  ## returns a stream that can be used to read the file named `filename`
-  ## from the archive `z`. Returns nil in case of an error.
-  ## The returned stream does not support the `setPosition`, `getPosition`, 
-  ## `writeData` or `atEnd` methods.
-  var x = zip_fopen(z.w, filename, 0'i32)
-  if x != nil: result = newZipFileStream(x)
-  
-iterator walkFiles*(z: var TZipArchive): string = 
-  ## walks over all files in the archive `z` and returns the filename 
-  ## (including the path).
-  var i = 0'i32
-  var num = zip_get_num_files(z.w)
-  while i < num:
-    yield $zip_get_name(z.w, i, 0'i32)
-    inc(i)
-
-
-proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
-  ## extracts a file from the zip archive `z` to the destination stream.
-  var strm = getStream(z, srcFile)
-  while true:
-    if not strm.atEnd:
-        dest.write(strm.readStr(1))
-    else: break
-  dest.flush()
-  strm.close()
-
-proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
-  ## extracts a file from the zip archive `z` to the destination filename.
-  var file = newFileStream(dest, fmReadWrite)
-  extractFile(z, srcFile, file)
-  file.close()
-
-proc extractAll*(z: var TZipArchive, dest: string) =
-  ## extracts all files from archive `z` to the destination directory.
-  for file in walkFiles(z):
-    extractFile(z, file, dest / extractFilename(file))
-