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_mongo.nim227
-rwxr-xr-xlib/impure/db_mysql.nim203
-rwxr-xr-xlib/impure/db_postgres.nim186
-rwxr-xr-xlib/impure/db_sqlite.nim204
-rwxr-xr-xlib/impure/dialogs.nim226
-rwxr-xr-xlib/impure/graphics.nim567
-rw-r--r--lib/impure/nre.nim751
-rw-r--r--lib/impure/nre/.gitignore9
-rw-r--r--lib/impure/nre/private/util.nim51
-rwxr-xr-xlib/impure/osinfo_posix.nim68
-rwxr-xr-xlib/impure/osinfo_win.nim405
-rw-r--r--[-rwxr-xr-x]lib/impure/rdstdin.nim96
-rw-r--r--[-rwxr-xr-x]lib/impure/re.nim766
-rwxr-xr-xlib/impure/ssl.nim96
-rwxr-xr-xlib/impure/web.nim63
-rwxr-xr-xlib/impure/zipfiles.nim167
16 files changed, 1324 insertions, 2761 deletions
diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim
deleted file mode 100644
index b11db78f8..000000000
--- a/lib/impure/db_mongo.nim
+++ /dev/null
@@ -1,227 +0,0 @@
-#
-#
-#            Nimrod'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 higher level wrapper for `mongodb`:idx:. Example:
-##
-## .. code-block:: nimrod
-##
-##    import mongo, db_mongo, oids, json
-##
-##    var conn = db_mongo.open()
-##
-##    # construct JSON data:
-##    var data = %{"a": %13, "b": %"my string value", 
-##                 "inner": %{"i": %71} }
-##
-##    var id = insertID(conn, "test.test", data)
-##
-##    for v in find(conn, "test.test", "this.a == 13"):
-##      print v
-##
-##    delete(conn, "test.test", id)
-##    close(conn)
-
-import mongo, oids, json
-
-type
-  EDb* = object of EIO ## exception that is raised if a database error occurs
-  TDbConn* = TMongo    ## a database connection; alias for ``TMongo``
-
-  FDb* = object of FIO ## 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 dbError*(db: TDbConn, msg: string) {.noreturn.} = 
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  if db.errstr[0] != '\0':
-    e.msg = $db.errstr
-  else:
-    e.msg = $db.err & " " & msg
-  raise e
-
-proc Close*(db: var TDbConn) {.tags: [FDB].} = 
-  ## closes the database connection.
-  disconnect(db)
-  destroy(db)
-
-proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
-  tags: [FDB].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  init(result)
-  
-  let x = connect(result, host, port.cint)
-  if x != 0'i32:
-    dbError(result, "cannot open: " & host)
-
-proc jsonToBSon(b: var TBSon, key: string, j: PJsonNode) =
-  case j.kind
-  of JString:
-    add(b, key, j.str)
-  of JInt:
-    add(b, key, j.num)
-  of JFloat:
-    add(b, key, j.fnum)
-  of JBool:
-    addBool(b, key, ord(j.bval))
-  of JNull:
-    addNull(b, key)
-  of JObject:
-    addStartObject(b, key)
-    for k, v in items(j.fields):
-      jsonToBSon(b, k, v)
-    addFinishObject(b)
-  of JArray:
-    addStartArray(b, key)
-    for i, e in pairs(j.elems):
-      jsonToBSon(b, $i, e)
-    addFinishArray(b)
-
-proc jsonToBSon*(j: PJsonNode, oid: TOid): TBSon =
-  ## converts a JSON value into the BSON format. The result must be
-  ## ``destroyed`` explicitely!
-  init(result)
-  assert j.kind == JObject
-  add(result, "_id", oid)
-  for key, val in items(j.fields):
-    jsonToBSon(result, key, val)
-  finish(result)
-
-proc `[]`*(obj: var TBSon, fieldname: cstring): TBSon =
-  ## retrieves the value belonging to `fieldname`. Raises `EInvalidKey` if
-  ## the attribute does not exist.
-  var it = initIter(obj)
-  let res = find(it, result, fieldname)
-  if res == bkEOO:
-    raise newException(EInvalidIndex, "key not in object")
-
-proc getId*(obj: var TBSon): TOid =
-  ## retrieves the ``_id`` attribute of `obj`.
-  var it = initIter(obj)
-  var b: TBSon
-  let res = find(it, b, "_id")
-  if res == bkOID:
-    result = oidVal(it)[]
-  else:
-    raise newException(EInvalidIndex, "_id not in object")
-
-proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
-  tags: [FWriteDb].} =
-  ## converts `data` to BSON format and inserts it in `namespace`. Returns
-  ## the generated OID for the ``_id`` field.
-  result = genOid()
-  var x = jsonToBSon(data, result)
-  insert(db, namespace, x)
-  destroy(x)
-
-proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.
-  tags: [FWriteDb].} =
-  ## converts `data` to BSON format and inserts it in `namespace`.  
-  discard InsertID(db, namespace, data)
-
-proc update*(db: var TDbConn, namespace: string, obj: var TBSon) {.
-  tags: [FReadDB, FWriteDb].} =
-  ## updates `obj` in `namespace`.
-  var cond: TBson
-  init(cond)
-  cond.add("_id", getId(obj))
-  finish(cond)
-  update(db, namespace, cond, obj, ord(UPDATE_UPSERT))
-  destroy(cond)
-
-proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) {.
-  tags: [FReadDB, FWriteDb].} =
-  ## updates the data with `oid` to have the new data `obj`.
-  var a = jsonToBSon(obj, oid)
-  Update(db, namespace, a)
-  destroy(a)
-
-proc delete*(db: var TDbConn, namespace: string, oid: TOid) {.
-  tags: [FWriteDb].} =
-  ## Deletes the object belonging to `oid`.
-  var cond: TBson
-  init(cond)
-  cond.add("_id", oid)
-  finish(cond)
-  discard remove(db, namespace, cond)
-  destroy(cond)
-
-proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) {.
-  tags: [FWriteDb].} =
-  ## Deletes the object `obj`.
-  delete(db, namespace, getId(obj))
-
-iterator find*(db: var TDbConn, namespace: string): var TBSon {.
-  tags: [FReadDB].} =
-  ## iterates over any object in `namespace`.
-  var cursor: TCursor
-  init(cursor, db, namespace)
-  while next(cursor) == mongo.OK:
-    yield bson(cursor)[]
-  destroy(cursor)
-
-iterator find*(db: var TDbConn, namespace: string, 
-               query, fields: var TBSon): var TBSon {.tags: [FReadDB].} =
-  ## yields the `fields` of any document that suffices `query`.
-  var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32)
-  if cursor != nil:
-    while next(cursor[]) == mongo.OK:
-      yield bson(cursor[])[]
-    destroy(cursor[])
-
-proc setupFieldnames(fields: varargs[string]): TBSon =
-  init(result)
-  for x in fields: add(result, x, 1'i32)
-  finish(result)
-
-iterator find*(db: var TDbConn, namespace: string, 
-               query: var TBSon, fields: varargs[string]): var TBSon {.
-               tags: [FReadDB].} =
-  ## yields the `fields` of any document that suffices `query`. If `fields` 
-  ## is ``[]`` the whole document is yielded.
-  var f = setupFieldnames(fields)
-  var cursor = find(db, namespace, query, f, 0'i32, 0'i32, 0'i32)
-  if cursor != nil:
-    while next(cursor[]) == mongo.OK:
-      yield bson(cursor[])[]
-    destroy(cursor[])
-  destroy(f)
-
-proc setupQuery(query: string): TBSon =
-  init(result)
-  add(result, "$where", query)
-  finish(result)
-
-iterator find*(db: var TDbConn, namespace: string, 
-               query: string, fields: varargs[string]): var TBSon {.
-               tags: [FReadDB].} =
-  ## yields the `fields` of any document that suffices `query`. If `fields` 
-  ## is ``[]`` the whole document is yielded.
-  var f = setupFieldnames(fields)
-  var q = setupQuery(query)
-  var cursor = find(db, namespace, q, f, 0'i32, 0'i32, 0'i32)
-  if cursor != nil:
-    while next(cursor[]) == mongo.OK:
-      yield bson(cursor[])[]
-    destroy(cursor[])
-  destroy(q)
-  destroy(f)
-
-when false:
-  # this doesn't work this way; would require low level hacking
-  iterator fieldPairs*(obj: var TBSon): tuple[key: cstring, value: TBSon] =
-    ## iterates over `obj` and yields all (key, value)-Pairs.
-    var it = initIter(obj)
-    var v: TBSon
-    while next(it) != bkEOO:
-      let key = key(it)
-      discard init(v, value(it))
-      yield (key, v)
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
deleted file mode 100755
index 91cf8a5eb..000000000
--- a/lib/impure/db_mysql.nim
+++ /dev/null
@@ -1,203 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `mySQL`:idx: database wrapper. The same interface is 
-## implemented for other databases too.
-
-import strutils, mysql
-
-type
-  TDbConn* = PMySQL    ## encapsulates a database connection
-  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
-                       ## transformed always to the empty string.
-  EDb* = object of EIO ## exception that is raised if a database error occurs
-
-  TSqlQuery* = distinct string ## an SQL query string
-
-  FDb* = object of FIO ## 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 dbError(db: TDbConn) {.noreturn.} = 
-  ## raises an EDb exception.
-  var e: ref EDb
-  new(e)
-  e.msg = $mysql.error(db)
-  raise e
-
-proc dbError*(msg: string) {.noreturn.} = 
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
-when false:
-  proc dbQueryOpt*(db: TDbConn, 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: 
-      dbError(db)
-    var 
-      binding: seq[MYSQL_BIND]
-    discard mysql_stmt_close(stmt)
-
-proc dbQuote(s: string): string =
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else: 
-      add(result, c)
-  
-proc TryExec*(db: TDbConn, query: TSqlQuery, 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, `$`]) =
-  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, `$`]) {.
-  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 = 
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-  
-proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =  
-  if row != nil:
-    while mysql.FetchRow(sqlres) != nil: nil
-  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!.
-  rawExec(db, query, args)
-  var sqlres = mysql.UseResult(db)
-  if sqlres != nil:
-    var L = int(mysql.NumFields(sqlres))
-    var result = newRow(L)
-    var row: cstringArray
-    while true:
-      row = mysql.FetchRow(sqlres)
-      if row == nil: break
-      for i in 0..L-1: 
-        setLen(result[i], 0)
-        add(result[i], row[i])
-      yield result
-    properFreeResult(sqlres, row)
-
-proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow {.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.
-  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: 
-        setLen(result[i], 0)
-        add(result[i], row[i])
-    properFreeResult(sqlres, row)
-
-proc GetAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
-  ## executes the query and returns the whole result dataset.
-  result = @[]
-  rawExec(db, query, args)
-  var sqlres = mysql.UseResult(db)
-  if sqlres != nil:
-    var L = int(mysql.NumFields(sqlres))
-    var row: cstringArray
-    var j = 0
-    while true:
-      row = mysql.FetchRow(sqlres)
-      if row == nil: break
-      setLen(result, j+1)
-      newSeq(result[j], L)
-      for i in 0..L-1: result[j][i] = $row[i]
-      inc(j)
-    mysql.FreeResult(sqlres)
-
-iterator Rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow {.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].} = 
-  ## 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
-
-proc TryInsertID*(db: TDbConn, query: TSqlQuery, 
-                  args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
-  ## 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: 
-    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 
-  ## generated ID for the row.
-  result = TryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 {.
-                       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].} = 
-  ## closes the database connection.
-  if db != nil: mysql.Close(db)
-
-proc Open*(connection, user, password, database: string): TDbConn {.
-  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 mysql.RealConnect(result, "", user, password, database, 
-                       0'i32, nil, 0) == nil:
-    var errmsg = $mysql.error(result)
-    db_mysql.Close(result)
-    dbError(errmsg)
-
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
deleted file mode 100755
index 2dd55e05f..000000000
--- a/lib/impure/db_postgres.nim
+++ /dev/null
@@ -1,186 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## 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
-                       ## transformed always to the empty string.
-  EDb* = object of EIO ## exception that is raised if a database error occurs
-  
-  TSqlQuery* = distinct string ## an SQL query string
-
-  FDb* = object of FIO ## 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 
-  ## 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 
-  ## on, later versions will check the string for valid syntax.
-  result = TSqlQuery(query)
- 
-proc dbError(db: TDbConn) {.noreturn.} = 
-  ## raises an EDb exception.
-  var e: ref EDb
-  new(e)
-  e.msg = $PQerrorMessage(db)
-  raise e
-
-proc dbError*(msg: string) {.noreturn.} = 
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
-proc dbQuote(s: string): string =
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else: 
-      add(result, c)
-  
-proc TryExec*(db: TDbConn, query: TSqlQuery, 
-              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
-  result = PQresultStatus(res) == PGRES_COMMAND_OK
-  PQclear(res)
-
-proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
-  tags: [FReadDB, FWriteDb].} =
-  ## executes the query and raises EDB if not successful.
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
-  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  PQclear(res)
-  
-proc newRow(L: int): TRow =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-  
-proc setupQuery(db: TDbConn, query: TSqlQuery, 
-                args: varargs[string]): PPGresult = 
-  var q = dbFormat(query, args)
-  result = PQExec(db, q)
-  if PQresultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-  
-proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
-  for col in 0..cols-1:
-    setLen(r[col], 0)
-    var x = PQgetvalue(res, line, col)
-    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 Postgres it is safe though.
-  var res = setupQuery(db, query, args)
-  var L = PQnfields(res)
-  var result = newRow(L)
-  for i in 0..PQntuples(res)-1:
-    setRow(res, result, i, L)
-    yield result
-  PQclear(res)
-
-proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow {.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.
-  var res = setupQuery(db, query, 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].} =
-  ## 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].} =
-  ## 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].} = 
-  ## 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, 
-                  args: varargs[string, `$`]): int64  {.tags: [FWriteDb].}=
-  ## 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"), 
-    args), 0, 0)
-  if not isNil(x):
-    result = ParseBiggestInt($x)
-  else:
-    result = -1
-
-proc InsertID*(db: TDbConn, query: TSqlQuery, 
-               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``. 
-  result = TryInsertID(db, query, args)
-  if result < 0: dbError(db)
-  
-proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 {.tags: [
-                       FReadDB, FWriteDb].} = 
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  var q = dbFormat(query, args)
-  var res = PQExec(db, q)
-  if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($PQcmdTuples(res))
-  PQclear(res)
-
-proc Close*(db: TDbConn) {.tags: [FDb].} = 
-  ## closes the database connection.
-  if db != nil: PQfinish(db)
-
-proc Open*(connection, user, password, database: string): TDbConn {.
-  tags: [FDb].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
-  if PQStatus(result) != CONNECTION_OK: dbError(result) # result = nil
-
-
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
deleted file mode 100755
index 693077553..000000000
--- a/lib/impure/db_sqlite.nim
+++ /dev/null
@@ -1,204 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `SQLite`:idx: database wrapper. This interface 
-## is implemented for other databases too.
-
-import strutils, sqlite3
-
-type
-  TDbConn* = PSqlite3  ## encapsulates a database connection
-  TRow* = seq[string]  ## a row of a dataset. NULL database values will be
-                       ## transformed always to the empty string.
-  EDb* = object of EIO ## exception that is raised if a database error occurs
-  
-  TSqlQuery* = distinct string ## an SQL query string
-  
-  FDb* = object of FIO ## 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 
-  ## 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 
-  ## on, later versions will check the string for valid syntax.
-  result = TSqlQuery(query)
- 
-proc dbError(db: TDbConn) {.noreturn.} = 
-  ## raises an EDb exception.
-  var e: ref EDb
-  new(e)
-  e.msg = $sqlite3.errmsg(db)
-  raise e
-
-proc dbError*(msg: string) {.noreturn.} = 
-  ## raises an EDb exception with message `msg`.
-  var e: ref EDb
-  new(e)
-  e.msg = msg
-  raise e
-
-proc dbQuote(s: string): string =
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else: 
-      add(result, c)
-  
-proc TryExec*(db: TDbConn, query: TSqlQuery, 
-              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var q = dbFormat(query, args)
-  var stmt: sqlite3.PStmt
-  if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
-    if step(stmt) == SQLITE_DONE:
-      result = finalize(stmt) == SQLITE_OK
-
-proc Exec*(db: TDbConn, query: TSqlQuery, 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 =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-  
-proc setupQuery(db: TDbConn, query: TSqlQuery, 
-                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) =
-  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.
-  var stmt = setupQuery(db, query, args)
-  var L = (columnCount(stmt))
-  var result = newRow(L)
-  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].} =
-  ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a TRow with empty strings for each column.
-  var stmt = setupQuery(db, query, args)
-  var L = (columnCount(stmt))
-  result = newRow(L)
-  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].} =
-  ## 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].} =
-  ## 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].} = 
-  ## 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: 
-      result = ""
-    else:
-      result = newStringOfCap(cb)
-      add(result, column_text(stmt, 0))
-    if finalize(stmt) != SQLITE_OK: dbError(db)
-  else:
-    result = ""
-  
-proc TryInsertID*(db: TDbConn, query: TSqlQuery, 
-                  args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
-  ## 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
-  if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
-    if step(stmt) == SQLITE_DONE:
-      if finalize(stmt) == SQLITE_OK:
-        return last_insert_rowid(db)
-  result = -1
-
-proc InsertID*(db: TDbConn, query: TSqlQuery, 
-               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``. 
-  result = TryInsertID(db, query, args)
-  if result < 0: dbError(db)
-  
-proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 {.
-                       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].} = 
-  ## closes the database connection.
-  if sqlite3.close(db) != SQLITE_OK: dbError(db)
-    
-proc Open*(connection, user, password, database: string): TDbConn {.
-  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
-  if sqlite3.open(connection, db) == SQLITE_OK:
-    result = db
-  else:
-    dbError(db)
-   
-when 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)", [])
-  exec(db, sql"insert into tbl1 values('goodbye', 20)", [])
-  #db.query("create table tbl1(one varchar(10), two smallint)")
-  #db.query("insert into tbl1 values('hello!',10)")
-  #db.query("insert into tbl1 values('goodbye', 20)")
-  for r in db.rows(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 100755
index 5bd7bf6f6..000000000
--- a/lib/impure/dialogs.nim
+++ /dev/null
@@ -1,226 +0,0 @@
-#
-#
-#            Nimrod'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 Nimrod; 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 100755
index 0ec2d3903..000000000
--- a/lib/impure/graphics.nim
+++ /dev/null
@@ -1,567 +0,0 @@
-#
-#
-#            Nimrod'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 Nimrod; 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 EIO
-
-  TFont {.pure, final.} = object
-    f: sdl_ttf.PFont
-    color: SDL.TColor
-  PFont* = ref TFont ## represents a font
-
-proc toSdlColor*(c: TColor): 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: TColor, 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(EIO, "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(EIO, "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.TColor(video[y * pitch + x])
-
-const
-  ColSize = 4
-
-proc getPixel(sur: PSurface, x, y: Natural): colors.TColor {.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.TColor) {.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): TColor =
-  ## get pixel at position `p`. No range checking is done!
-  result = getPixel(sur, p.x, p.y)
-
-proc `[]`*(sur: PSurface, x, y: int): TColor =
-  ## get pixel at position ``(x, y)``. No range checking is done!
-  result = getPixel(sur, x, y)
-
-proc `[]=`*(sur: PSurface, p: TPoint, col: TColor) =
-  ## 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: TColor) =
-  ## 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: TColor, 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: TColor) =
-  ## 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: TColor) =
-  ## 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: TColor) =
-  ## 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: TColor) =
-  ## 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: TColor) =
-  ## 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: TColor) =
-  ## 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 - 1)
-    var minH = min(sur.s.h - r.y, r.height - 1)
-    
-    # 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: TColor) =
-  ## 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: TColor) =
-  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: TColor) =
-  ## 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: Natural
-    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: TColor) =
-  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: TColor) =
-  ## 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: TColor): 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: TColor) =
-  ## 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.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")
-      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:
-      #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..39d238055
--- /dev/null
+++ b/lib/impure/nre.nim
@@ -0,0 +1,751 @@
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(js):
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
+
+## What is NRE?
+## ============
+##
+## A regular expression library for Nim using PCRE to do the hard work.
+##
+## For documentation on how to write patterns, there exists `the official PCRE
+## pattern documentation
+## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_. You can also
+## search the internet for a wide variety of third-party documentation and
+## tools.
+##
+## .. warning:: If you love `sequtils.toSeq` we have bad news for you. This
+##   library doesn't work with it due to documented compiler limitations. As
+##   a workaround, use this:
+runnableExamples:
+  # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`:
+  import std/sequtils
+  iterator iota(n: int): int =
+    for i in 0..<n: yield i
+  assert sequtils.toSeq(iota(3)) == @[0, 1, 2]
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
+## Licencing
+## ---------
+##
+## PCRE has `some additional terms`_ that you must agree to in order to use
+## this module.
+##
+## .. _`some additional terms`: http://pcre.sourceforge.net/license.txt
+runnableExamples:
+  import std/sugar
+  let vowels = re"[aeoui]"
+  let bounds = collect:
+    for match in "moiga".findIter(vowels): match.matchBounds
+  assert bounds == @[1 .. 1, 2 .. 2, 4 .. 4]
+  from std/sequtils import toSeq
+  let s = sequtils.toSeq("moiga".findIter(vowels))
+    # fully qualified to avoid confusion with nre.toSeq
+  assert s.len == 3
+
+  let firstVowel = "foo".find(vowels)
+  let hasVowel = firstVowel.isSome()
+  assert hasVowel
+  let matchBounds = firstVowel.get().captureBounds[-1]
+  assert matchBounds.a == 1
+
+  # as with module `re`, unless specified otherwise, `start` parameter in each
+  # proc indicates where the scan starts, but outputs are relative to the start
+  # of the input string, not to `start`:
+  assert find("uxabc", re"(?<=x|y)ab", start = 1).get.captures[-1] == "ab"
+  assert find("uxabc", re"ab", start = 3).isNone
+
+from std/pcre import nil
+import nre/private/util
+import std/tables
+from std/strutils import `%`
+import std/options
+from std/unicode import runeLenAt
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+export options
+
+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. For details on how
+    ##     to write a pattern, please see `the official PCRE pattern
+    ##     documentation.
+    ##     <https://www.pcre.org/original/doc/html/pcrepattern.html>`_
+    ##
+    ## `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>`_.
+    ##
+    ## Some of these options are not part of PCRE and are converted by nre
+    ## into PCRE flags. These include `NEVER_UTF`, `ANCHORED`,
+    ## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`,
+    ## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you
+    ## will need to pass these as separate flags to PCRE.
+    pattern*: string
+    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. See examples for `match`.
+    ##
+    ## `captureBounds[]: HSlice[int, 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.  See examples for `match`.
+    ##
+    ## `match: string`
+    ## :   the full text of the match.
+    ##
+    ## `matchBounds: HSlice[int, 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.
+    pcreMatchBounds: seq[HSlice[cint, 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 CatchableError
+
+  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 fails
+    ## for whatever reason. The message contains the error
+    ## code.
+
+proc destroyRegex(pattern: Regex) =
+  `=destroy`(pattern.pattern)
+  pcre.free_substring(cast[cstring](pattern.pcreObj))
+  if pattern.pcreExtra != nil:
+    pcre.free_study(pattern.pcreExtra)
+  `=destroy`(pattern.captureNameToId)
+
+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(FieldDefect, "Invalid getinfo for $1, errno $2" % [$opt, $retcode])
+
+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:
+    var options: cint = 0
+    var hasJit: cint
+    if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
+      if hasJit == 1'i32:
+        options = pcre.STUDY_JIT_COMPILE
+    result.pcreExtra = pcre.study(result.pcreObj, options, addr errorMsg)
+    if errorMsg != nil:
+      raise StudyError(msg: $errorMsg)
+
+  result.captureNameToId = result.getNameToNumberTable()
+
+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
+
+
+func captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
+
+func captures*(pattern: RegexMatch): Captures = return Captures(pattern)
+
+func contains*(pattern: CaptureBounds, i: int): bool =
+  let pattern = RegexMatch(pattern)
+  pattern.pcreMatchBounds[i + 1].a != -1
+
+func contains*(pattern: Captures, i: int): bool =
+  i in CaptureBounds(pattern)
+
+func `[]`*(pattern: CaptureBounds, i: int): HSlice[int, int] =
+  let pattern = RegexMatch(pattern)
+  if not (i in pattern.captureBounds):
+    raise newException(IndexDefect, "Group '" & $i & "' was not captured")
+
+  let bounds = pattern.pcreMatchBounds[i + 1]
+  int(bounds.a)..int(bounds.b-1)
+
+func `[]`*(pattern: Captures, i: int): string =
+  let pattern = RegexMatch(pattern)
+  let bounds = pattern.captureBounds[i]
+
+  pattern.str.substr(bounds.a, bounds.b)
+
+func match*(pattern: RegexMatch): string =
+  return pattern.captures[-1]
+
+func matchBounds*(pattern: RegexMatch): HSlice[int, int] =
+  return pattern.captureBounds[-1]
+
+func contains*(pattern: CaptureBounds, name: string): bool =
+  let pattern = RegexMatch(pattern)
+  let nameToId = pattern.pattern.captureNameToId
+  if not (name in nameToId):
+      return false
+  nameToId[name] in pattern.captureBounds
+
+func contains*(pattern: Captures, name: string): bool =
+  name in CaptureBounds(pattern)
+
+func checkNamedCaptured(pattern: RegexMatch, name: string) =
+  if not (name in pattern.captureBounds):
+    raise newException(KeyError, "Group '" & name & "' was not captured")
+
+func `[]`*(pattern: CaptureBounds, name: string): HSlice[int, int] =
+  let pattern = RegexMatch(pattern)
+  checkNamedCaptured(pattern, name)
+  {.noSideEffect.}:
+    result = pattern.captureBounds[pattern.pattern.captureNameToId[name]]
+
+func `[]`*(pattern: Captures, name: string): string =
+  let pattern = RegexMatch(pattern)
+  checkNamedCaptured(pattern, name)
+  {.noSideEffect.}:
+    result = pattern.captures[pattern.pattern.captureNameToId[name]]
+
+template toTableImpl() {.dirty.} =
+  for key in RegexMatch(pattern).pattern.captureNameId.keys:
+    if key in pattern:
+      result[key] = pattern[key]
+
+func toTable*(pattern: Captures): Table[string, string] =
+  result = initTable[string, string]()
+  toTableImpl()
+
+func toTable*(pattern: CaptureBounds): Table[string, HSlice[int, int]] =
+  result = initTable[string, HSlice[int, int]]()
+  toTableImpl()
+
+template itemsImpl() {.dirty.} =
+  for i in 0 ..< RegexMatch(pattern).pattern.captureCount:
+    # done in this roundabout way to avoid multiple yields (potential code
+    # bloat)
+    let nextYieldVal = if i in pattern:
+      some(pattern[i])
+    else:
+      default
+
+    yield nextYieldVal
+
+iterator items*(pattern: CaptureBounds,
+                default = none(HSlice[int, int])): Option[HSlice[int, int]] =
+  itemsImpl()
+
+iterator items*(pattern: Captures,
+                default: Option[string] = none(string)): Option[string] =
+  itemsImpl()
+
+proc toSeq*(pattern: CaptureBounds,
+            default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
+  result = @[]
+  for it in pattern.items(default): result.add it
+
+proc toSeq*(pattern: Captures,
+            default: Option[string] = none(string)): seq[Option[string]] =
+  result = @[]
+  for it in pattern.items(default): result.add it
+
+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
+
+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]
+
+proc re*(pattern: string): Regex =
+  let (pattern, flags, study) = extractOptions(pattern)
+  initRegex(pattern, flags, study)
+
+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
+  # plus 1 because we need the ceiling, not the floor
+  myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]]((vecsize + 1) div 2)
+  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(AccessViolationDefect, "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(...)<#find,string,Regex,int>`_, but anchored to the start of the
+  ## string.
+  runnableExamples:
+    assert "foo".match(re"f").isSome
+    assert "foo".match(re"o").isNone
+
+    assert "abc".match(re"(\w)").get.captures[0] == "a"
+    assert "abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"
+    assert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
+
+    assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
+    assert 0 in "abc".match(re"(\w)").get.captureBounds
+    assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
+    assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
+  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(...)<#find,string,Regex,int>`_, but finds every
+  ## non-overlapping match:
+  runnableExamples:
+    import std/sugar
+    assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"]
+     # not @["22", "22", "22"]
+  ## Arguments are the same as `find(...)<#find,string,Regex,int>`_
+  ##
+  ## Variants:
+  ##
+  ## -  `proc findAll(...)` returns a `seq[string]`
+  # see pcredemo for explanation => https://www.pcre.org/original/doc/html/pcredemo.html
+  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]
+  var neverMatched = true
+
+  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 - we also need to bail
+      # if we've never matched and we've already tried to...
+      if flags == 0 or offset >= strlen or neverMatched: # All matches found
+        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:
+      neverMatched = false
+      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 contains*(str: string, pattern: Regex, start = 0, endpos = int.high): bool =
+  ## Determine if the string contains the given pattern between the end and
+  ## start positions:
+  ## This function is equivalent to `isSome(str.find(pattern, start, endpos))`.
+  runnableExamples:
+    assert "abc".contains(re"bc")
+    assert not "abc".contains(re"cd")
+    assert not "abc".contains(re"a", start = 1)
+
+  return isSome(str.find(pattern, start, endpos))
+
+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.
+  ##
+  ## `start` behaves the same as in `find(...)<#find,string,Regex,int>`_.
+  ##
+  runnableExamples:
+    # -  If the match is zero-width, then the string is still split:
+    assert "123".split(re"") == @["1", "2", "3"]
+
+    # -  If the pattern has a capture in it, it is added after the string
+    #    split:
+    assert "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.
+    assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]
+
+  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
+      if cap.isSome:
+        result.add(cap.get)
+
+    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: untyped) {.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
+    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 `subproc`, which should
+  ## never be or return `nil`.
+  ##
+  ## If `subproc` is a `proc (RegexMatch): string`, then it is executed with
+  ## each match and the return value is the replacement value.
+  ##
+  ## If `subproc` is a `proc (string): string`, then it is executed with the
+  ## full text of the match and the return value is the replacement value.
+  ##
+  ## If `subproc` 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, `IndexDefect` thrown for un-named captures
+  ## and `KeyError` for named captures.
+  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]))
+
+proc escapeRe*(str: string): string {.gcsafe.} =
+  ## Escapes the string so it doesn't match any special characters.
+  ## Incompatible with the Extra flag (`X`).
+  ##
+  ## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -`
+  runnableExamples:
+    assert escapeRe("fly+wind") == "fly\\+wind"
+    assert escapeRe("!") == "\\!"
+    assert escapeRe("nim*") == "nim\\*"
+
+  #([\\+*?[^\]$(){}=!<>|:-])
+  const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(',
+                              ')', '{', '}', '=', '!', '<', '>', '|', ':',
+                              '-'}
+
+  for c in items(str):
+    case c
+    of SpecialCharMatcher:
+      result.add("\\")
+      result.add(c)
+    else:
+      result.add(c)
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..ed8420776
--- /dev/null
+++ b/lib/impure/nre/private/util.nim
@@ -0,0 +1,51 @@
+## INTERNAL FILE FOR USE ONLY BY nre.nim.
+import std/tables
+
+const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
+const StartIdent = Ident - {'0'..'9'}
+
+template formatStr*(howExpr, namegetter, idgetter): untyped =
+  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(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(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(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(namegetter)
+      else:
+        raise newException(ValueError, "Syntax error in format string at " & $i)
+  val
diff --git a/lib/impure/osinfo_posix.nim b/lib/impure/osinfo_posix.nim
deleted file mode 100755
index 4fde82012..000000000
--- a/lib/impure/osinfo_posix.nim
+++ /dev/null
@@ -1,68 +0,0 @@
-import posix, strutils, os
-
-type
-  Tstatfs {.importc: "struct statfs64", 
-            header: "<sys/statfs.h>", final, pure.} = object
-    f_type: int
-    f_bsize: int
-    f_blocks: int
-    f_bfree: int
-    f_bavail: int
-    f_files: int
-    f_ffree: int
-    f_fsid: int
-    f_namelen: int
-
-proc statfs(path: string, buf: var Tstatfs): int {.
-  importc, header: "<sys/vfs.h>".}
-
-
-proc getSystemVersion*(): string =
-  result = ""
-  
-  var unix_info: TUtsname
-  
-  if uname(unix_info) != 0:
-    os.OSError()
-  
-  if $unix_info.sysname == "Linux":
-    # Linux
-    result.add("Linux ")
-
-    result.add($unix_info.release & " ")
-    result.add($unix_info.machine)
-  elif $unix_info.sysname == "Darwin":
-    # Darwin
-    result.add("Mac OS X ")
-    if "10" in $unix_info.release:
-      result.add("v10.6 Snow Leopard")
-    elif "9" in $unix_info.release:
-      result.add("v10.5 Leopard")
-    elif "8" in $unix_info.release:
-      result.add("v10.4 Tiger")
-    elif "7" in $unix_info.release:
-      result.add("v10.3 Panther")
-    elif "6" in $unix_info.release:
-      result.add("v10.2 Jaguar")
-    elif "1.4" in $unix_info.release:
-      result.add("v10.1 Puma")
-    elif "1.3" in $unix_info.release:
-      result.add("v10.0 Cheetah")
-    elif "0" in $unix_info.release:
-      result.add("Server 1.0 Hera")
-  else:
-    result.add($unix_info.sysname & " " & $unix_info.release)
-    
-    
-when false:
-  var unix_info: TUtsname
-  echo(uname(unix_info))
-  echo(unix_info.sysname)
-  echo("8" in $unix_info.release)
-
-  echo(getSystemVersion())
-
-  var stfs: TStatfs
-  echo(statfs("sysinfo_posix.nim", stfs))
-  echo(stfs.f_files)
-  
diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim
deleted file mode 100755
index 46af1ddd6..000000000
--- a/lib/impure/osinfo_win.nim
+++ /dev/null
@@ -1,405 +0,0 @@
-# XXX clean up this mess!
-
-import winlean
-
-const
-  INVALID_HANDLE_VALUE = int(- 1) # GetStockObject
-
-type
-  TMEMORYSTATUSEX {.final, pure.} = object
-    dwLength: int32
-    dwMemoryLoad: int32
-    ullTotalPhys: int64
-    ullAvailPhys: int64
-    ullTotalPageFile: int64
-    ullAvailPageFile: int64
-    ullTotalVirtual: int64
-    ullAvailVirtual: int64
-    ullAvailExtendedVirtual: int64
-    
-  SYSTEM_INFO* {.final, pure.} = object
-    wProcessorArchitecture*: int16
-    wReserved*: int16
-    dwPageSize*: int32
-    lpMinimumApplicationAddress*: pointer
-    lpMaximumApplicationAddress*: pointer
-    dwActiveProcessorMask*: int32
-    dwNumberOfProcessors*: int32
-    dwProcessorType*: int32
-    dwAllocationGranularity*: int32
-    wProcessorLevel*: int16
-    wProcessorRevision*: int16
-
-  LPSYSTEM_INFO* = ptr SYSTEM_INFO
-  TSYSTEMINFO* = SYSTEM_INFO
-
-  TMemoryInfo* = object
-    MemoryLoad*: int ## occupied memory, in percent
-    TotalPhysMem*: int64 ## Total Physical memory, in bytes
-    AvailablePhysMem*: int64 ## Available physical memory, in bytes
-    TotalPageFile*: int64 ## The current committed memory limit 
-                          ## for the system or the current process, whichever is smaller, in bytes.
-    AvailablePageFile*: int64 ## The maximum amount of memory the current process can commit, in bytes.
-    TotalVirtualMem*: int64 ## Total virtual memory, in bytes
-    AvailableVirtualMem*: int64 ## Available virtual memory, in bytes
-    
-  TOSVERSIONINFOEX {.final, pure.} = object
-    dwOSVersionInfoSize: int32
-    dwMajorVersion: int32
-    dwMinorVersion: int32
-    dwBuildNumber: int32
-    dwPlatformId: int32
-    szCSDVersion: array[0..127, char]
-    wServicePackMajor: int16
-    wServicePackMinor: int16
-    wSuiteMask: int16
-    wProductType: int8
-    wReserved: char
-    
-  TVersionInfo* = object
-    majorVersion*: int
-    minorVersion*: int
-    buildNumber*: int
-    platformID*: int
-    SPVersion*: string ## Full Service pack version string
-    SPMajor*: int ## Major service pack version
-    SPMinor*: int ## Minor service pack version
-    SuiteMask*: int
-    ProductType*: int
-    
-  TPartitionInfo* = tuple[FreeSpace, TotalSpace: Tfiletime]
-  
-const
-  # SuiteMask - VersionInfo.SuiteMask
-  VER_SUITE_BACKOFFICE* = 0x00000004
-  VER_SUITE_BLADE* = 0x00000400
-  VER_SUITE_COMPUTE_SERVER* = 0x00004000
-  VER_SUITE_DATACENTER* = 0x00000080
-  VER_SUITE_ENTERPRISE* = 0x00000002
-  VER_SUITE_EMBEDDEDNT* = 0x00000040
-  VER_SUITE_PERSONAL* = 0x00000200
-  VER_SUITE_SINGLEUSERTS* = 0x00000100
-  VER_SUITE_SMALLBUSINESS* = 0x00000001
-  VER_SUITE_SMALLBUSINESS_RESTRICTED* = 0x00000020
-  VER_SUITE_STORAGE_SERVER* = 0x00002000
-  VER_SUITE_TERMINAL* = 0x00000010
-  VER_SUITE_WH_SERVER* = 0x00008000
-
-  # ProductType - VersionInfo.ProductType
-  VER_NT_DOMAIN_CONTROLLER* = 0x0000002
-  VER_NT_SERVER* = 0x0000003
-  VER_NT_WORKSTATION* = 0x0000001
-  
-  VER_PLATFORM_WIN32_NT* = 2
-  
-  # Product Info - getProductInfo() - (Remove unused ones ?)
-  PRODUCT_BUSINESS* = 0x00000006
-  PRODUCT_BUSINESS_N* = 0x00000010
-  PRODUCT_CLUSTER_SERVER* = 0x00000012
-  PRODUCT_DATACENTER_SERVER* = 0x00000008
-  PRODUCT_DATACENTER_SERVER_CORE* = 0x0000000C
-  PRODUCT_DATACENTER_SERVER_CORE_V* = 0x00000027
-  PRODUCT_DATACENTER_SERVER_V* = 0x00000025
-  PRODUCT_ENTERPRISE* = 0x00000004
-  PRODUCT_ENTERPRISE_E* = 0x00000046
-  PRODUCT_ENTERPRISE_N* = 0x0000001B
-  PRODUCT_ENTERPRISE_SERVER* = 0x0000000A
-  PRODUCT_ENTERPRISE_SERVER_CORE* = 0x0000000E
-  PRODUCT_ENTERPRISE_SERVER_CORE_V* = 0x00000029
-  PRODUCT_ENTERPRISE_SERVER_IA64* = 0x0000000F
-  PRODUCT_ENTERPRISE_SERVER_V* = 0x00000026
-  PRODUCT_HOME_BASIC* = 0x00000002
-  PRODUCT_HOME_BASIC_E* = 0x00000043
-  PRODUCT_HOME_BASIC_N* = 0x00000005
-  PRODUCT_HOME_PREMIUM* = 0x00000003
-  PRODUCT_HOME_PREMIUM_E* = 0x00000044
-  PRODUCT_HOME_PREMIUM_N* = 0x0000001A
-  PRODUCT_HYPERV* = 0x0000002A
-  PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT* = 0x0000001E
-  PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING* = 0x00000020
-  PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY* = 0x0000001F
-  PRODUCT_PROFESSIONAL* = 0x00000030
-  PRODUCT_PROFESSIONAL_E* = 0x00000045
-  PRODUCT_PROFESSIONAL_N* = 0x00000031
-  PRODUCT_SERVER_FOR_SMALLBUSINESS* = 0x00000018
-  PRODUCT_SERVER_FOR_SMALLBUSINESS_V* = 0x00000023
-  PRODUCT_SERVER_FOUNDATION* = 0x00000021
-  PRODUCT_SMALLBUSINESS_SERVER* = 0x00000009
-  PRODUCT_STANDARD_SERVER* = 0x00000007
-  PRODUCT_STANDARD_SERVER_CORE * = 0x0000000D
-  PRODUCT_STANDARD_SERVER_CORE_V* = 0x00000028
-  PRODUCT_STANDARD_SERVER_V* = 0x00000024
-  PRODUCT_STARTER* = 0x0000000B
-  PRODUCT_STARTER_E* = 0x00000042
-  PRODUCT_STARTER_N* = 0x0000002F
-  PRODUCT_STORAGE_ENTERPRISE_SERVER* = 0x00000017
-  PRODUCT_STORAGE_EXPRESS_SERVER* = 0x00000014
-  PRODUCT_STORAGE_STANDARD_SERVER* = 0x00000015
-  PRODUCT_STORAGE_WORKGROUP_SERVER* = 0x00000016
-  PRODUCT_UNDEFINED* = 0x00000000
-  PRODUCT_ULTIMATE* = 0x00000001
-  PRODUCT_ULTIMATE_E* = 0x00000047
-  PRODUCT_ULTIMATE_N* = 0x0000001C
-  PRODUCT_WEB_SERVER* = 0x00000011
-  PRODUCT_WEB_SERVER_CORE* = 0x0000001D
-  
-  PROCESSOR_ARCHITECTURE_AMD64* = 9 ## x64 (AMD or Intel)
-  PROCESSOR_ARCHITECTURE_IA64* = 6 ## Intel Itanium Processor Family (IPF)
-  PROCESSOR_ARCHITECTURE_INTEL* = 0 ## x86
-  PROCESSOR_ARCHITECTURE_UNKNOWN* = 0xffff ## Unknown architecture.
-  
-  # GetSystemMetrics
-  SM_SERVERR2 = 89 
-  
-proc GlobalMemoryStatusEx*(lpBuffer: var TMEMORYSTATUSEX){.stdcall, dynlib: "kernel32",
-    importc: "GlobalMemoryStatusEx".}
-    
-proc getMemoryInfo*(): TMemoryInfo =
-  ## Retrieves memory info
-  var statex: TMEMORYSTATUSEX
-  statex.dwLength = sizeof(statex).int32
-
-  GlobalMemoryStatusEx(statex)
-  result.MemoryLoad = statex.dwMemoryLoad
-  result.TotalPhysMem = statex.ullTotalPhys
-  result.AvailablePhysMem = statex.ullAvailPhys
-  result.TotalPageFile = statex.ullTotalPageFile
-  result.AvailablePageFile = statex.ullAvailPageFile
-  result.TotalVirtualMem = statex.ullTotalVirtual
-  result.AvailableVirtualMem = statex.ullAvailExtendedVirtual
-
-proc GetVersionEx*(lpVersionInformation: var TOSVERSIONINFOEX): WINBOOL{.stdcall,
-    dynlib: "kernel32", importc: "GetVersionExA".}
-
-proc GetProcAddress*(hModule: int, lpProcName: cstring): pointer{.stdcall,
-    dynlib: "kernel32", importc: "GetProcAddress".}
-
-proc GetModuleHandleA*(lpModuleName: cstring): int{.stdcall,
-     dynlib: "kernel32", importc.}
-
-proc getVersionInfo*(): TVersionInfo =
-  ## Retrieves operating system info
-  var osvi: TOSVERSIONINFOEX
-  osvi.dwOSVersionInfoSize = sizeof(osvi).int32
-  discard GetVersionEx(osvi)
-  result.majorVersion = osvi.dwMajorVersion
-  result.minorVersion = osvi.dwMinorVersion
-  result.buildNumber = osvi.dwBuildNumber
-  result.platformID = osvi.dwPlatformId
-  result.SPVersion = $osvi.szCSDVersion
-  result.SPMajor = osvi.wServicePackMajor
-  result.SPMinor = osvi.wServicePackMinor
-  result.SuiteMask = osvi.wSuiteMask
-  result.ProductType = osvi.wProductType
-
-proc getProductInfo*(majorVersion, minorVersion, SPMajorVersion, 
-                     SPMinorVersion: int): int =
-  ## Retrieves Windows' ProductInfo, this function only works in Vista and 7
-
-  var pGPI = cast[proc (dwOSMajorVersion, dwOSMinorVersion, 
-              dwSpMajorVersion, dwSpMinorVersion: int32, outValue: Pint32)](GetProcAddress(
-                GetModuleHandleA("kernel32.dll"), "GetProductInfo"))
-                
-  if pGPI != nil:
-    var dwType: int32
-    pGPI(int32(majorVersion), int32(minorVersion), int32(SPMajorVersion), int32(SPMinorVersion), addr(dwType))
-    result = int(dwType)
-  else:
-    return PRODUCT_UNDEFINED
-
-proc GetSystemInfo*(lpSystemInfo: LPSYSTEM_INFO){.stdcall, dynlib: "kernel32",
-    importc: "GetSystemInfo".}
-    
-proc getSystemInfo*(): TSYSTEM_INFO =
-  ## Returns the SystemInfo
-
-  # Use GetNativeSystemInfo if it's available
-  var pGNSI = cast[proc (lpSystemInfo: LPSYSTEM_INFO)](GetProcAddress(
-                GetModuleHandleA("kernel32.dll"), "GetNativeSystemInfo"))
-                
-  var systemi: TSYSTEM_INFO              
-  if pGNSI != nil:
-    pGNSI(addr(systemi))
-  else:
-    GetSystemInfo(addr(systemi))
-
-  return systemi
-
-proc GetSystemMetrics*(nIndex: int32): int32{.stdcall, dynlib: "user32",
-    importc: "GetSystemMetrics".}
-
-proc `$`*(osvi: TVersionInfo): string =
-  ## Turns a VersionInfo object, into a string
-
-  if osvi.platformID == VER_PLATFORM_WIN32_NT and osvi.majorVersion > 4:
-    result = "Microsoft "
-    
-    var si = getSystemInfo()
-    # Test for the specific product
-    if osvi.majorVersion == 6:
-      if osvi.minorVersion == 0:
-        if osvi.ProductType == VER_NT_WORKSTATION:
-          result.add("Windows Vista ")
-        else: result.add("Windows Server 2008 ")
-      elif osvi.minorVersion == 1:
-        if osvi.ProductType == VER_NT_WORKSTATION:
-          result.add("Windows 7 ")
-        else: result.add("Windows Server 2008 R2 ")
-    
-      var dwType = getProductInfo(osvi.majorVersion, osvi.minorVersion, 0, 0)
-      case dwType
-      of PRODUCT_ULTIMATE:
-        result.add("Ultimate Edition")
-      of PRODUCT_PROFESSIONAL:
-        result.add("Professional")
-      of PRODUCT_HOME_PREMIUM:
-        result.add("Home Premium Edition")
-      of PRODUCT_HOME_BASIC:
-        result.add("Home Basic Edition")
-      of PRODUCT_ENTERPRISE:
-        result.add("Enterprise Edition")
-      of PRODUCT_BUSINESS:
-        result.add("Business Edition")
-      of PRODUCT_STARTER:
-        result.add("Starter Edition")
-      of PRODUCT_CLUSTER_SERVER:
-        result.add("Cluster Server Edition")
-      of PRODUCT_DATACENTER_SERVER:
-        result.add("Datacenter Edition")
-      of PRODUCT_DATACENTER_SERVER_CORE:
-        result.add("Datacenter Edition (core installation)")
-      of PRODUCT_ENTERPRISE_SERVER:
-        result.add("Enterprise Edition")
-      of PRODUCT_ENTERPRISE_SERVER_CORE:
-        result.add("Enterprise Edition (core installation)")
-      of PRODUCT_ENTERPRISE_SERVER_IA64:
-        result.add("Enterprise Edition for Itanium-based Systems")
-      of PRODUCT_SMALLBUSINESS_SERVER:
-        result.add("Small Business Server")
-      of PRODUCT_STANDARD_SERVER:
-        result.add("Standard Edition")
-      of PRODUCT_STANDARD_SERVER_CORE:
-        result.add("Standard Edition (core installation)")
-      of PRODUCT_WEB_SERVER:
-        result.add("Web Server Edition")
-      else:
-        nil
-    # End of Windows 6.*
-
-    if osvi.majorVersion == 5 and osvi.minorVersion == 2:
-      if GetSystemMetrics(SM_SERVERR2) != 0:
-        result.add("Windows Server 2003 R2, ")
-      elif (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0: # Not sure if this will work
-        result.add("Windows Storage Server 2003")
-      elif (osvi.SuiteMask and VER_SUITE_WH_SERVER) != 0:
-        result.add("Windows Home Server")
-      elif osvi.ProductType == VER_NT_WORKSTATION and 
-          si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64:
-        result.add("Windows XP Professional x64 Edition")
-      else:
-        result.add("Windows Server 2003, ")
-      
-      # Test for the specific product
-      if osvi.ProductType != VER_NT_WORKSTATION:
-        if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_IA64:
-          if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0:
-            result.add("Datacenter Edition for Itanium-based Systems")
-          elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0:
-            result.add("Enterprise Edition for Itanium-based Systems")
-        elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64:
-          if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0:
-            result.add("Datacenter x64 Edition")
-          elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0:
-            result.add("Enterprise x64 Edition")
-          else:
-            result.add("Standard x64 Edition")
-        else:
-          if (osvi.SuiteMask and VER_SUITE_COMPUTE_SERVER) != 0:
-            result.add("Compute Cluster Edition")
-          elif (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0:
-            result.add("Datacenter Edition")
-          elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0:
-            result.add("Enterprise Edition")
-          elif (osvi.SuiteMask and VER_SUITE_BLADE) != 0:
-            result.add("Web Edition")
-          else:
-            result.add("Standard Edition")
-    # End of 5.2
-    
-    if osvi.majorVersion == 5 and osvi.minorVersion == 1:
-      result.add("Windows XP ")
-      if (osvi.SuiteMask and VER_SUITE_PERSONAL) != 0:
-        result.add("Home Edition")
-      else:
-        result.add("Professional")
-    # End of 5.1
-    
-    if osvi.majorVersion == 5 and osvi.minorVersion == 0:
-      result.add("Windows 2000 ")
-      if osvi.ProductType == VER_NT_WORKSTATION:
-        result.add("Professional")
-      else:
-        if (osvi.SuiteMask and VER_SUITE_DATACENTER) != 0:
-          result.add("Datacenter Server")
-        elif (osvi.SuiteMask and VER_SUITE_ENTERPRISE) != 0:
-          result.add("Advanced Server")
-        else:
-          result.add("Server")
-    # End of 5.0
-    
-    # Include service pack (if any) and build number.
-    if len(osvi.SPVersion) > 0:
-      result.add(" ")
-      result.add(osvi.SPVersion)
-    
-    result.add(" (build " & $osvi.buildNumber & ")")
-    
-    if osvi.majorVersion >= 6:
-      if ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_AMD64:
-        result.add(", 64-bit")
-      elif ze(si.wProcessorArchitecture) == PROCESSOR_ARCHITECTURE_INTEL:
-        result.add(", 32-bit")
-    
-  else:
-    # Windows 98 etc...
-    result = "Unknown version of windows[Kernel version <= 4]"
-    
-
-proc getFileSize*(file: string): biggestInt =
-  var fileData: TWIN32_FIND_DATA
-
-  when useWinUnicode:
-    var aa = allocWideCString(file)
-    var hFile = FindFirstFileW(aa, fileData)
-    dealloc aa
-  else:
-    var hFile = FindFirstFileA(file, fileData)
-  
-  if hFile == INVALID_HANDLE_VALUE:
-    raise newException(EIO, $GetLastError())
-  
-  return fileData.nFileSizeLow
-
-proc GetDiskFreeSpaceEx*(lpDirectoryName: cstring, lpFreeBytesAvailableToCaller,
-                         lpTotalNumberOfBytes,
-                         lpTotalNumberOfFreeBytes: var TFiletime): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "GetDiskFreeSpaceExA".}
-
-proc getPartitionInfo*(partition: string): TPartitionInfo =
-  ## Retrieves partition info, for example ``partition`` may be ``"C:\"``
-  var FreeBytes, TotalBytes, TotalFreeBytes: TFiletime 
-  var res = GetDiskFreeSpaceEx(r"C:\", FreeBytes, TotalBytes, 
-                               TotalFreeBytes)
-  return (FreeBytes, TotalBytes)
-
-when isMainModule:
-  var r = getMemoryInfo()
-  echo("Memory load: ", r.MemoryLoad, "%")
-  
-  var osvi = getVersionInfo()
-  
-  echo($osvi)
-
-  echo(getFileSize(r"osinfo_win.nim") div 1024 div 1024)
-  
-  echo(rdFileTime(getPartitionInfo(r"C:\")[0]))
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index cf076e929..f4fc26380 100755..100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -1,62 +1,74 @@
 #
 #
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    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 
-## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
-## is used. This suffices because Windows' console already provides the 
+## 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.
 
-when defined(Windows):
-  proc ReadLineFromStdin*(prompt: string): TaintedString {.
-                          tags: [FReadIO, FWriteIO].} = 
+runnableExamples("-r:off"):
+  echo readLineFromStdin("Is Nim awesome? (Y/n): ")
+  var line: string
+  while true:
+    let ok = readLineFromStdin("How are you? ", line)
+    if not ok: break # ctrl-C or ctrl-D will cause a break
+    if line.len > 0: echo line
+  echo "exiting"
+
+
+when defined(windows):
+  when defined(nimPreviewSlimSystem):
+    import std/syncio
+
+  proc readLineFromStdin*(prompt: string): string {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a line from stdin.
     stdout.write(prompt)
+    stdout.flushFile()
     result = readLine(stdin)
 
-  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
-                          tags: [FReadIO, FWriteIO].} =
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a `line` from stdin. `line` must not be
-    ## ``nil``! May throw an IO exception.
-    ## A line of text may be delimited by ``CR``, ``LF`` or
-    ## ``CRLF``. The newline character(s) are not part of the returned string.
-    ## Returns ``false`` if the end of the file has been reached, ``true``
-    ## otherwise. If ``false`` is returned `line` contains no new data.
+    ## `nil`! May throw an IO exception.
+    ## A line of text may be delimited by `CR`, `LF` or
+    ## `CRLF`. The newline character(s) are not part of the returned string.
+    ## Returns `false` if the end of the file has been reached, `true`
+    ## otherwise. If `false` is returned `line` contains no new data.
     stdout.write(prompt)
     result = readLine(stdin, line)
 
+elif defined(genode):
+  proc readLineFromStdin*(prompt: string): string {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
+    stdin.readLine()
+
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
+    stdin.readLine(line)
+
 else:
-  import readline, history
-    
-  proc ReadLineFromStdin*(prompt: string): TaintedString {.
-                          tags: [FReadIO, FWriteIO].} =
-    var buffer = readline.readLine(prompt)
-    if isNil(buffer): quit(0)
-    result = TaintedString($buffer)
-    if result.string.len > 0:
-      add_history(buffer)
-    readline.free(buffer)
-
-  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
-                          tags: [FReadIO, FWriteIO].} =
-    var buffer = readline.readLine(prompt)
-    if isNil(buffer): quit(0)
-    line = TaintedString($buffer)
-    if line.string.len > 0:
-      add_history(buffer)
-    readline.free(buffer)
-    # XXX how to determine CTRL+D?
-    result = true
+  import std/linenoise
 
-  # initialization:
-  # disable auto-complete: 
-  proc doNothing(a, b: cint): cint {.cdecl, procvar.} = nil
-  
-  discard readline.bind_key('\t'.ord, doNothing)
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
+    var buffer = linenoise.readLine(prompt)
+    if isNil(buffer):
+      line.setLen(0)
+      return false
+    line = $buffer
+    if line.len > 0:
+      historyAdd(buffer)
+    linenoise.free(buffer)
+    result = true
 
+  proc readLineFromStdin*(prompt: string): string {.inline.} =
+    if not readLineFromStdin(prompt, result):
+      raise newException(IOError, "Linenoise returned nil")
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 57dc3a313..053c6ab55 100755..100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -1,126 +1,218 @@
 #
 #
-#            Nimrod's Runtime Library
+#            Nim's Runtime Library
 #        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-## Regular expression support for Nimrod. Consider using the pegs module
-## instead.
+when defined(js):
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
+
+## Regular expression support for Nim.
+##
 ## This module is implemented by providing a wrapper around the
-## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
-## C library. This means that your application will depend on the PRCE
+## `PCRE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
+## C library. This means that your application will depend on the PCRE
 ## library's licence when using this module, which should not be a problem
 ## though.
-## PRCE's licence follows:
 ##
-## .. include:: ../doc/regexprs.txt
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
+##
+## PCRE's licence follows:
+##
+## .. include:: ../../doc/regexprs.txt
 ##
 
+runnableExamples:
+  ## Unless specified otherwise, `start` parameter in each proc indicates
+  ## where the scan starts, but outputs are relative to the start of the input
+  ## string, not to `start`:
+  doAssert find("uxabc", re"(?<=x|y)ab", start = 1) == 2 # lookbehind assertion
+  doAssert find("uxabc", re"ab", start = 3) == -1 # we're past `start` => not found
+  doAssert not match("xabc", re"^abc$", start = 1)
+    # can't match start of string since we're starting at 1
+
 import
-  pcre, strutils
+  std/[pcre, strutils, rtarrays]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 const
-  MaxSubpatterns* = 10
+  MaxSubpatterns* = 20
     ## defines the maximum number of subpatterns that can be captured.
-    ## More subpatterns cannot be captured!
+    ## This limit still exists for `replacef` and `parallelReplace`.
 
 type
-  TRegExFlag* = enum     ## options for regular expressions
-    reIgnoreCase = 0,    ## do caseless matching
-    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)
-    
-  TRegExDesc {.pure, final.}  = object 
-    h: PPcre
-    e: ptr TExtra
-    
-  TRegEx* = ref TRegExDesc ## a compiled regular expression
-    
-  EInvalidRegEx* = object of EInvalidValue
+  RegexFlag* = enum     ## options for regular expressions
+    reIgnoreCase = 0,   ## do caseless matching
+    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: ptr Pcre
+    e: ptr ExtraData
+
+  Regex* = ref RegexDesc ## a compiled regular expression
+
+  RegexError* = object of ValueError
     ## is raised if the pattern is no valid regular expression.
 
-proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} = 
-  var e: ref EInvalidRegEx
+when defined(gcDestructors):
+  when defined(nimAllowNonVarDestructor):
+    proc `=destroy`(x: RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
+  else:
+    proc `=destroy`(x: var RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
+
+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
-  result = pcre.Compile(pattern, flags, addr(msg), addr(offset), nil)
+    msg: cstring = ""
+    offset: cint = 0
+  result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil)
   if result == nil:
-    raiseInvalidRegEx($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n")
+    raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
 
-proc finalizeRegEx(x: TRegEx) = 
+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. 
+  # Sigh. The hack relies on PCRE's implementation (see `pcre_get.c`).
+  # 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))
+    pcre.free_study(x.e)
 
-proc re*(s: string, flags = {reExtended, reStudy}): TRegEx =
-  ## Constructor of regular expressions. Note that Nimrod's
-  ## extended raw string literals support this syntax ``re"[abc]"`` as
-  ## a short form for ``re(r"[abc]")``.
-  new(result, finalizeRegEx)
+proc re*(s: string, flags = {reStudy}): Regex =
+  ## Constructor of regular expressions.
+  ##
+  ## Note that Nim's
+  ## extended raw string literals support the syntax `re"[abc]"` as
+  ## a short form for `re(r"[abc]")`. Also note that since this
+  ## compiles the regular expression, which is expensive, you should
+  ## avoid putting it directly in the arguments of the functions like
+  ## the examples show below if you plan to use it a lot of times, as
+  ## this will hurt performance immensely. (e.g. outside a loop, ...)
+  when defined(gcDestructors):
+    result = Regex()
+  else:
+    new(result, finalizeRegEx)
   result.h = rawCompile(s, cast[cint](flags - {reStudy}))
   if reStudy in flags:
-    var msg: cstring
-    result.e = pcre.study(result.h, 0, msg)
+    var msg: cstring = ""
+    var options: cint = 0
+    var hasJit: cint = 0
+    if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
+      if hasJit == 1'i32:
+        options = pcre.STUDY_JIT_COMPILE
+    result.e = pcre.study(result.h, options, addr msg)
     if not isNil(msg): raiseInvalidRegex($msg)
 
-proc matchOrFind(s: string, pattern: TRegEx, matches: var openarray[string],
-                 start, flags: cint): cint =
+proc rex*(s: string, flags = {reStudy, reExtended}): Regex =
+  ## Constructor for extended regular expressions.
+  ##
+  ## The extended means that comments starting with `#` and
+  ## whitespace are ignored.
+  result = re(s, flags)
+
+proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} =
+  ## Return a Nim string built from a slice of a cstring buffer.
+  ## Don't assume cstring is '\0' terminated
+  let sz = ePos - sPos
+  result = newString(sz+1)
+  copyMem(addr(result[0]), unsafeAddr(b[sPos]), sz)
+  result.setLen(sz)
+
+proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
+                 start, bufSize, flags: cint): cint =
   var
-    rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
-    res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start, flags,
-      cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+    rtarray = initRtArray[cint]((matches.len+1)*3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize, start, flags,
+      cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return res
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32:
+      matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = ""
   return rawMatches[1] - rawMatches[0]
-  
-proc findBounds*(s: string, pattern: TRegEx, matches: var openarray[string],
-                 start = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of `pattern` in `s` 
+
+const MaxReBufSize* = high(cint)
+  ## Maximum PCRE (API 1) buffer start/size equal to `high(cint)`, which even
+  ## for 64-bit systems can be either 2`31`:sup:-1 or 2`63`:sup:-1.
+
+proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the starting position and end position of `pattern` in `buf`
+  ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated),
   ## and the captured
   ## substrings in the array `matches`. If it does not match, nothing
-  ## is written into `matches` and ``(-1,0)`` is returned.
+  ## is written into `matches` and `(-1,0)` is returned.
+  ##
+  ## Note: The memory for `matches` needs to be allocated before this function is
+  ## called, otherwise it will just remain empty.
   var
-    rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
-    res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
-      cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+    rtarray = initRtArray[cint]((matches.len+1)*3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
+      cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return (-1, 0)
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = ""
   return (rawMatches[0].int, rawMatches[1].int - 1)
-  
-proc findBounds*(s: string, pattern: TRegEx, 
-                 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`. 
+
+proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
+                 start = 0): tuple[first, last: int] {.inline.} =
+  ## 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.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    var matches = newSeq[string](1)
+    let (first, last) = findBounds("Hello World", re"(W\w+)", matches)
+    doAssert first == 6
+    doAssert last == 10
+    doAssert matches[0] == "World"
+  result = findBounds(cstring(s), pattern, matches,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
+
+proc findBounds*(buf: cstring, pattern: Regex,
+                 matches: var openArray[tuple[first, last: int]],
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the starting position and end position of `pattern` in `buf`
+  ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated),
+  ## and the captured substrings in the array `matches`.
   ## If it does not match, nothing is written into `matches` and
-  ## ``(-1,0)`` is returned.
+  ## `(-1,0)` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   var
-    rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
-    res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
-      cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+    rtarray = initRtArray[cint]((matches.len+1)*3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
+      cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return (-1, 0)
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
@@ -129,206 +221,322 @@ proc findBounds*(s: string, pattern: TRegEx,
     else: matches[i-1] = (-1,0)
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
-proc findBounds*(s: string, pattern: TRegEx, 
-                 start = 0): tuple[first, last: int] =
-  ## returns the starting position of `pattern` in `s`. If it does not
-  ## match, ``(-1,0)`` is returned.
+proc findBounds*(s: string, pattern: Regex,
+                 matches: var openArray[tuple[first, last: int]],
+                 start = 0): tuple[first, last: int] {.inline.} =
+  ## 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.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    var matches = newSeq[tuple[first, last: int]](1)
+    let (first, last) = findBounds("Hello World", re"(\w+)", matches)
+    doAssert first == 0
+    doAssert last == 4
+    doAssert matches[0] == (0, 4)
+  result = findBounds(cstring(s), pattern, matches,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
+
+proc findBoundsImpl(buf: cstring, pattern: Regex,
+                    start = 0, bufSize = 0, flags = 0): tuple[first, last: int] =
+  var rtarray = initRtArray[cint](3)
+  let rawMatches = rtarray.getRawData
+  let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags.int32,
+                cast[ptr cint](rawMatches), 3)
+
+  if res < 0'i32:
+    result = (-1, 0)
+  else:
+    result = (int(rawMatches[0]), int(rawMatches[1]-1))
+
+proc findBounds*(buf: cstring, pattern: Regex,
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the `first` and `last` position of `pattern` in `buf`,
+  ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ## If it does not match, `(-1,0)` is returned.
   var
-    rawMatches: array[0..3 - 1, cint]
-    res = pcre.Exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32,
-      cast[ptr cint](addr(rawMatches)), 3)
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
+      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: TRegEx, start, flags: cint): cint =
-  var rawMatches: array [0..maxSubpatterns * 3 - 1, cint]
-  result = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start, flags,
-                    cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+
+proc findBounds*(s: string, pattern: Regex,
+                 start = 0): tuple[first, last: int] {.inline.} =
+  ## returns the `first` and `last` position of `pattern` in `s`.
+  ## If it does not match, `(-1,0)` is returned.
+  ##
+  ## Note: there is a speed improvement if the matches do not need to be captured.
+  runnableExamples:
+    assert findBounds("01234abc89", re"abc") == (5,7)
+  result = findBounds(cstring(s), pattern,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
+
+proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint): cint =
+  var
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
+  result = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags,
+                    cast[ptr cint](rawMatches), 3)
   if result >= 0'i32:
     result = rawMatches[1] - rawMatches[0]
 
-proc match*(s: string, pattern: TRegEx, matches: var openarray[string],
-           start = 0): bool =
-  ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
-  ## the captured substrings in the array ``matches``. If it does not
-  ## match, nothing is written into ``matches`` and ``false`` is
-  ## returned.
-  return matchOrFind(s, pattern, matches, start.cint, 
-                     pcre.ANCHORED) == cint(s.len - start)
+proc matchLen*(s: string, pattern: Regex, matches: var openArray[string],
+              start = 0): int {.inline.} =
+  ## 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.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED)
+
+proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string],
+              start = 0, bufSize: int): int {.inline.} =
+  ## 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.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  return matchOrFind(buf, pattern, matches, start.cint, bufSize.cint, pcre.ANCHORED)
 
-proc match*(s: string, pattern: TRegEx, start = 0): bool =
-  ## returns ``true`` if ``s[start..]`` matches the ``pattern``.
-  return matchOrFind(s, pattern, start.cint, pcre.ANCHORED) == cint(s.len-start)
+proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} =
+  ## 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.
+  ##
+  runnableExamples:
+    doAssert matchLen("abcdefg", re"cde", 2) == 3
+    doAssert matchLen("abcdefg", re"abcde") == 5
+    doAssert matchLen("abcdefg", re"cde") == -1
+  result = matchOrFind(cstring(s), pattern, start.cint, s.len.cint, pcre.ANCHORED)
 
-proc matchLen*(s: string, pattern: TRegEx, matches: var openarray[string],
-              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
+proc matchLen*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int {.inline.} =
+  ## 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.
-  return matchOrFind(s, pattern, matches, start.cint, pcre.ANCHORED)
-
-proc matchLen*(s: string, pattern: TRegEx, 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. 
-  return matchOrFind(s, pattern, start.cint, pcre.ANCHORED)
-
-proc find*(s: string, pattern: TRegEx, matches: var openarray[string],
-           start = 0): int =
-  ## returns the starting 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 is returned.
+  result = matchOrFind(buf, pattern, start.cint, bufSize, pcre.ANCHORED)
+
+proc match*(s: string, pattern: Regex, start = 0): bool {.inline.} =
+  ## returns `true` if `s[start..]` matches the `pattern`.
+  result = matchLen(cstring(s), pattern, start, s.len) != -1
+
+proc match*(s: string, pattern: Regex, matches: var openArray[string],
+           start = 0): bool {.inline.} =
+  ## returns `true` if `s[start..]` matches the `pattern` and
+  ## the captured substrings in the array `matches`. If it does not
+  ## match, nothing is written into `matches` and `false` is
+  ## returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    import std/sequtils
+    var matches: array[2, string]
+    if match("abcdefg", re"c(d)ef(g)", matches, 2):
+      doAssert toSeq(matches) == @["d", "g"]
+  result = matchLen(cstring(s), pattern, matches, start, s.len) != -1
+
+proc match*(buf: cstring, pattern: Regex, matches: var openArray[string],
+           start = 0, bufSize: int): bool {.inline.} =
+  ## returns `true` if `buf[start..<bufSize]` matches the `pattern` and
+  ## the captured substrings in the array `matches`. If it does not
+  ## match, nothing is written into `matches` and `false` is
+  ## returned.
+  ## `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  result = matchLen(buf, pattern, matches, start, bufSize) != -1
+
+proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
+           start = 0, bufSize: int): int =
+  ## returns the starting position of `pattern` in `buf` and the captured
+  ## substrings in the array `matches`. If it does not match, nothing
+  ## is written into `matches` and `-1` is returned.
+  ## `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   var
-    rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
-    res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, start.cint, 0'i32,
-      cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+    rtarray = initRtArray[cint]((matches.len+1)*3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
+      cast[ptr cint](rawMatches), (matches.len+1).cint*3)
   if res < 0'i32: return res
   for i in 1..int(res)-1:
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
-    if a >= 0'i32: matches[i-1] = substr(s, int(a), int(b)-1)
+    if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
     else: matches[i-1] = ""
   return rawMatches[0]
 
-proc find*(s: string, pattern: TRegEx, start = 0): int =
-  ## returns the starting position of ``pattern`` in ``s``. If it does not
-  ## match, -1 is returned.
+proc find*(s: string, pattern: Regex, matches: var openArray[string],
+           start = 0): int {.inline.} =
+  ## returns the starting 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` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  result = find(cstring(s), pattern, matches, start, s.len)
+
+proc find*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int =
+  ## returns the starting position of `pattern` in `buf`,
+  ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ## If it does not match, `-1` is returned.
   var
-    rawMatches: array[0..3 - 1, cint]
-    res = pcre.Exec(pattern.h, nil, s, len(s).cint, start.cint, 0'i32,
-      cast[ptr cint](addr(rawMatches)), 3)
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
+    res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, 0'i32,
+      cast[ptr cint](rawMatches), 3)
   if res < 0'i32: return res
   return rawMatches[0]
-  
-iterator findAll*(s: string, pattern: TRegEx, start = 0): string = 
-  ## yields all matching *substrings* of `s` that match `pattern`.
-  var i = int32(start)
-  var rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
+
+proc find*(s: string, pattern: Regex, start = 0): int {.inline.} =
+  ## returns the starting position of `pattern` in `s`. If it does not
+  ## match, `-1` is returned. We start the scan at `start`.
+  runnableExamples:
+    doAssert find("abcdefg", re"cde") == 2
+    doAssert find("abcdefg", re"abc") == 0
+    doAssert find("abcdefg", re"zz") == -1 # not found
+    doAssert find("abcdefg", re"cde", start = 2) == 2 # still 2
+    doAssert find("abcdefg", re"cde", start = 3) == -1 # we're past the start position
+    doAssert find("xabc", re"(?<=x|y)abc", start = 1) == 1
+      # lookbehind assertion `(?<=x|y)` can look behind `start`
+  result = find(cstring(s), pattern, start, s.len)
+
+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
+  ## are iterating over: bad things could happen.
+  var
+    i = int32(start)
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
   while true:
-    let res = pcre.Exec(pattern.h, pattern.e, s, len(s).cint, i, 0'i32,
-      cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
+    let res = pcre.exec(pattern.h, pattern.e, s, len(s).cint, i, 0'i32,
+      cast[ptr cint](rawMatches), 3)
     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: TRegEx, 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))
+iterator findAll*(buf: cstring, pattern: Regex, start = 0, bufSize: int): string =
+  ## Yields all matching `substrings` of `s` that match `pattern`.
+  ##
+  ## Note that since this is an iterator you should not modify the string you
+  ## are iterating over: bad things could happen.
+  var
+    i = int32(start)
+    rtarray = initRtArray[cint](3)
+    rawMatches = rtarray.getRawData
+  while true:
+    let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, i, 0'i32,
+      cast[ptr cint](rawMatches), 3)
+    if res < 0'i32: break
+    let a = rawMatches[0]
+    let b = rawMatches[1]
+    if a == b and a == i: break
+    var str = newString(b-a)
+    copyMem(str[0].addr, unsafeAddr(buf[a]), b-a)
+    yield str
+    i = b
 
-when not defined(nimhygiene):
-  {.pragma: inject.}
+proc findAll*(s: string, pattern: Regex, start = 0): seq[string] {.inline.} =
+  ## returns all matching `substrings` of `s` that match `pattern`.
+  ## If it does not match, `@[]` is returned.
+  result = @[]
+  for x in findAll(s, pattern, start): result.add x
 
-template `=~` *(s: string, pattern: TRegEx): expr = 
-  ## This calls ``match`` with an implicit declared ``matches`` array that 
-  ## can be used in the scope of the ``=~`` call: 
-  ## 
-  ## .. code-block:: nimrod
-  ##
-  ##   if line =~ re"\s*(\w+)\s*\=\s*(\w+)": 
-  ##     # matches a key=value pair:
-  ##     echo("Key: ", matches[0])
-  ##     echo("Value: ", matches[1])
-  ##   elif line =~ re"\s*(\#.*)":
-  ##     # matches a comment
-  ##     # note that the implicit ``matches`` array is different from the
-  ##     # ``matches`` array of the first branch
-  ##     echo("comment: ", matches[0])
-  ##   else:
-  ##     echo("syntax error")
-  ##
-  when not definedInScope(matches):
-    var matches {.inject.}: array[0..re.maxSubPatterns-1, string]
+template `=~` *(s: string, pattern: Regex): untyped =
+  ## This calls `match` with an implicit declared `matches` array that
+  ## can be used in the scope of the `=~` call:
+  runnableExamples:
+    proc parse(line: string): string =
+      if line =~ re"\s*(\w+)\s*\=\s*(\w+)": # matches a key=value pair:
+        result = $(matches[0], matches[1])
+      elif line =~ re"\s*(\#.*)": # matches a comment
+        # note that the implicit `matches` array is different from 1st branch
+        result = $(matches[0],)
+      else: raiseAssert "unreachable"
+      doAssert not declared(matches)
+    doAssert parse("NAME = LENA") == """("NAME", "LENA")"""
+    doAssert parse("   # comment ... ") == """("# comment ... ",)"""
+  bind MaxSubpatterns
+  when not declaredInScope(matches):
+    var matches {.inject.}: array[MaxSubpatterns, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
 
-proc contains*(s: string, pattern: TRegEx, start = 0): bool =
-  ## same as ``find(s, pattern, start) >= 0``
+proc contains*(s: string, pattern: Regex, start = 0): bool {.inline.} =
+  ## same as `find(s, pattern, start) >= 0`
   return find(s, pattern, start) >= 0
 
-proc contains*(s: string, pattern: TRegEx, matches: var openArray[string],
-              start = 0): bool =
-  ## same as ``find(s, pattern, matches, start) >= 0``
+proc contains*(s: string, pattern: Regex, matches: var openArray[string],
+              start = 0): bool {.inline.} =
+  ## same as `find(s, pattern, matches, start) >= 0`
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   return find(s, pattern, matches, start) >= 0
 
-proc startsWith*(s: string, prefix: TRegEx): bool =
+proc startsWith*(s: string, prefix: Regex): bool {.inline.} =
   ## returns true if `s` starts with the pattern `prefix`
   result = matchLen(s, prefix) >= 0
 
-proc endsWith*(s: string, suffix: TRegEx): bool =
-  ## returns true if `s` ends with the pattern `prefix`
+proc endsWith*(s: string, suffix: Regex): bool {.inline.} =
+  ## returns true if `s` ends with the pattern `suffix`
   for i in 0 .. s.len-1:
     if matchLen(s, suffix, i) == s.len - i: return true
 
-proc replace*(s: string, sub: TRegEx, by = ""): string =
-  ## Replaces `sub` in `s` by the string `by`. Captures cannot be 
-  ## accessed in `by`. Examples:
-  ##
-  ## .. code-block:: nimrod
-  ##   "var1=key; var2=key2".replace(re"(\w+)'='(\w+)")
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nimrod
-  ##
-  ##   "; "
+proc replace*(s: string, sub: Regex, by = ""): string =
+  ## Replaces `sub` in `s` by the string `by`. Captures cannot be
+  ## accessed in `by`.
+  runnableExamples:
+    doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)") == "; "
+    doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)", "?") == "?; ?"
   result = ""
   var prev = 0
-  while true:
-    var match = findBounds(s, sub, prev)
+  var flags = int32(0)
+  while prev < s.len:
+    var match = findBoundsImpl(s.cstring, sub, prev, s.len, flags)
+    flags = 0
     if match.first < 0: break
     add(result, substr(s, prev, match.first-1))
     add(result, by)
+    if match.first > match.last:
+      # 0-len match
+      flags = pcre.NOTEMPTY_ATSTART
     prev = match.last + 1
   add(result, substr(s, prev))
-  
-proc replacef*(s: string, sub: TRegEx, by: string): string =
+
+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:
-  ##
-  ## .. code-block:: nimrod
-  ## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2")
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nimrod
-  ##
-  ## "var1<-keykey; val2<-key2key2"
+  ## with the notation `$i` and `$#` (see strutils.\`%\`).
+  runnableExamples:
+    doAssert "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
+      "var1<-keykey; var2<-key2key2"
   result = ""
-  var caps: array[0..maxSubpatterns-1, string]
+  var caps: array[MaxSubpatterns, string]
   var prev = 0
-  while true:
+  while prev < s.len:
     var match = findBounds(s, sub, caps, prev)
     if match.first < 0: break
     add(result, substr(s, prev, match.first-1))
     addf(result, by, caps)
+    if match.last + 1 == prev: break
     prev = match.last + 1
   add(result, substr(s, prev))
-  when false:
-    result = ""
-    var i = 0
-    var caps: array[0..maxSubpatterns-1, string]
-    while i < s.len:
-      var x = matchLen(s, sub, caps, i)
-      if x <= 0:
-        add(result, s[i])
-        inc(i)
-      else:
-        addf(result, by, caps)
-        inc(i, x)
-    # substr the rest:
-    add(result, substr(s, i))
-  
-proc parallelReplace*(s: string, subs: openArray[
-                      tuple[pattern: TRegEx, repl: string]]): string = 
+
+proc multiReplace*(s: string, subs: openArray[
+                   tuple[pattern: Regex, repl: string]]): string =
   ## Returns a modified copy of `s` with the substitutions in `subs`
   ## 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):
@@ -340,120 +548,64 @@ proc parallelReplace*(s: string, subs: openArray[
       add(result, s[i])
       inc(i)
   # copy the rest:
-  add(result, substr(s, i))  
-  
+  add(result, substr(s, i))
+
 proc transformFile*(infile, outfile: string,
-                    subs: openArray[tuple[pattern: TRegEx, repl: string]]) =
+                    subs: openArray[tuple[pattern: Regex, repl: string]]) =
   ## reads in the file `infile`, performs a parallel replacement (calls
-  ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
+  ## `parallelReplace`) and writes back to `outfile`. Raises `IOError` if an
   ## 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: TRegEx): string =
+  var x = readFile(infile)
+  writeFile(outfile, x.multiReplace(subs))
+
+iterator split*(s: string, sep: Regex; maxsplit = -1): string =
   ## Splits the string `s` into substrings.
   ##
-  ## Substrings are separated by the regular expression `sep`.
-  ## Examples:
-  ##
-  ## .. code-block:: nimrod
-  ##   for word in split("00232this02939is39an22example111", re"\d+"):
-  ##     writeln(stdout, word)
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nimrod
-  ##   "this"
-  ##   "is"
-  ##   "an"
-  ##   "example"
-  ##
-  var
-    first = 0
-    last = 0
-  while last < len(s):
-    var x = matchLen(s, sep, last)
-    if x > 0: inc(last, x)
-    first = last
-    while last < len(s):
+  ## Substrings are separated by the regular expression `sep`
+  ## (and the portion matched by `sep` is not returned).
+  runnableExamples:
+    import std/sequtils
+    doAssert toSeq(split("00232this02939is39an22example111", re"\d+")) ==
+      @["", "this", "is", "an", "example", ""]
+  var last = 0
+  var splits = maxsplit
+  var x = -1
+  if len(s) == 0:
+    last = 1
+  if matchLen(s, sep, 0) == 0:
+    x = 0
+  while last <= len(s):
+    var first = last
+    var sepLen = 1
+    if x == 0:
       inc(last)
+    while last < len(s):
       x = matchLen(s, sep, last)
-      if x > 0: break
-    if first < last:
-      yield substr(s, first, last-1)
+      if x >= 0:
+        sepLen = x
+        break
+      inc(last)
+    if splits == 0: last = len(s)
+    yield substr(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last, sepLen)
 
-proc split*(s: string, sep: TRegEx): 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 split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} =
+  ## Splits the string `s` into a seq of substrings.
+  ##
+  ## The portion matched by `sep` is not returned.
+  result = @[]
+  for x in split(s, sep, maxsplit): result.add x
+
+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"
-    ## 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"
-    ## describes a common email address
-  reURL* = 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+")
-
-  assert matchLen("key", re(reIdentifier)) == 3
-
-  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): 
-    assert matches[0] == "d"
-    assert matches[1] == "g"
-  else:
-    assert false
-  
-  if "abc" =~ re"(a)bcxyz|(\w+)":
-    assert matches[1] == "abc"
-  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")
-  assert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
-         "$1<-$2$2; $1<-$2$2")
-
-  for word in split("00232this02939is39an22example111", re"\d+"):
-    writeln(stdout, word)
-
-  for x in findAll("abcdef", re"^{.}", 3):
-    assert x == "d"
-  for x in findAll("abcdef", re".", 3):
-    echo x
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
deleted file mode 100755
index 54d524c7b..000000000
--- a/lib/impure/ssl.nim
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module provides an easy to use sockets-style 
-## nimrod interface to the OpenSSL library.
-
-{.deprecated.}
-
-import openssl, strutils, os
-
-type
-  TSecureSocket* {.final.} = object
-    ssl: PSSL
-    bio: PBIO
-
-proc connect*(sock: var TSecureSocket, 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:
-    OSError()
-  
-  var ctx = SSL_CTX_new(SSLv23_client_method())
-  if ctx == nil:
-    ERR_print_errors_fp(stderr)
-    OSError()
-    
-  #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:
-    OSError()
-
-  if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
-    OSError()
-  
-  if BIO_do_connect(sock.bio) <= 0:
-    ERR_print_errors_fp(stderr)
-    OSError()
-  
-  result = SSL_get_verify_result(sock.ssl)
-
-proc recvLine*(sock: TSecureSocket, 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!
-  setLen(line.string, 0)
-  while True:
-    var c: array[0..0, char]
-    var n = BIO_read(sock.bio, c, c.len.cint)
-    if n <= 0: return False
-    if c[0] == '\r':
-      n = BIO_read(sock.bio, c, c.len.cint)
-      if n > 0 and c[0] == '\L':
-        return True
-      elif n <= 0:
-        return False
-    elif c[0] == '\L': return True
-    add(line.string, c)
-
-
-proc send*(sock: TSecureSocket, data: string) =
-  ## Writes `data` to the socket.
-  if BIO_write(sock.bio, data, data.len.cint) <= 0:
-    OSError()
-
-proc close*(sock: TSecureSocket) =
-  ## Closes the socket
-  if BIO_free(sock.bio) <= 0:
-    ERR_print_errors_fp(stderr)
-    OSError()
-
-when isMainModule:
-  var s: TSecureSocket
-  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.len
-  
diff --git a/lib/impure/web.nim b/lib/impure/web.nim
deleted file mode 100755
index 5f04422d1..000000000
--- a/lib/impure/web.nim
+++ /dev/null
@@ -1,63 +0,0 @@
-#

-#

-#            Nimrod's Runtime Library

-#        (c) Copyright 2012 Andreas Rumpf

-#

-#    See the file "copying.txt", included in this

-#    distribution, for details about the copyright.

-#

-

-## This module contains simple high-level procedures for dealing with the

-## web. Use cases: 

-##

-## * requesting URLs

-## * sending and retrieving emails

-## * sending and retrieving files from an FTP server

-##

-## Currently only requesting URLs is implemented. The implementation depends

-## on the libcurl library!

-##

-## **Deprecated since version 0.8.8:** Use the

-## `httpclient <httpclient.html>`_ module instead. 

-## 

-

-{.deprecated.}

-

-import libcurl, streams

-

-proc curlwrapperWrite(p: pointer, size, nmemb: int, 

-                      data: pointer): int {.cdecl.} = 

-  var stream = cast[PStream](data)

-  stream.writeData(p, size*nmemb)

-  return size*nmemb

-

-proc URLretrieveStream*(url: string): PStream = 

-  ## retrieves the given `url` and returns a stream which one can read from to

-  ## obtain the contents. Returns nil if an error occurs.

-  result = newStringStream()

-  var hCurl = easy_init() 

-  if hCurl == nil: return nil

-  if easy_setopt(hCurl, OPT_URL, url) != E_OK: return nil

-  if easy_setopt(hCurl, OPT_WRITEFUNCTION, 

-                      curlwrapperWrite) != E_OK: return nil

-  if easy_setopt(hCurl, OPT_WRITEDATA, result) != E_OK: return nil

-  if easy_perform(hCurl) != E_OK: return nil

-  easy_cleanup(hCurl)

-  

-proc URLretrieveString*(url: string): TaintedString = 

-  ## retrieves the given `url` and returns the contents. Returns nil if an

-  ## error occurs.

-  var stream = newStringStream()

-  var hCurl = easy_init()

-  if hCurl == nil: return

-  if easy_setopt(hCurl, OPT_URL, url) != E_OK: return

-  if easy_setopt(hCurl, OPT_WRITEFUNCTION, 

-                      curlwrapperWrite) != E_OK: return

-  if easy_setopt(hCurl, OPT_WRITEDATA, stream) != E_OK: return

-  if easy_perform(hCurl) != E_OK: return

-  easy_cleanup(hCurl)

-  result = stream.data.TaintedString

-

-when isMainModule:

-  echo URLretrieveString("http://nimrod-code.org/")

-

diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
deleted file mode 100755
index 029d8527d..000000000
--- a/lib/impure/zipfiles.nim
+++ /dev/null
@@ -1,167 +0,0 @@
-#
-#
-#            Nimrod'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 TObject ## represents a zip archive
-    mode: TFileMode
-    w: PZip
-
-
-proc zipError(z: var TZipArchive) = 
-  var e: ref EIO
-  new(e)
-  e.msg = $zip_strerror(z.w)
-  raise e
-  
-proc open*(z: var TZipArchive, filename: string, mode: TFileMode = 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) 
-  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: Tzip_source_cmd): int {.cdecl.} = 
-  var src = cast[PStream](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: PStream) = 
-  ## 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 TStream
-    f: Pzip_file
-
-  PZipFileStream* = 
-    ref TZipFileStream ## a reader stream of a file within a zip archive 
-
-proc fsClose(s: PStream) = zip_fclose(PZipFileStream(s).f)
-proc fsReadData(s: PStream, 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: PStream) =
-  ## 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))
-