summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/allocators.nim35
-rw-r--r--lib/core/macros.nim121
-rw-r--r--lib/core/refs.nim97
-rw-r--r--lib/core/seqs.nim117
-rw-r--r--lib/core/strs.nim111
-rw-r--r--lib/core/typelayouts.nim19
-rw-r--r--lib/deprecated/pure/actors.nim10
-rw-r--r--lib/deprecated/pure/rawsockets.nim4
-rw-r--r--lib/deprecated/pure/sockets.nim2
-rw-r--r--lib/impure/db_postgres.nim45
-rw-r--r--lib/impure/db_sqlite.nim2
-rw-r--r--lib/impure/nre.nim36
-rw-r--r--lib/impure/re.nim33
-rw-r--r--lib/js/asyncjs.nim141
-rw-r--r--lib/js/dom.nim4
-rw-r--r--lib/js/jsffi.nim22
-rw-r--r--lib/nimbase.h21
-rw-r--r--lib/packages/docutils/highlite.nim20
-rw-r--r--lib/packages/docutils/rst.nim13
-rw-r--r--lib/packages/docutils/rstgen.nim33
-rw-r--r--lib/posix/epoll.nim19
-rw-r--r--lib/posix/linux.nim2
-rw-r--r--lib/posix/posix.nim3
-rw-r--r--lib/posix/posix_linux_amd64.nim6
-rw-r--r--lib/posix/posix_other.nim36
-rw-r--r--lib/pure/algorithm.nim123
-rw-r--r--lib/pure/asyncdispatch.nim587
-rw-r--r--lib/pure/asyncfutures.nim83
-rw-r--r--lib/pure/asynchttpserver.nim268
-rw-r--r--lib/pure/asyncmacro.nim59
-rw-r--r--lib/pure/bitops.nim2
-rw-r--r--lib/pure/browsers.nim20
-rw-r--r--lib/pure/cgi.nim49
-rw-r--r--lib/pure/collections/critbits.nim8
-rw-r--r--lib/pure/collections/deques.nim6
-rw-r--r--lib/pure/collections/heapqueue.nim9
-rw-r--r--lib/pure/collections/lists.nim139
-rw-r--r--lib/pure/collections/queues.nim3
-rw-r--r--lib/pure/collections/sequtils.nim498
-rw-r--r--lib/pure/collections/sets.nim28
-rw-r--r--lib/pure/collections/sharedlist.nim15
-rw-r--r--lib/pure/collections/sharedstrings.nim6
-rw-r--r--lib/pure/collections/sharedtables.nim21
-rw-r--r--lib/pure/collections/tableimpl.nim2
-rw-r--r--lib/pure/collections/tables.nim24
-rw-r--r--lib/pure/concurrency/cpuinfo.nim21
-rw-r--r--lib/pure/concurrency/threadpool.nim8
-rw-r--r--lib/pure/cookies.nim15
-rw-r--r--lib/pure/cstrutils.nim79
-rw-r--r--lib/pure/future.nim8
-rw-r--r--lib/pure/gentabs.nim211
-rw-r--r--lib/pure/htmlgen.nim4
-rw-r--r--lib/pure/httpclient.nim74
-rw-r--r--lib/pure/httpcore.nim3
-rw-r--r--lib/pure/includes/osenv.nim4
-rw-r--r--lib/pure/includes/oserr.nim3
-rw-r--r--lib/pure/ioselectors.nim293
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim113
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim25
-rw-r--r--lib/pure/ioselects/ioselectors_poll.nim19
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim51
-rw-r--r--lib/pure/json.nim182
-rw-r--r--lib/pure/lenientops.nim60
-rw-r--r--lib/pure/logging.nim8
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/matchers.nim2
-rw-r--r--lib/pure/math.nim21
-rw-r--r--lib/pure/memfiles.nim11
-rw-r--r--lib/pure/mersenne.nim2
-rw-r--r--lib/pure/mimetypes.nim9
-rw-r--r--lib/pure/net.nim27
-rw-r--r--lib/pure/numeric.nim87
-rw-r--r--lib/pure/oids.nim2
-rw-r--r--lib/pure/os.nim87
-rw-r--r--lib/pure/ospaths.nim64
-rw-r--r--lib/pure/osproc.nim388
-rw-r--r--lib/pure/parsecsv.nim7
-rw-r--r--lib/pure/parseopt2.nim2
-rw-r--r--lib/pure/parsesql.nim466
-rw-r--r--lib/pure/parseutils.nim79
-rw-r--r--lib/pure/poly.nim371
-rw-r--r--lib/pure/random.nim133
-rw-r--r--lib/pure/rationals.nim87
-rw-r--r--lib/pure/romans.nim59
-rw-r--r--lib/pure/ropes.nim10
-rw-r--r--lib/pure/scgi.nim2
-rw-r--r--lib/pure/securehash.nim2
-rw-r--r--lib/pure/selectors.nim683
-rw-r--r--lib/pure/streams.nim32
-rw-r--r--lib/pure/strformat.nim619
-rw-r--r--lib/pure/strscans.nim47
-rw-r--r--lib/pure/strutils.nim488
-rw-r--r--lib/pure/terminal.nim17
-rw-r--r--lib/pure/times.nim1549
-rw-r--r--lib/pure/typetraits.nim18
-rw-r--r--lib/pure/unicode.nim12
-rw-r--r--lib/pure/unittest.nim93
-rw-r--r--lib/pure/uri.nim87
-rw-r--r--lib/system.nim463
-rw-r--r--lib/system/alloc.nim186
-rw-r--r--lib/system/atomics.nim20
-rw-r--r--lib/system/chcks.nim1
-rw-r--r--lib/system/debugger.nim6
-rw-r--r--lib/system/endb.nim2
-rw-r--r--lib/system/excpt.nim120
-rw-r--r--lib/system/gc.nim25
-rw-r--r--lib/system/gc2.nim10
-rw-r--r--lib/system/gc_ms.nim6
-rw-r--r--lib/system/genodealloc.nim114
-rw-r--r--lib/system/jssys.nim12
-rw-r--r--lib/system/mmdisp.nim17
-rw-r--r--lib/system/nimscript.nim2
-rw-r--r--lib/system/osalloc.nim14
-rw-r--r--lib/system/repr.nim4
-rw-r--r--lib/system/reprjs.nim36
-rw-r--r--lib/system/sysio.nim16
-rw-r--r--lib/system/sysspawn.nim6
-rw-r--r--lib/system/sysstr.nim89
-rw-r--r--lib/system/threads.nim6
-rw-r--r--lib/windows/winlean.nim2
-rw-r--r--lib/wrappers/libuv.nim732
-rw-r--r--lib/wrappers/openssl.nim10
-rw-r--r--lib/wrappers/pdcurses.nim1560
-rw-r--r--lib/wrappers/tinyc.nim23
124 files changed, 6550 insertions, 6580 deletions
diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim
new file mode 100644
index 000000000..d6608a203
--- /dev/null
+++ b/lib/core/allocators.nim
@@ -0,0 +1,35 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type
+  Allocator* {.inheritable.} = ptr object
+    alloc*: proc (a: Allocator; size: int; alignment = 8): pointer {.nimcall.}
+    dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
+    realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
+
+var
+  currentAllocator {.threadvar.}: Allocator
+
+proc getCurrentAllocator*(): Allocator =
+  result = currentAllocator
+
+proc setCurrentAllocator*(a: Allocator) =
+  currentAllocator = a
+
+proc alloc*(size: int): pointer =
+  let a = getCurrentAllocator()
+  result = a.alloc(a, size)
+
+proc dealloc*(p: pointer; size: int) =
+  let a = getCurrentAllocator()
+  a.dealloc(a, size)
+
+proc realloc*(p: pointer; oldSize, newSize: int): pointer =
+  let a = getCurrentAllocator()
+  result = a.realloc(a, oldSize, newSize)
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 8c70d2b47..b08a2198e 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -21,7 +21,7 @@ type
     nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
     nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
     nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
-    nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall,
+    nnkTripleStrLit, nnkNilLit, nnkComesFrom, nnkDotCall,
     nnkCommand, nnkCall, nnkCallStrLit, nnkInfix,
     nnkPrefix, nnkPostfix, nnkHiddenCallConv,
     nnkExprEqExpr,
@@ -113,7 +113,9 @@ type
 
 type
   NimIdent* = object of RootObj
-    ## represents a Nim identifier in the AST
+    ## represents a Nim identifier in the AST. **Note**: This is only
+    ## rarely useful, for identifier construction from a string
+    ## use ``ident"abc"``.
 
   NimSymObj = object # hidden
   NimSym* = ref NimSymObj
@@ -129,14 +131,11 @@ const
   nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
                    nnkCallStrLit}
 
-proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.}
-  ## get `n`'s `i`'th child.
-
-proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild",
-  noSideEffect.}
-  ## set `n`'s `i`'th child to `child`.
+proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
+  ## constructs an identifier from the string `s`
+  ## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
 
-proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
+proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
   ## constructs an identifier from the string `s`
 
 proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
@@ -162,6 +161,20 @@ proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
 proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.}
   ## returns the number of children of `n`.
 
+proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.}
+  ## get `n`'s `i`'th child.
+
+proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int]
+  ## get `n`'s `i`'th child.
+
+proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild",
+  noSideEffect.}
+  ## set `n`'s `i`'th child to `child`.
+
+proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) =
+  ## set `n`'s `i`'th child to `child`.
+  n[n.len - i.int] = child
+
 proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable,
   noSideEffect, locks: 0.}
   ## Adds the `child` to the `father` node. Returns the
@@ -230,7 +243,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
 proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
 
 proc newNimNode*(kind: NimNodeKind,
-                 lineInfoFrom: NimNode=nil): NimNode
+                 lineInfoFrom: NimNode = nil): NimNode
   {.magic: "NNewNimNode", noSideEffect.}
   ## Creates a new AST node of the specified kind.
   ##
@@ -283,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
 proc newIdentNode*(i: string): NimNode {.compileTime.} =
   ## creates an identifier node from `i`
   result = newNimNode(nnkIdent)
-  result.ident = !i
+  result.ident = toNimIdent i
 
 
 type
@@ -393,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
   ##
   ## .. code-block:: nim
   ##
-  ##   macro check(ex: expr): stmt =
+  ##   macro check(ex: untyped): typed =
   ##     # this is a simplified version of the check macro from the
   ##     # unittest module.
   ##
@@ -428,6 +441,13 @@ proc expectLen*(n: NimNode, len: int) {.compileTime.} =
   ## macros that check its number of arguments.
   if n.len != len: error("macro expects a node with " & $len & " children", n)
 
+proc expectLen*(n: NimNode, min, max: int) {.compileTime.} =
+  ## checks that `n` has a number of children in the range ``min..max``.
+  ## If this is not the case, compilation aborts with an error message.
+  ## This is useful for writing macros that check its number of arguments.
+  if n.len < min or n.len > max:
+    error("macro expects a node with " & $min & ".." & $max " children", n)
+
 proc newTree*(kind: NimNodeKind,
               children: varargs[NimNode]): NimNode {.compileTime.} =
   ## produces a new node with children.
@@ -463,7 +483,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
   result = newNimNode(nnkCharLit)
   result.intVal = ord(c)
 
-
 proc newLit*(i: int): NimNode {.compileTime.} =
   ## produces a new integer literal node.
   result = newNimNode(nnkIntLit)
@@ -567,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
 proc newLit*(arg: tuple): NimNode {.compileTime.} =
   result = nnkPar.newTree
   for a,b in arg.fieldPairs:
-    result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
+    result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
 
 proc newLit*(s: string): NimNode {.compileTime.} =
   ## produces a new string literal node.
@@ -601,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
     of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
     of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
-    of nnkIdent: res.add(" !\"" & $n.ident & '"')
+    of nnkIdent: res.add(" ident\"" & $n.ident & '"')
     of nnkSym: res.add(" \"" & $n.symbol & '"')
     of nnkNone: assert false
     else:
@@ -626,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
   of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
   of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
   of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
-  of nnkIdent: add(result, "!\"" & $n.ident & '"')
+  of nnkIdent: add(result, "ident\"" & $n.ident & '"')
   of nnkSym: add(result, $n.symbol)
   of nnkNone: assert false
   else:
@@ -650,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
   ## .. code-block:: nim
   ##   nnkStmtList.newTree(
   ##     nnkCommand.newTree(
-  ##       newIdentNode(!"echo"),
+  ##       newIdentNode("echo"),
   ##       newLit("Hello world")
   ##     )
   ##   )
@@ -704,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
     of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
     of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
-    of nnkIdent: res.add("!" & ($n.ident).escape())
+    of nnkIdent: res.add(($n.ident).escape())
     of nnkSym: res.add(($n.symbol).escape())
     of nnkNone: assert false
     else:
@@ -839,7 +858,7 @@ proc newNilLit*(): NimNode {.compileTime.} =
   ## New nil literal shortcut
   result = newNimNode(nnkNilLit)
 
-proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
+proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1]
   ## Return the last item in nodes children. Same as `node[^1]`
 
 
@@ -884,10 +903,52 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
   for i in branches:
     result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
 
+proc newEnum*(name: NimNode, fields: openArray[NimNode],
+              public, pure: bool): NimNode {.compileTime.} =
+
+  ## Creates a new enum. `name` must be an ident. Fields are allowed to be
+  ## either idents or EnumFieldDef
+  ##
+  ## .. code-block:: nim
+  ##
+  ##    newEnum(
+  ##      name    = ident("Colors"),
+  ##      fields  = [ident("Blue"), ident("Red")],
+  ##      public  = true, pure = false)
+  ##
+  ##    # type Colors* = Blue Red
+  ##
+
+  expectKind name, nnkIdent
+  doAssert len(fields) > 0, "Enum must contain at least one field"
+  for field in fields:
+    expectKind field, {nnkIdent, nnkEnumFieldDef}
+
+  let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields)
+  var typeDefArgs = [name, newEmptyNode(), enumBody]
+
+  if public:
+    let postNode = newNimNode(nnkPostfix).add(
+      newIdentNode("*"), typeDefArgs[0])
+
+    typeDefArgs[0] = postNode
+
+  if pure:
+    let pragmaNode = newNimNode(nnkPragmaExpr).add(
+      typeDefArgs[0],
+      add(newNimNode(nnkPragma), newIdentNode("pure")))
+
+    typeDefArgs[0] = pragmaNode
+
+  let
+    typeDef   = add(newNimNode(nnkTypeDef), typeDefArgs)
+    typeSect  = add(newNimNode(nnkTypeSection), typeDef)
+
+  return typeSect
 
 proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
   ## Copy all children from `src` to `dest`
-  for i in 0 .. < src.len:
+  for i in 0 ..< src.len:
     dest.add src[i].copyNimTree
 
 template expectRoutine(node: NimNode) =
@@ -986,6 +1047,11 @@ iterator items*(n: NimNode): NimNode {.inline.} =
   for i in 0 ..< n.len:
     yield n[i]
 
+iterator pairs*(n: NimNode): (int, NimNode) {.inline.} =
+  ## Iterates over the children of the NimNode ``n`` and its indices.
+  for i in 0 ..< n.len:
+    yield (i, n[i])
+
 iterator children*(n: NimNode): NimNode {.inline.} =
   ## Iterates over the children of the NimNode ``n``.
   for i in 0 ..< n.len:
@@ -996,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
   ##
   ## .. code-block:: nim
   ##   var res = findChild(n, it.kind == nnkPostfix and
-  ##                          it.basename.ident == !"foo")
+  ##                          it.basename.ident == toNimIdent"foo")
   block:
     var res: NimNode
     for it in n.children:
@@ -1030,7 +1096,7 @@ proc basename*(a: NimNode): NimNode =
 
 proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
   case a.kind
-  of nnkIdent: macros.`ident=`(a,  !val)
+  of nnkIdent: macros.`ident=`(a, toNimIdent val)
   of nnkPostfix, nnkPrefix: a[1] = ident(val)
   else:
     quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
@@ -1091,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
   ## other ways like ``node.ident`` are much more error-prone, unfortunately.
   case node.kind
   of nnkIdent:
-    result = node.ident == !s
+    result = node.ident == toNimIdent s
   of nnkSym:
     result = eqIdent($node.symbol, s)
   of nnkOpenSymChoice, nnkClosedSymChoice:
@@ -1099,10 +1165,10 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
   else:
     result = false
 
-proc hasArgOfName* (params: NimNode; name: string): bool {.compiletime.}=
+proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
   ## Search nnkFormalParams for an argument.
   assert params.kind == nnkFormalParams
-  for i in 1 .. <params.len:
+  for i in 1 ..< params.len:
     template node: untyped = params[i]
     if name.eqIdent( $ node[0]):
       return true
@@ -1160,3 +1226,8 @@ when not defined(booting):
     macro payload: untyped {.gensym.} =
       result = parseStmt(e)
     payload()
+
+macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
+  result = newCall(callee)
+  for i in 0 ..< args.len:
+    result.add args[i]
diff --git a/lib/core/refs.nim b/lib/core/refs.nim
new file mode 100644
index 000000000..e1575b68c
--- /dev/null
+++ b/lib/core/refs.nim
@@ -0,0 +1,97 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Default ref implementation used by Nim's core.
+
+# We cannot use the allocator interface here as we require a heap walker to
+# exist. Thus we import 'alloc' directly here to get our own heap that is
+# all under the GC's control and can use the ``allObjects`` iterator which
+# is crucial for the "sweep" phase.
+import typelayouts, alloc
+
+type
+  TracingGc = ptr object of Allocator
+    visit*: proc (fieldAddr: ptr pointer; a: Allocator) {.nimcall.}
+
+  GcColor = enum
+    white = 0, black = 1, grey = 2 ## to flip the meaning of white/black
+                                   ## perform (1 - col)
+
+  GcHeader = object
+    t: ptr TypeLayout
+    color: GcColor
+  Cell = ptr GcHeader
+
+  GcFrame {.core.} = object
+    prev: ptr GcFrame
+    marker: proc (self: GcFrame; a: Allocator)
+
+  Phase = enum
+    None, Marking, Sweeping
+
+  GcHeap = object
+    r: MemRegion
+    phase: Phase
+    currBlack, currWhite: GcColor
+    greyStack: seq[Cell]
+
+var
+  gch {.threadvar.}: GcHeap
+
+proc `=trace`[T](a: ref T) =
+  if not marked(a):
+    mark(a)
+    `=trace`(a[])
+
+template usrToCell(p: pointer): Cell =
+
+template cellToUsr(cell: Cell): pointer =
+  cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(GcHeader)))
+
+template usrToCell(usr: pointer): Cell =
+  cast[Cell](cast[ByteAddress](usr)-%ByteAddress(sizeof(GcHeader)))
+
+template markGrey(x: Cell) =
+  if x.color == gch.currWhite and phase == Marking:
+    x.color = grey
+    add(gch.greyStack, x)
+
+proc `=`[T](dest: var ref T; src: ref T) =
+  ## full write barrier implementation.
+  if src != nil:
+    let s = usrToCell(src)
+    markGrey(s)
+  system.`=`(dest, src)
+
+proc linkGcFrame(f: ptr GcFrame) {.core.}
+proc unlinkGcFrame() {.core.}
+
+proc setGcFrame(f: ptr GcFrame) {.core.}
+
+proc registerGlobal(p: pointer; t: ptr TypeLayout) {.core.}
+proc unregisterGlobal(p: pointer; t: ptr TypeLayout) {.core.}
+
+proc registerThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
+proc unregisterThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
+
+proc newImpl(t: ptr TypeLayout): pointer =
+  let r = cast[Cell](rawAlloc(t.size + sizeof(GcHeader)))
+  r.typ = t
+  result = r +! sizeof(GcHeader)
+
+template new*[T](x: var ref T) =
+  x = newImpl(getTypeLayout(x))
+
+
+when false:
+  # implement these if your GC requires them:
+  proc writeBarrierLocal() {.core.}
+  proc writeBarrierGlobal() {.core.}
+
+  proc writeBarrierGeneric() {.core.}
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
new file mode 100644
index 000000000..6be95a3bc
--- /dev/null
+++ b/lib/core/seqs.nim
@@ -0,0 +1,117 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import allocators
+
+## Default seq implementation used by Nim's core.
+type
+  seq*[T] = object
+    len, cap: int
+    data: ptr UncheckedArray[T]
+
+template frees(s) = dealloc(s.data, s.cap * sizeof(T))
+
+# XXX make code memory safe for overflows in '*'
+proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} =
+  seq[T](len: x.len, cap: x.len, data: x)
+
+when defined(nimHasTrace):
+  proc `=trace`[T](s: seq[T]; a: Allocator) =
+    for i in 0 ..< s.len: `=trace`(s.data[i], a)
+
+proc `=destroy`[T](x: var seq[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      for i in 0..<x.len: `=destroy`(x[i])
+    frees(x)
+    x.data = nil
+    x.len = 0
+    x.cap = 0
+
+proc `=`[T](a: var seq[T]; b: seq[T]) =
+  if a.data == b.data: return
+  if a.data != nil:
+    frees(a)
+    a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, a.cap * sizeof(T))
+    else:
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+proc `=sink`[T](a: var seq[T]; b: seq[T]) =
+  if a.data != nil and a.data != b.data:
+    frees(a)
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc resize[T](s: var seq[T]) =
+  let old = s.cap
+  if old == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
+
+proc reserveSlot[T](x: var seq[T]): ptr T =
+  if x.len >= x.cap: resize(x)
+  result = addr(x.data[x.len])
+  inc x.len
+
+template add*[T](x: var seq[T]; y: T) =
+  reserveSlot(x)[] = y
+
+proc shrink*[T](x: var seq[T]; newLen: int) =
+  assert newLen <= x.len
+  assert newLen >= 0
+  when not supportsCopyMem(T):
+    for i in countdown(x.len - 1, newLen - 1):
+      `=destroy`(x.data[i])
+  x.len = newLen
+
+proc grow*[T](x: var seq[T]; newLen: int; value: T) =
+  if newLen <= x.len: return
+  assert newLen >= 0
+  if x.cap == 0: x.cap = newLen
+  else: x.cap = max(newLen, (x.cap * 3) shr 1)
+  x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
+  for i in x.len..<newLen:
+    x.data[i] = value
+  x.len = newLen
+
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
+proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
+  if newlen < x.len: shrink(x, newLen)
+  else: grow(x, newLen, default(T))
+
+template `[]`*[T](x: seq[T]; i: Natural): T =
+  assert i < x.len
+  x.data[i]
+
+template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
+  assert i < x.len
+  x.data[i] = y
+
+proc `@`*[T](elems: openArray[T]): seq[T] =
+  result.cap = elems.len
+  result.len = elems.len
+  result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
+  when supportsCopyMem(T):
+    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
+  else:
+    for i in 0..<result.len:
+      result.data[i] = elems[i]
+
+proc len*[T](x: seq[T]): int {.inline.} = x.len
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
new file mode 100644
index 000000000..1958f4974
--- /dev/null
+++ b/lib/core/strs.nim
@@ -0,0 +1,111 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Default string implementation used by Nim's core.
+
+import allocators
+
+type
+  string {.core.} = object
+    len, cap: int
+    data: ptr UncheckedArray[char]
+
+proc nimStringLiteral(x: cstring; len: int): string {.core.} =
+  string(len: len, cap: len, data: x)
+
+template frees(s) = dealloc(s.data, s.cap + 1)
+
+proc `=destroy`(s: var string) =
+  if s.data != nil:
+    frees(s)
+    s.data = nil
+    s.len = 0
+    s.cap = 0
+
+proc `=sink`(a: var string, b: string) =
+  # we hope this is optimized away for not yet alive objects:
+  if a.data != nil and a.data != b.data:
+    frees(a)
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc `=`(a: var string; b: string) =
+  if a.data != nil and a.data != b.data:
+    frees(a)
+    a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap + 1))
+    copyMem(a.data, b.data, a.cap+1)
+
+proc resize(s: var string) =
+  let old = s.cap
+  if old == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
+
+proc add*(s: var string; c: char) =
+  if s.len >= s.cap: resize(s)
+  s.data[s.len] = c
+  s.data[s.len+1] = '\0'
+  inc s.len
+
+proc ensure(s: var string; newLen: int) =
+  let old = s.cap
+  if newLen >= old:
+    s.cap = max((old * 3) shr 1, newLen)
+    if s.cap > 0:
+      s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
+
+proc add*(s: var string; y: string) =
+  if y.len != 0:
+    let newLen = s.len + y.len
+    ensure(s, newLen)
+    copyMem(addr s.data[len], y.data, y.data.len + 1)
+    s.len = newLen
+
+proc len*(s: string): int {.inline.} = s.len
+
+proc newString*(len: int): string =
+  result.len = len
+  result.cap = len
+  if len > 0:
+    result.data = alloc0(len+1)
+
+converter toCString(x: string): cstring {.core.} =
+  if x.len == 0: cstring"" else: cast[cstring](x.data)
+
+proc newStringOfCap*(cap: int): string =
+  result.len = 0
+  result.cap = cap
+  if cap > 0:
+    result.data = alloc(cap+1)
+
+proc `&`*(a, b: string): string =
+  let sum = a.len + b.len
+  result = newStringOfCap(sum)
+  result.len = sum
+  copyMem(addr result.data[0], a.data, a.len)
+  copyMem(addr result.data[a.len], b.data, b.len)
+  if sum > 0:
+    result.data[sum] = '\0'
+
+proc concat(x: openArray[string]): string {.core.} =
+  ## used be the code generator to optimize 'x & y & z ...'
+  var sum = 0
+  for i in 0 ..< x.len: inc(sum, x[i].len)
+  result = newStringOfCap(sum)
+  sum = 0
+  for i in 0 ..< x.len:
+    let L = x[i].len
+    copyMem(addr result.data[sum], x[i].data, L)
+    inc(sum, L)
+
diff --git a/lib/core/typelayouts.nim b/lib/core/typelayouts.nim
new file mode 100644
index 000000000..445ce77c4
--- /dev/null
+++ b/lib/core/typelayouts.nim
@@ -0,0 +1,19 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type
+  TypeLayout* = object
+    size*, alignment*: int
+    destructor*: proc (self: pointer; a: Allocator) {.nimcall.}
+    trace*: proc (self: pointer; a: Allocator) {.nimcall.}
+    when false:
+      construct*: proc (self: pointer; a: Allocator) {.nimcall.}
+      copy*, deepcopy*, sink*: proc (self, other: pointer; a: Allocator) {.nimcall.}
+
+proc getTypeLayout(t: typedesc): ptr TypeLayout {.magic: "getTypeLayout".}
diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim
index 36bd41e9e..17321cc0e 100644
--- a/lib/deprecated/pure/actors.nim
+++ b/lib/deprecated/pure/actors.nim
@@ -18,7 +18,7 @@
 ##      var
 ##        a: ActorPool[int, void]
 ##      createActorPool(a)
-##      for i in 0 .. < 300:
+##      for i in 0 ..< 300:
 ##        a.spawn(i, proc (x: int) {.thread.} = echo x)
 ##      a.join()
 ##
@@ -133,7 +133,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
   newSeq(a.actors, poolSize)
   when Out isnot void:
     open(a.outputs)
-  for i in 0 .. < a.actors.len:
+  for i in 0 ..< a.actors.len:
     a.actors[i] = spawn(poolWorker[In, Out])
 
 proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
@@ -164,8 +164,8 @@ proc terminate*[In, Out](a: var ActorPool[In, Out]) =
   ## resources attached to `a`.
   var t: Task[In, Out]
   t.shutdown = true
-  for i in 0.. <a.actors.len: send(a.actors[i].i, t)
-  for i in 0.. <a.actors.len: join(a.actors[i])
+  for i in 0..<a.actors.len: send(a.actors[i].i, t)
+  for i in 0..<a.actors.len: join(a.actors[i])
   when Out isnot void:
     close(a.outputs)
   a.actors = nil
@@ -227,7 +227,7 @@ when not defined(testing) and isMainModule:
   var
     a: ActorPool[int, void]
   createActorPool(a)
-  for i in 0 .. < 300:
+  for i in 0 ..< 300:
     a.spawn(i, proc (x: int) {.thread.} = echo x)
 
   when false:
diff --git a/lib/deprecated/pure/rawsockets.nim b/lib/deprecated/pure/rawsockets.nim
index ee77b232e..876334f9e 100644
--- a/lib/deprecated/pure/rawsockets.nim
+++ b/lib/deprecated/pure/rawsockets.nim
@@ -3,12 +3,12 @@ export nativesockets
 
 {.warning: "rawsockets module is deprecated, use nativesockets instead".}
 
-template newRawSocket*(domain, sockType, protocol: cint): expr =
+template newRawSocket*(domain, sockType, protocol: cint): untyped =
   {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
   newNativeSocket(domain, sockType, protocol)
 
 template newRawSocket*(domain: Domain = AF_INET,
                        sockType: SockType = SOCK_STREAM,
-                       protocol: Protocol = IPPROTO_TCP): expr =
+                       protocol: Protocol = IPPROTO_TCP): untyped =
   {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
   newNativeSocket(domain, sockType, protocol)
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
index 153db9ed8..f068c7d56 100644
--- a/lib/deprecated/pure/sockets.nim
+++ b/lib/deprecated/pure/sockets.nim
@@ -262,7 +262,7 @@ proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
 
   # TODO: Perhaps this should just raise EOS when an error occurs.
   when defined(Windows):
-    result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
+    result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered)
   else:
     result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)
 
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index a42950557..1459f0d7e 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -74,7 +74,7 @@ type
                        ## converted to nil.
   InstantRow* = object ## a handle that can be
     res: PPGresult     ## used to get a row's
-    line: int          ## column text on demand             
+    line: int          ## column text on demand
   SqlPrepared* = distinct string ## a identifier for the prepared queries
 {.deprecated: [TRow: Row, TDbConn: DbConn, TSqlPrepared: SqlPrepared].}
 
@@ -98,15 +98,18 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   var a = 0
   if args.len > 0 and not string(formatstr).contains("?"):
     dbError("""parameter substitution expects "?" """)
-  for c in items(string(formatstr)):
-    if c == '?':
-      if args[a] == nil:
-        add(result, "NULL")
+  if args.len == 0:
+    return string(formatstr)
+  else:
+    for c in items(string(formatstr)):
+      if c == '?':
+        if args[a] == nil:
+          add(result, "NULL")
+        else:
+          add(result, dbQuote(args[a]))
+        inc(a)
       else:
-        add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
+        add(result, c)
 
 proc tryExec*(db: DbConn, query: SqlQuery,
               args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
@@ -172,7 +175,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
   return SqlPrepared(stmtName)
 
 proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
-  for col in 0..cols-1:
+  for col in 0'i32..cols-1:
     setLen(r[col], 0)
     let x = pqgetvalue(res, line, col)
     if x.isNil:
@@ -188,7 +191,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
   var res = setupQuery(db, query, args)
   var L = pqnfields(res)
   var result = newRow(L)
-  for i in 0..pqntuples(res)-1:
+  for i in 0'i32..pqntuples(res)-1:
     setRow(res, result, i, L)
     yield result
   pqclear(res)
@@ -199,7 +202,7 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
   var res = setupQuery(db, stmtName, args)
   var L = pqNfields(res)
   var result = newRow(L)
-  for i in 0..pqNtuples(res)-1:
+  for i in 0'i32..pqNtuples(res)-1:
     setRow(res, result, i, L)
     yield result
   pqClear(res)
@@ -210,7 +213,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
   ## same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, query, args)
-  for i in 0..pqNtuples(res)-1:
+  for i in 0'i32..pqNtuples(res)-1:
     yield InstantRow(res: res, line: i)
   pqClear(res)
 
@@ -220,7 +223,7 @@ iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
   ## same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within iterator body.
   var res = setupQuery(db, stmtName, args)
-  for i in 0..pqNtuples(res)-1:
+  for i in 0'i32..pqNtuples(res)-1:
     yield InstantRow(res: res, line: i)
   pqClear(res)
 
@@ -237,7 +240,7 @@ proc getColumnType(res: PPGresult, col: int) : DbType =
   of 21:   return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2)
   of 23:   return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4)
   of 20:   return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8)
-  of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")  
+  of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")
   of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit")
 
   of 18:   return DbType(kind: DbTypeKind.dbFixedChar, name: "char")
@@ -251,7 +254,7 @@ proc getColumnType(res: PPGresult, col: int) : DbType =
   of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4")
   of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8")
 
-  of 790:  return DbType(kind: DbTypeKind.dbDecimal, name: "money")  
+  of 790:  return DbType(kind: DbTypeKind.dbDecimal, name: "money")
   of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric")
 
   of 704:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval")
@@ -274,12 +277,12 @@ proc getColumnType(res: PPGresult, col: int) : DbType =
   of 603: return DbType(kind: DbTypeKind.dbBox, name: "box")
   of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon")
   of 628: return DbType(kind: DbTypeKind.dbLine, name: "line")
-  of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")  
+  of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")
 
   of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr")
   of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr")
   of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet")
-  
+
   of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid")
   of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector")
   of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery")
@@ -361,11 +364,11 @@ proc getColumnType(res: PPGresult, col: int) : DbType =
 
 proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) =
   setLen(columns, L)
-  for i in 0..<L:
+  for i in 0'i32..<L:
     columns[i].name = $pqfname(res, i)
     columns[i].typ = getColumnType(res, i)
     columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched.
-                                               ## Query the system table pg_class to determine exactly which table is referenced.  
+                                               ## Query the system table pg_class to determine exactly which table is referenced.
     #columns[i].primaryKey = libpq does not have a function for that
     #columns[i].foreignKey = libpq does not have a function for that
 
@@ -374,7 +377,7 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
                       {.tags: [ReadDbEffect].} =
   var res = setupQuery(db, query, args)
   setColumnInfo(columns, res, pqnfields(res))
-  for i in 0..<pqntuples(res):
+  for i in 0'i32..<pqntuples(res):
     yield InstantRow(res: res, line: i)
   pqClear(res)
 
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 53dafdda7..21049571f 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -148,7 +148,7 @@ proc setupQuery(db: DbConn, query: SqlQuery,
   if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
 
 proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
-  for col in 0..cols-1:
+  for col in 0'i32..cols-1:
     setLen(r[col], column_bytes(stmt, col)) # set capacity
     setLen(r[col], 0)
     let x = column_text(stmt, col)
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 4013182af..3d4afc0ae 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -155,7 +155,7 @@ type
     ##     -  ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"``
     ##     -  ``"abc".match(re"(\w)\w").captures[-1] == "ab"``
     ##
-    ## ``captureBounds[]: Option[Slice[int]]``
+    ## ``captureBounds[]: Option[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.
@@ -167,7 +167,7 @@ type
     ## ``match: string``
     ##     the full text of the match.
     ##
-    ## ``matchBounds: Slice[int]``
+    ## ``matchBounds: HSlice[int, int]``
     ##     the bounds of the match, as in ``captureBounds[]``
     ##
     ## ``(captureBounds|captures).toTable``
@@ -182,9 +182,9 @@ type
                      ## Not nil.
     str*: string  ## The string that was matched against.
                   ## Not nil.
-    pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match
-                                      ## Other items are the captures
-                                      ## `a` is inclusive start, `b` is exclusive end
+    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
@@ -251,13 +251,13 @@ proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(p
 
 proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
 
-proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] =
+proc `[]`*(pattern: CaptureBounds, i: int): Option[HSlice[int, int]] =
   let pattern = RegexMatch(pattern)
   if pattern.pcreMatchBounds[i + 1].a != -1:
     let bounds = pattern.pcreMatchBounds[i + 1]
     return some(int(bounds.a) .. int(bounds.b-1))
   else:
-    return none(Slice[int])
+    return none(HSlice[int, int])
 
 proc `[]`*(pattern: Captures, i: int): string =
   let pattern = RegexMatch(pattern)
@@ -272,10 +272,10 @@ proc `[]`*(pattern: Captures, i: int): string =
 proc match*(pattern: RegexMatch): string =
   return pattern.captures[-1]
 
-proc matchBounds*(pattern: RegexMatch): Slice[int] =
+proc matchBounds*(pattern: RegexMatch): HSlice[int, int] =
   return pattern.captureBounds[-1].get
 
-proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] =
+proc `[]`*(pattern: CaptureBounds, name: string): Option[HSlice[int, int]] =
   let pattern = RegexMatch(pattern)
   return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
 
@@ -295,13 +295,13 @@ proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
   result = initTable[string, string]()
   toTableImpl(nextVal == nil)
 
-proc toTable*(pattern: CaptureBounds, default = none(Slice[int])):
-    Table[string, Option[Slice[int]]] =
-  result = initTable[string, Option[Slice[int]]]()
+proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
+    Table[string, Option[HSlice[int, int]]] =
+  result = initTable[string, Option[HSlice[int, int]]]()
   toTableImpl(nextVal.isNone)
 
 template itemsImpl(cond: untyped) {.dirty.} =
-  for i in 0 .. <RegexMatch(pattern).pattern.captureCount:
+  for i in 0 ..< RegexMatch(pattern).pattern.captureCount:
     let nextVal = pattern[i]
     # done in this roundabout way to avoid multiple yields (potential code
     # bloat)
@@ -309,13 +309,13 @@ template itemsImpl(cond: untyped) {.dirty.} =
     yield nextYieldVal
 
 
-iterator items*(pattern: CaptureBounds, default = none(Slice[int])): Option[Slice[int]] =
+iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
   itemsImpl(nextVal.isNone)
 
 iterator items*(pattern: Captures, default: string = nil): string =
   itemsImpl(nextVal == nil)
 
-proc toSeq*(pattern: CaptureBounds, default = none(Slice[int])): seq[Option[Slice[int]]] =
+proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
   accumulateResult(pattern.items(default))
 
 proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
@@ -396,8 +396,6 @@ proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study:
 
 # }}}
 
-type UncheckedArray {.unchecked.}[T] = array[0 .. 0, T]
-
 proc destroyRegex(pattern: Regex) =
   pcre.free_substring(cast[cstring](pattern.pcreObj))
   pattern.pcreObj = nil
@@ -412,7 +410,7 @@ proc getNameToNumberTable(pattern: Regex): Table[string, int] =
 
   result = initTable[string, int]()
 
-  for i in 0 .. <entryCount:
+  for i in 0 ..< entryCount:
     let pos = i * entrySize
     let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1
     var name = ""
@@ -464,7 +462,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
   # 1x capture count as slack space for PCRE
   let vecsize = (pattern.captureCount() + 1) * 3
   # div 2 because each element is 2 cints long
-  myResult.pcreMatchBounds = newSeq[Slice[cint]](ceil(vecsize / 2).int)
+  myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]](ceil(vecsize / 2).int)
   myResult.pcreMatchBounds.setLen(vecsize div 3)
 
   let strlen = if endpos == int.high: str.len else: endpos+1
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index e00f91de1..c7f8f336b 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -13,10 +13,6 @@
 ## We had to de-deprecate this module since too much code relies on it
 ## and many people prefer its API over ``nre``'s.
 ##
-## **Note:** The 're' proc defaults to the **extended regular expression
-## syntax** which lets you use whitespace freely to make your regexes readable.
-## However, this means matching whitespace requires ``\s`` or something similar.
-##
 ## 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
@@ -53,9 +49,6 @@ type
   RegexError* = object of ValueError
     ## is raised if the pattern is no valid regular expression.
 
-{.deprecated: [TRegexFlag: RegexFlag, TRegexDesc: RegexDesc, TRegex: Regex,
-    EInvalidRegEx: RegexError].}
-
 proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
   var e: ref RegexError
   new(e)
@@ -78,7 +71,7 @@ proc finalizeRegEx(x: Regex) =
   if not isNil(x.e):
     pcre.free_substring(cast[cstring](x.e))
 
-proc re*(s: string, flags = {reExtended, reStudy}): Regex =
+proc re*(s: string, flags = {reStudy}): Regex =
   ## Constructor of regular expressions.
   ##
   ## Note that Nim's
@@ -96,6 +89,13 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
     result.e = pcre.study(result.h, options, addr msg)
     if not isNil(msg): raiseInvalidRegex($msg)
 
+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
@@ -467,8 +467,8 @@ proc replacef*(s: string, sub: Regex, by: string): string =
     prev = match.last + 1
   add(result, substr(s, prev))
 
-proc parallelReplace*(s: string, subs: openArray[
-                      tuple[pattern: Regex, 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 = ""
@@ -487,13 +487,20 @@ proc parallelReplace*(s: string, subs: openArray[
   # copy the rest:
   add(result, substr(s, i))
 
+proc parallelReplace*(s: string, subs: openArray[
+                      tuple[pattern: Regex, repl: string]]): string {.deprecated.} =
+  ## Returns a modified copy of ``s`` with the substitutions in ``subs``
+  ## applied in parallel.
+  ## **Deprecated since version 0.18.0**: Use ``multiReplace`` instead.
+  result = multiReplace(s, subs)
+
 proc transformFile*(infile, outfile: 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 ``IOError`` if an
   ## error occurs. This is supposed to be used for quick scripting.
   var x = readFile(infile).string
-  writeFile(outfile, x.parallelReplace(subs))
+  writeFile(outfile, x.multiReplace(subs))
 
 iterator split*(s: string, sep: Regex): string =
   ## Splits the string ``s`` into substrings.
@@ -576,12 +583,12 @@ const ## common regular expressions
     ## describes an URL
 
 when isMainModule:
-  doAssert match("(a b c)", re"\( .* \)")
+  doAssert match("(a b c)", rex"\( .* \)")
   doAssert match("WHiLe", re("while", {reIgnoreCase}))
 
   doAssert "0158787".match(re"\d+")
   doAssert "ABC 0232".match(re"\w+\s+\d+")
-  doAssert "ABC".match(re"\d+ | \w+")
+  doAssert "ABC".match(rex"\d+ | \w+")
 
   {.push warnings:off.}
   doAssert matchLen("key", re(reIdentifier)) == 3
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
new file mode 100644
index 000000000..ec410ee39
--- /dev/null
+++ b/lib/js/asyncjs.nim
@@ -0,0 +1,141 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim Authors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+
+## This module implements types and macros for writing asynchronous code
+## for the JS backend. It provides tools for interaction with JavaScript async API-s
+## and libraries, writing async procedures in Nim and converting callback-based code
+## to promises.
+##
+## A Nim procedure is asynchronous when it includes the ``{.async.}`` pragma. It
+## should always have a ``Future[T]`` return type or not have a return type at all.
+## A ``Future[void]`` return type is assumed by default.
+##
+## This is roughly equivalent to the ``async`` keyword in JavaScript code.
+##
+## .. code-block:: nim
+##  proc loadGame(name: string): Future[Game] {.async.} =
+##    # code
+##
+## should be equivalent to
+##
+## .. code-block:: javascript
+##   async function loadGame(name) {
+##     // code
+##   }
+##
+## A call to an asynchronous procedure usually needs ``await`` to wait for
+## the completion of the ``Future``.
+##
+## .. code-block:: nim
+##   var game = await loadGame(name)
+##
+## Often, you might work with callback-based API-s. You can wrap them with
+## asynchronous procedures using promises and ``newPromise``:
+##
+## .. code-block:: nim
+##   proc loadGame(name: string): Future[Game] =
+##     var promise = newPromise() do (resolve: proc(response: Game)):
+##       cbBasedLoadGame(name) do (game: Game):
+##         resolve(game)
+##     return promise
+##
+## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
+##
+## .. code-block:: nim
+##   proc loadGame(name: string): Future[Game] {.async.}
+##
+## JavaScript compatibility
+## ~~~~~~~~~~~~~~~~~~~~~~~~~
+##
+## Nim currently generates `async/await` JavaScript code which is supported in modern
+## EcmaScript and most modern versions of browsers, Node.js and Electron.
+## If you need to use this module with older versions of JavaScript, you can
+## use a tool that backports the resulting JavaScript code, as babel.
+
+import jsffi
+import macros
+
+when not defined(js) and not defined(nimdoc) and not defined(nimsuggest):
+  {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
+
+type
+  Future*[T] = ref object
+    future*: T
+  ## Wraps the return type of an asynchronous procedure.
+
+  PromiseJs* {.importcpp: "Promise".} = ref object
+  ## A JavaScript Promise
+
+proc replaceReturn(node: var NimNode) =
+  var z = 0
+  for s in node:
+    var son = node[z]
+    if son.kind == nnkReturnStmt:
+      node[z] = nnkReturnStmt.newTree(nnkCall.newTree(ident("jsResolve"), son[0]))
+    elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result":
+      node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(ident("jsResolve"), son[1]))
+    else:
+      replaceReturn(son)
+    inc z
+
+proc isFutureVoid(node: NimNode): bool =
+  result = node.kind == nnkBracketExpr and
+           node[0].kind == nnkIdent and $node[0] == "Future" and
+           node[1].kind == nnkIdent and $node[1] == "void"
+
+proc generateJsasync(arg: NimNode): NimNode =
+  assert arg.kind == nnkProcDef
+  result = arg
+  var isVoid = false
+  var jsResolveNode = ident("jsResolve")
+
+  if arg.params[0].kind == nnkEmpty:
+    result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
+    isVoid = true
+  elif isFutureVoid(arg.params[0]):
+    isVoid = true
+
+  var code = result.body
+  replaceReturn(code)
+  result.body = nnkStmtList.newTree()
+
+  if len(code) > 0:
+    var awaitFunction = quote:
+      proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
+    result.body.add(awaitFunction)
+
+    var resolve: NimNode
+    if isVoid:
+      resolve = quote:
+        var `jsResolveNode` {.importcpp: "undefined".}: Future[void]
+    else:
+      resolve = quote:
+        proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
+    result.body.add(resolve)
+  else:
+    result.body = newEmptyNode()
+  for child in code:
+    result.body.add(child)
+
+  if len(code) > 0 and isVoid:
+    var voidFix = quote:
+      return `jsResolveNode`
+    result.body.add(voidFix)
+
+  result.pragma = quote:
+    {.codegenDecl: "async function $2($3)".}
+
+
+macro async*(arg: untyped): untyped =
+  ## Macro which converts normal procedures into
+  ## javascript-compatible async procedures
+  generateJsasync(arg)
+
+proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
+  ## A helper for wrapping callback-based functions
+  ## into promises and async procedures
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index cdefc772c..aa7f5d839 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -134,9 +134,9 @@ type
 
   # https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
   HtmlElement* = ref object of Element
-    contentEditable*: string
+    contentEditable*: cstring
     isContentEditable*: bool
-    dir*: string
+    dir*: cstring
     offsetHeight*: int
     offsetWidth*: int
     offsetLeft*: int
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 13eb1e759..f34efe9a2 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -177,7 +177,7 @@ proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .}
   ## and not strings or numbers, this is a *comparison of references*.
 
 {. experimental .}
-macro `.`*(obj: JsObject, field: static[cstring]): JsObject =
+macro `.`*(obj: JsObject, field: untyped): JsObject =
   ## Experimental dot accessor (get) for type JsObject.
   ## Returns the value of a property of name `field` from a JsObject `x`.
   ##
@@ -196,14 +196,14 @@ macro `.`*(obj: JsObject, field: static[cstring]): JsObject =
       helper(`obj`)
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName(field)
+      mangledNames[$field] = $mangleJsName($field)
     let importString = "#." & mangledNames[$field]
     result = quote do:
       proc helper(o: JsObject): JsObject
         {. importcpp: `importString`, gensym .}
       helper(`obj`)
 
-macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
+macro `.=`*(obj: JsObject, field, value: untyped): untyped =
   ## Experimental dot accessor (set) for type JsObject.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
   if validJsName($field):
@@ -214,7 +214,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
       helper(`obj`, `value`)
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName(field)
+      mangledNames[$field] = $mangleJsName($field)
     let importString = "#." & mangledNames[$field] & " = #"
     result = quote do:
       proc helper(o: JsObject, v: auto)
@@ -222,7 +222,7 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
       helper(`obj`, `value`)
 
 macro `.()`*(obj: JsObject,
-             field: static[cstring],
+             field: untyped,
              args: varargs[JsObject, jsFromAst]): JsObject =
   ## Experimental "method call" operator for type JsObject.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
@@ -245,7 +245,7 @@ macro `.()`*(obj: JsObject,
     importString = "#." & $field & "(@)"
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName(field)
+      mangledNames[$field] = $mangleJsName($field)
     importString = "#." & mangledNames[$field] & "(@)"
   result = quote:
     proc helper(o: JsObject): JsObject
@@ -257,7 +257,7 @@ macro `.()`*(obj: JsObject,
     result[1].add args[idx].copyNimTree
 
 macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
-                                   field: static[cstring]): V =
+                                   field: untyped): V =
   ## Experimental dot accessor (get) for type JsAssoc.
   ## Returns the value of a property of name `field` from a JsObject `x`.
   var importString: string
@@ -265,7 +265,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
     importString = "#." & $field
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName(field)
+      mangledNames[$field] = $mangleJsName($field)
     importString = "#." & mangledNames[$field]
   result = quote do:
     proc helper(o: type(`obj`)): `obj`.V
@@ -273,7 +273,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`)
 
 macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
-                                    field: static[cstring],
+                                    field: untyped,
                                     value: V): untyped =
   ## Experimental dot accessor (set) for type JsAssoc.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
@@ -282,7 +282,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
     importString = "#." & $field & " = #"
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName(field)
+      mangledNames[$field] = $mangleJsName($field)
     importString = "#." & mangledNames[$field] & " = #"
   result = quote do:
     proc helper(o: type(`obj`), v: `obj`.V)
@@ -290,7 +290,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`, `value`)
 
 macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
-                                           field: static[cstring],
+                                           field: untyped,
                                            args: varargs[untyped]): auto =
   ## Experimental "method call" operator for type JsAssoc.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 34a2e43e7..31075bbd2 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -70,7 +70,7 @@ __clang__
 #if defined(_MSC_VER)
 #  pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309)
 #  pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706)
-#  pragma warning(disable: 4710 4711 4774 4800 4820 4996 4090)
+#  pragma warning(disable: 4710 4711 4774 4800 4820 4996 4090 4297)
 #endif
 /* ------------------------------------------------------------------------- */
 
@@ -159,6 +159,7 @@ __clang__
 /* ------------------------------------------------------------------- */
 
 #if defined(WIN32) || defined(_WIN32) /* only Windows has this mess... */
+#  define N_LIB_PRIVATE
 #  define N_CDECL(rettype, name) rettype __cdecl name
 #  define N_STDCALL(rettype, name) rettype __stdcall name
 #  define N_SYSCALL(rettype, name) rettype __syscall name
@@ -178,6 +179,7 @@ __clang__
 #  endif
 #  define N_LIB_IMPORT  extern __declspec(dllimport)
 #else
+#  define N_LIB_PRIVATE __attribute__((visibility("hidden")))
 #  if defined(__GNUC__)
 #    define N_CDECL(rettype, name) rettype name
 #    define N_STDCALL(rettype, name) rettype name
@@ -396,15 +398,13 @@ typedef struct TStringDesc* string;
 #define GenericSeqSize sizeof(TGenericSeq)
 #define paramCount() cmdCount
 
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__i386__)
-#  ifndef NAN
-static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff};
-#    define NAN (*(double*) nimNaN)
-#  endif
-#endif
-
+// NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
 #ifndef NAN
-#  define NAN (0.0 / 0.0)
+#  ifndef _HUGE_ENUF
+#    define _HUGE_ENUF  1e+300  // _HUGE_ENUF*_HUGE_ENUF must overflow
+#  endif
+#  define NAN_INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
+#  define NAN ((float)(NAN_INFINITY * 0.0F))
 #endif
 
 #ifndef INF
@@ -482,7 +482,6 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
    On disagreement, your C compiler will say something like:
    "error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
 typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
-#endif
 
 #ifdef  __cplusplus
 #  define NIM_EXTERNC extern "C"
@@ -509,3 +508,5 @@ extern Libc::Env *genodeEnv;
 /* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
 #define NIM_CHECK_SIZE(typ, sz) \
   _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
+
+#endif /* NIMBASE_H */
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 1d396d9e0..2a58854a6 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -31,14 +31,12 @@ type
     state: TokenClass
 
   SourceLanguage* = enum
-    langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava,
+    langNone, langNim, langCpp, langCsharp, langC, langJava,
     langYaml
-{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
-              TGeneralTokenizer: GeneralTokenizer].}
 
 const
   sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
-    "Nim", "Nimrod", "C++", "C#", "C", "Java", "Yaml"]
+    "Nim", "C++", "C#", "C", "Java", "Yaml"]
   tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
     "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
     "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
@@ -49,12 +47,12 @@ const
 
   # The following list comes from doc/keywords.txt, make sure it is
   # synchronized with this array by running the module itself as a test case.
-  nimKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block",
+  nimKeywords = ["addr", "and", "as", "asm", "bind", "block",
     "break", "case", "cast", "concept", "const", "continue", "converter",
     "defer", "discard", "distinct", "div", "do",
     "elif", "else", "end", "enum", "except", "export",
     "finally", "for", "from", "func",
-    "generic", "if", "import", "in", "include",
+    "if", "import", "in", "include",
     "interface", "is", "isnot", "iterator", "let", "macro", "method",
     "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
     "ptr", "raise", "ref", "return", "shl", "shr", "static",
@@ -398,7 +396,6 @@ type
   TokenizerFlag = enum
     hasPreprocessor, hasNestedComments
   TokenizerFlags = set[TokenizerFlag]
-{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
 
 proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
                     flags: TokenizerFlags) =
@@ -888,7 +885,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
 proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
   case lang
   of langNone: assert false
-  of langNim, langNimrod: nimNextToken(g)
+  of langNim: nimNextToken(g)
   of langCpp: cppNextToken(g)
   of langCsharp: csharpNextToken(g)
   of langC: cNextToken(g)
@@ -901,12 +898,11 @@ when isMainModule:
   for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]:
     try:
       let input = string(readFile(filename))
-      keywords = input.split()
+      keywords = input.splitWhitespace()
       break
     except:
       echo filename, " not found"
   doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
-  doAssert keywords.len == nimKeywords.len, "No matching lengths"
-  for i in 0..keywords.len-1:
-    #echo keywords[i], " == ", nimKeywords[i]
+  for i in 0..min(keywords.len, nimKeywords.len)-1:
     doAssert keywords[i] == nimKeywords[i], "Unexpected keyword"
+  doAssert keywords.len == nimKeywords.len, "No matching lengths"
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 53699166f..223fc836a 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -45,8 +45,6 @@ type
   MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
                        arg: string) {.nimcall.} ## what to do in case of an error
   FindFileHandler* = proc (filename: string): string {.nimcall.}
-{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
-              TMsgKind: MsgKind].}
 
 const
   messages: array[MsgKind, string] = [
@@ -127,8 +125,6 @@ type
     bufpos*: int
     line*, col*, baseIndent*: int
     skipPounds*: bool
-{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
-              TLexer: Lexer].}
 
 proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
   tok.kind = tkWord
@@ -288,10 +284,6 @@ type
     hasToc*: bool
 
   EParseError* = object of ValueError
-{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
-              TSharedState: SharedState, TRstParser: RstParser,
-              TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
-              TMsgClass: MsgClass].}
 
 proc whichMsgClass*(k: MsgKind): MsgClass =
   ## returns which message class `k` belongs to.
@@ -341,11 +333,6 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) =
                              p.col + p.tok[p.idx].col, msgKind,
                              p.tok[p.idx].symbol)
 
-when false:
-  proc corrupt(p: RstParser) =
-    assert p.indentStack[0] == 0
-    for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
-
 proc currInd(p: RstParser): int =
   result = p.indentStack[high(p.indentStack)]
 
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 13016bfc0..e6c95b59e 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -46,7 +46,7 @@ type
     target*: OutputTarget
     config*: StringTableRef
     splitAfter*: int          # split too long entries in the TOC
-    listingCounter: int
+    listingCounter*: int
     tocPart*: seq[TocEntry]
     hasToc*: bool
     theIndex: string # Contents of the index file to be dumped at the end.
@@ -61,6 +61,9 @@ type
     seenIndexTerms: Table[string, int] ## \
     ## Keeps count of same text index terms to generate different identifiers
     ## for hyperlinks. See renderIndexTerm proc for details.
+    id*: int               ## A counter useful for generating IDs.
+    onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int;
+                          content: string)
 
   PDoc = var RstGenerator ## Alias to type less.
 
@@ -69,8 +72,9 @@ type
     startLine: int ## The starting line of the code block, by default 1.
     langStr: string ## Input string used to specify the language.
     lang: SourceLanguage ## Type of highlighting, by default none.
-{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
-              TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
+    filename: string
+    testCmd: string
+    status: int
 
 proc init(p: var CodeBlockParams) =
   ## Default initialisation of CodeBlockParams to sane values.
@@ -133,6 +137,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
   g.options = options
   g.findFile = findFile
   g.currentSection = ""
+  g.id = 0
   let fileParts = filename.splitFile
   if fileParts.ext == ".nim":
     g.currentSection = "Module " & fileParts.name
@@ -368,7 +373,6 @@ type
     ##
     ## The value indexed by this IndexEntry is a sequence with the real index
     ## entries found in the ``.idx`` file.
-{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
 
 proc cmp(a, b: IndexEntry): int =
   ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
@@ -798,7 +802,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
   if arg.valid:
     let htmlOut = if isObject:
         "<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"
-        else: "<img src=\"$1\"$2 />"
+      else:
+        "<img src=\"$1\"$2 />"
     dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
           [arg, options])
   if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
@@ -822,13 +827,20 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
     var number: int
     if parseInt(n.getFieldValue, number) > 0:
       params.startLine = number
-  of "file":
+  of "file", "filename":
     # The ``file`` option is a Nim extension to the official spec, it acts
     # like it would for other directives like ``raw`` or ``cvs-table``. This
     # field is dealt with in ``rst.nim`` which replaces the existing block with
     # the referenced file, so we only need to ignore it here to avoid incorrect
     # warning messages.
-    discard
+    params.filename = n.getFieldValue.strip
+  of "test":
+    params.testCmd = n.getFieldValue.strip
+    if params.testCmd.len == 0: params.testCmd = "nim c -r $1"
+  of "status":
+    var status: int
+    if parseInt(n.getFieldValue, status) > 0:
+      params.status = status
   of "default-language":
     params.langStr = n.getFieldValue.strip
     params.lang = params.langStr.getSourceLanguage
@@ -866,7 +878,7 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
   inc d.listingCounter
   let id = $d.listingCounter
   if not params.numberLines:
-    result = (d.config.getOrDefault"doc.listing_start" % id,
+    result = (d.config.getOrDefault"doc.listing_start" % [id, $params.lang],
               d.config.getOrDefault"doc.listing_end" % id)
     return
 
@@ -879,7 +891,7 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
     line.inc
     codeLines.dec
   result.beginTable.add("</pre></td><td>" & (
-      d.config.getOrDefault"doc.listing_start" % id))
+      d.config.getOrDefault"doc.listing_start" % [id, $params.lang]))
   result.endTable = (d.config.getOrDefault"doc.listing_end" % id) &
       "</td></tr></tbody></table>" & (
       d.config.getOrDefault"doc.listing_button" % id)
@@ -900,6 +912,9 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
   var m = n.sons[2].sons[0]
   assert m.kind == rnLeaf
 
+  if params.testCmd.len > 0 and d.onTestSnippet != nil:
+    d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text)
+
   let (blockStart, blockEnd) = buildLinesHTMLTable(d, params, m.text)
 
   dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index 86b977576..c5ed1a873 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -35,18 +35,13 @@ const
   EPOLL_CTL_MOD* = 3          # Change file descriptor epoll_event structure.
 
 type
-  epoll_data* {.importc: "union epoll_data",
+  EpollData* {.importc: "union epoll_data",
       header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
-    #thePtr* {.importc: "ptr".}: pointer
-    fd* {.importc: "fd".}: cint
-    when defined(linux) and defined(amd64):
-      u32: uint32 # this field ensures that binary size is right - it cannot be
-                  # used because its offset is wrong
-    #u64*: uint64
+    u64* {.importc: "u64".}: uint64
 
-  epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
+  EpollEvent* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
     events*: uint32 # Epoll events
-    data*: epoll_data # User data variable
+    data*: EpollData # User data variable
 
 proc epoll_create*(size: cint): cint {.importc: "epoll_create",
     header: "<sys/epoll.h>".}
@@ -60,7 +55,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
   ## Same as epoll_create but with an FLAGS parameter.  The unused SIZE
   ##   parameter has been dropped.
 
-proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
+proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr EpollEvent): cint {.
     importc: "epoll_ctl", header: "<sys/epoll.h>".}
   ## Manipulate an epoll instance "epfd". Returns 0 in case of success,
   ##   -1 in case of error ( the "errno" variable will contain the
@@ -69,7 +64,7 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_
   ##   operation. The "event" parameter describes which events the caller
   ##   is interested in and any associated user data.
 
-proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
+proc epoll_wait*(epfd: cint; events: ptr EpollEvent; maxevents: cint;
                  timeout: cint): cint {.importc: "epoll_wait",
     header: "<sys/epoll.h>".}
   ## Wait for events on an epoll instance "epfd". Returns the number of
@@ -84,7 +79,7 @@ proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
   ##   __THROW.
 
 
-#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
+#proc epoll_pwait*(epfd: cint; events: ptr EpollEvent; maxevents: cint;
 #                  timeout: cint; ss: ptr sigset_t): cint {.
 #    importc: "epoll_pwait", header: "<sys/epoll.h>".}
 # Same as epoll_wait, but the thread's signal mask is temporarily
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
index 01d5e57de..8786ab535 100644
--- a/lib/posix/linux.nim
+++ b/lib/posix/linux.nim
@@ -26,3 +26,5 @@ const
 proc clone*(fn: pointer; child_stack: pointer; flags: cint;
             arg: pointer; ptid: ptr Pid; tls: pointer;
             ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
+
+proc pipe2*(a: array[0..1, cint], flags: cint): cint {.importc, header: "<unistd.h>".}
\ No newline at end of file
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index b635c0b0b..fba35868c 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -609,11 +609,12 @@ proc clock_nanosleep*(a1: ClockId, a2: cint, a3: var Timespec,
 proc clock_settime*(a1: ClockId, a2: var Timespec): cint {.
   importc, header: "<time.h>".}
 
+proc `==`*(a, b: Time): bool {.borrow.}
+proc `-`*(a, b: Time): Time {.borrow.}
 proc ctime*(a1: var Time): cstring {.importc, header: "<time.h>".}
 proc ctime_r*(a1: var Time, a2: cstring): cstring {.importc, header: "<time.h>".}
 proc difftime*(a1, a2: Time): cdouble {.importc, header: "<time.h>".}
 proc getdate*(a1: cstring): ptr Tm {.importc, header: "<time.h>".}
-
 proc gmtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".}
 proc gmtime_r*(a1: var Time, a2: var Tm): ptr Tm {.importc, header: "<time.h>".}
 proc localtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".}
diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim
index c44128b16..9e6211b63 100644
--- a/lib/posix/posix_linux_amd64.nim
+++ b/lib/posix/posix_linux_amd64.nim
@@ -12,8 +12,6 @@
 
 # To be included from posix.nim!
 
-from times import Time
-
 const
   hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
   hasAioH = defined(linux)
@@ -40,13 +38,15 @@ type
 const SIG_HOLD* = cast[SigHandler](2)
 
 type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
+
   Timespec* {.importc: "struct timespec",
                header: "<time.h>", final, pure.} = object ## struct timespec
     tv_sec*: Time  ## Seconds.
     tv_nsec*: clong  ## Nanoseconds.
 
   Dirent* {.importc: "struct dirent",
-             header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+            header: "<dirent.h>", final, pure.} = object ## dirent_t struct
     d_ino*: Ino
     d_off*: Off
     d_reclen*: cushort
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index 7321889a8..01bc1c1e5 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -9,8 +9,6 @@
 
 {.deadCodeElim:on.}
 
-from times import Time
-
 const
   hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
   hasAioH = defined(linux)
@@ -36,6 +34,8 @@ type
 {.deprecated: [TSocketHandle: SocketHandle].}
 
 type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
+
   Timespec* {.importc: "struct timespec",
                header: "<time.h>", final, pure.} = object ## struct timespec
     tv_sec*: Time  ## Seconds.
@@ -209,24 +209,24 @@ type
     st_gid*: Gid          ## Group ID of file.
     st_rdev*: Dev         ## Device ID (if file is character or block special).
     st_size*: Off         ## For regular files, the file size in bytes.
-                           ## For symbolic links, the length in bytes of the
-                           ## pathname contained in the symbolic link.
-                           ## For a shared memory object, the length in bytes.
-                           ## For a typed memory object, the length in bytes.
-                           ## For other file types, the use of this field is
-                           ## unspecified.
+                          ## For symbolic links, the length in bytes of the
+                          ## pathname contained in the symbolic link.
+                          ## For a shared memory object, the length in bytes.
+                          ## For a typed memory object, the length in bytes.
+                          ## For other file types, the use of this field is
+                          ## unspecified.
     when defined(macosx) or defined(android):
-      st_atime*: Time      ## Time of last access.
-      st_mtime*: Time      ## Time of last data modification.
-      st_ctime*: Time      ## Time of last status change.
+      st_atime*: Time     ## Time of last access.
+      st_mtime*: Time     ## Time of last data modification.
+      st_ctime*: Time     ## Time of last status change.
     else:
-      st_atim*: Timespec   ## Time of last access.
-      st_mtim*: Timespec   ## Time of last data modification.
-      st_ctim*: Timespec   ## Time of last status change.
-    st_blksize*: Blksize   ## A file system-specific preferred I/O block size
-                           ## for this object. In some file system types, this
-                           ## may vary from file to file.
-    st_blocks*: Blkcnt     ## Number of blocks allocated for this object.
+      st_atim*: Timespec  ## Time of last access.
+      st_mtim*: Timespec  ## Time of last data modification.
+      st_ctim*: Timespec  ## Time of last status change.
+    st_blksize*: Blksize  ## A file system-specific preferred I/O block size
+                          ## for this object. In some file system types, this
+                          ## may vary from file to file.
+    st_blocks*: Blkcnt    ## Number of blocks allocated for this object.
 
 
   Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 9eee04404..fdf2d7cbb 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -371,3 +371,126 @@ when isMainModule:
   for i in 0 .. high(arr1):
     assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
     assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
+
+
+proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
+  ## A port of std::rotate from c++. Ported from `this reference <http://www.cplusplus.com/reference/algorithm/rotate/>`_.
+  result = first + last - middle
+
+  if first == middle or middle == last:
+    return
+
+  assert first < middle
+  assert middle < last
+
+  # m prefix for mutable
+  var
+    mFirst = first
+    mMiddle = middle
+    next = middle
+
+  swap(arg[mFirst], arg[next])
+  mFirst += 1
+  next  += 1
+  if mFirst == mMiddle:
+    mMiddle = next
+
+  while next != last:
+    swap(arg[mFirst], arg[next])
+    mFirst += 1
+    next += 1
+    if mFirst == mMiddle:
+      mMiddle = next
+
+  next = mMiddle
+  while next != last:
+    swap(arg[mFirst], arg[next])
+    mFirst += 1
+    next += 1
+    if mFirst == mMiddle:
+      mMiddle = next
+    elif next == last:
+      next = mMiddle
+
+proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] =
+  result = newSeq[T](arg.len)
+  for i in 0 ..< first:
+    result[i] = arg[i]
+  let N = last - middle
+  let M = middle - first
+  for i in 0 ..< N:
+    result[first+i] = arg[middle+i]
+  for i in 0 ..< M:
+    result[first+N+i] = arg[first+i]
+  for i in last ..< arg.len:
+    result[i] = arg[i]
+
+proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int =
+  ## Performs a left rotation on a range of elements. If you want to rotate right, use a negative ``dist``.
+  ## Specifically, ``rotateLeft`` rotates the elements at ``slice`` by ``dist`` positions.
+  ## The element at index ``slice.a + dist`` will be at index ``slice.a``.
+  ## The element at index ``slice.b`` will be at ``slice.a + dist -1``.
+  ## The element at index ``slice.a`` will be at ``slice.b + 1 - dist``.
+  ## The element at index ``slice.a + dist - 1`` will be at ``slice.b``.
+  #
+  ## Elements outsize of ``slice`` will be left unchanged.
+  ## The time complexity is linear to ``slice.b - slice.a + 1``.
+  ##
+  ## ``slice``
+  ##   the indices of the element range that should be rotated.
+  ##
+  ## ``dist``
+  ##   the distance in amount of elements that the data should be rotated. Can be negative, can be any number.
+  ##
+  ## .. code-block:: nim
+  ##     var list     = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+  ##     list.rotateLeft(1 .. 8, 3)
+  ##     doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+  let sliceLen = slice.b + 1 - slice.a
+  let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
+  arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1)
+
+proc rotateLeft*[T](arg: var openarray[T]; dist: int): int =
+  ## default arguments for slice, so that this procedure operates on the entire
+  ## ``arg``, and not just on a part of it.
+  let arglen = arg.len
+  let distLeft = ((dist mod arglen) + arglen) mod arglen
+  arg.rotateInternal(0, distLeft, arglen)
+
+proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq[T] =
+  ## same as ``rotateLeft``, just with the difference that it does
+  ## not modify the argument. It creates a new ``seq`` instead
+  let sliceLen = slice.b + 1 - slice.a
+  let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
+  arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1)
+
+proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] =
+  ## same as ``rotateLeft``, just with the difference that it does
+  ## not modify the argument. It creates a new ``seq`` instead
+  let arglen = arg.len
+  let distLeft = ((dist mod arglen) + arglen) mod arglen
+  arg.rotatedInternal(0, distLeft, arg.len)
+
+when isMainModule:
+  var list     = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+  let list2    = list.rotatedLeft(1 ..< 9, 3)
+  let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+
+  doAssert list.rotateLeft(1 ..< 9, 3) == 6
+  doAssert list  ==  expected
+  doAssert list2 == @expected
+
+  var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx"
+
+  doAssert s0.rotateLeft(3 ..< 10, 3) == 7
+  doAssert s0 == "xxxdefgabcxxx"
+  doAssert s1.rotateLeft(3 ..< 10, 2) == 8
+  doAssert s1 == "xxxcdefgabxxx"
+  doAssert s2.rotateLeft(3 ..< 10, 4) == 6
+  doAssert s2 == "xxxefgabcdxxx"
+  doAssert s3.rotateLeft(3 ..< 10, -3) == 6
+  doAssert s3 == "xxxefgabcdxxx"
+  doAssert s4.rotateLeft(3 ..< 10, -10) == 6
+  doAssert s4 == "xxxefgabcdxxx"
+  doAssert s5.rotateLeft(3 ..< 10, 11) == 6
+  doAssert s5 == "xxxefgabcdxxx"
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 281d5b848..42ffa236c 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -9,8 +9,9 @@
 
 include "system/inclrtl"
 
-import os, tables, strutils, times, heapqueue, options, asyncstreams
+import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
 import asyncfutures except callSoon
+
 import nativesockets, net, deques
 
 export Port, SocketFlag
@@ -58,9 +59,10 @@ export asyncfutures, asyncstreams
 ##
 ##   .. code-block::nim
 ##      var future = socket.recv(100)
-##      future.callback =
+##      future.addCallback(
 ##        proc () =
 ##          echo(future.read)
+##      )
 ##
 ## All asynchronous functions returning a ``Future`` will not block. They
 ## will not however return immediately. An asynchronous function will have
@@ -136,6 +138,7 @@ export asyncfutures, asyncstreams
 ## and occasionally the compilation may fail altogether.
 ## As such it is better to use the former style when possible.
 ##
+##
 ## Discarding futures
 ## ------------------
 ##
@@ -165,18 +168,20 @@ type
     timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
     callbacks*: Deque[proc ()]
 
-proc processTimers(p: PDispatcherBase) {.inline.} =
+proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
   #Process just part if timers at a step
   var count = p.timers.len
   let t = epochTime()
   while count > 0 and t >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
     dec count
+    didSomeWork = true
 
-proc processPendingCallbacks(p: PDispatcherBase) =
+proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
   while p.callbacks.len > 0:
     var cb = p.callbacks.popFirst()
     cb()
+    didSomeWork = true
 
 proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
   # If dispatcher has active timers this proc returns the timeout
@@ -226,6 +231,12 @@ when defined(windows) or defined(nimdoc):
       ovl: PCustomOverlapped
     PostCallbackDataPtr = ptr PostCallbackData
 
+    AsyncEventImpl = object
+      hEvent: Handle
+      hWaiter: Handle
+      pcd: PostCallbackDataPtr
+    AsyncEvent* = ptr AsyncEventImpl
+
     Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
   {.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
                 TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
@@ -275,14 +286,13 @@ when defined(windows) or defined(nimdoc):
     let p = getGlobalDispatcher()
     p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
 
-  proc poll*(timeout = 500) =
-    ## Waits for completion events and processes them. Raises ``ValueError``
-    ## if there are no pending operations.
+  proc runOnce(timeout = 500): bool =
     let p = getGlobalDispatcher()
     if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
+    result = false
     if p.handles.len != 0:
       let at = p.adjustedTimeout(timeout)
       var llTimeout =
@@ -295,6 +305,7 @@ when defined(windows) or defined(nimdoc):
       let res = getQueuedCompletionStatus(p.ioPort,
           addr lpNumberOfBytesTransferred, addr lpCompletionKey,
           cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
+      result = true
 
       # http://stackoverflow.com/a/12277264/492186
       # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
@@ -324,17 +335,18 @@ when defined(windows) or defined(nimdoc):
         else:
           if errCode.int32 == WAIT_TIMEOUT:
             # Timed out
-            discard
+            result = false
           else: raiseOSError(errCode)
 
     # Timer processing.
-    processTimers(p)
+    processTimers(p, result)
     # Callback queue processing
-    processPendingCallbacks(p)
+    processPendingCallbacks(p, result)
+
 
-  var connectExPtr: pointer = nil
-  var acceptExPtr: pointer = nil
-  var getAcceptExSockAddrsPtr: pointer = nil
+  var acceptEx: WSAPROC_ACCEPTEX
+  var connectEx: WSAPROC_CONNECTEX
+  var getAcceptExSockAddrs: WSAPROC_GETACCEPTEXSOCKADDRS
 
   proc initPointer(s: SocketHandle, fun: var pointer, guid: var GUID): bool =
     # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
@@ -346,56 +358,19 @@ when defined(windows) or defined(nimdoc):
 
   proc initAll() =
     let dummySock = newNativeSocket()
-    if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
+    if dummySock == INVALID_SOCKET:
       raiseOSError(osLastError())
-    if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
+    var fun: pointer = nil
+    if not initPointer(dummySock, fun, WSAID_CONNECTEX):
       raiseOSError(osLastError())
-    if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
+    connectEx = cast[WSAPROC_CONNECTEX](fun)
+    if not initPointer(dummySock, fun, WSAID_ACCEPTEX):
       raiseOSError(osLastError())
-
-  proc connectEx(s: SocketHandle, name: ptr SockAddr, namelen: cint,
-                  lpSendBuffer: pointer, dwSendDataLength: Dword,
-                  lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool =
-    if connectExPtr.isNil: raise newException(ValueError, "Need to initialise ConnectEx().")
-    let fun =
-      cast[proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
-         lpSendBuffer: pointer, dwSendDataLength: Dword,
-         lpdwBytesSent: PDword, lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](connectExPtr)
-
-    result = fun(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
-         lpOverlapped)
-
-  proc acceptEx(listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer,
-                 dwReceiveDataLength, dwLocalAddressLength,
-                 dwRemoteAddressLength: Dword, lpdwBytesReceived: PDword,
-                 lpOverlapped: POVERLAPPED): bool =
-    if acceptExPtr.isNil: raise newException(ValueError, "Need to initialise AcceptEx().")
-    let fun =
-      cast[proc (listenSock, acceptSock: SocketHandle, lpOutputBuffer: pointer,
-                 dwReceiveDataLength, dwLocalAddressLength,
-                 dwRemoteAddressLength: Dword, lpdwBytesReceived: PDword,
-                 lpOverlapped: POVERLAPPED): bool {.stdcall,gcsafe.}](acceptExPtr)
-    result = fun(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
-        dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
-        lpOverlapped)
-
-  proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
-      dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: Dword,
-      LocalSockaddr: ptr ptr SockAddr, LocalSockaddrLength: LPInt,
-      RemoteSockaddr: ptr ptr SockAddr, RemoteSockaddrLength: LPInt) =
-    if getAcceptExSockAddrsPtr.isNil:
-      raise newException(ValueError, "Need to initialise getAcceptExSockAddrs().")
-
-    let fun =
-      cast[proc (lpOutputBuffer: pointer,
-                 dwReceiveDataLength, dwLocalAddressLength,
-                 dwRemoteAddressLength: Dword, LocalSockaddr: ptr ptr SockAddr,
-                 LocalSockaddrLength: LPInt, RemoteSockaddr: ptr ptr SockAddr,
-                RemoteSockaddrLength: LPInt) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
-
-    fun(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
-                  dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
-                  RemoteSockaddr, RemoteSockaddrLength)
+    acceptEx = cast[WSAPROC_ACCEPTEX](fun)
+    if not initPointer(dummySock, fun, WSAID_GETACCEPTEXSOCKADDRS):
+      raiseOSError(osLastError())
+    getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](fun)
+    close(dummySock)
 
   proc recv*(socket: AsyncFD, size: int,
              flags = {SocketFlag.SafeDisconn}): Future[string] =
@@ -506,10 +481,7 @@ when defined(windows) or defined(nimdoc):
       proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
-            if bytesCount == 0 and dataBuf.buf[0] == '\0':
-              retFuture.complete(0)
-            else:
-              retFuture.complete(bytesCount)
+            retFuture.complete(bytesCount)
           else:
             if flags.isDisconnectionError(errcode):
               retFuture.complete(0)
@@ -543,10 +515,11 @@ when defined(windows) or defined(nimdoc):
 
   proc send*(socket: AsyncFD, buf: pointer, size: int,
              flags = {SocketFlag.SafeDisconn}): Future[void] =
-    ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
-    ## data has been sent.
-    ## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, you must use GC_ref/GC_unref calls
-    ## to avoid early freeing of the buffer
+    ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future
+    ## will complete once all data has been sent.
+    ##
+    ## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object,
+    ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer.
     verifyPresence(socket)
     var retFuture = newFuture[void]("send")
 
@@ -793,7 +766,7 @@ when defined(windows) or defined(nimdoc):
                                        cast[pointer](p.ovl))
   {.pop.}
 
-  template registerWaitableEvent(mask) =
+  proc registerWaitableEvent(fd: AsyncFD, cb: Callback; mask: Dword) =
     let p = getGlobalDispatcher()
     var flags = (WT_EXECUTEINWAITTHREAD or WT_EXECUTEONLYONCE).Dword
     var hEvent = wsaCreateEvent()
@@ -843,8 +816,8 @@ when defined(windows) or defined(nimdoc):
                                        cast[pointer](pcd), INFINITE, flags):
               # pcd.ovl will be unrefed in poll()
               let err = osLastError()
-              discard wsaCloseEvent(hEvent)
               deallocShared(cast[pointer](pcd))
+              discard wsaCloseEvent(hEvent)
               raiseOSError(err)
             else:
               # we incref `pcd.ovl` and `protect` callback one more time,
@@ -883,16 +856,17 @@ when defined(windows) or defined(nimdoc):
     ##
     ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
     ## so if you can avoid it, please do it. Use `addRead` only if really
-    ## need it (main usecase is adaptation of `unix like` libraries to be
+    ## need it (main usecase is adaptation of unix-like libraries to be
     ## asynchronous on Windows).
-    ## If you use this function, you dont need to use asyncdispatch.recv()
+    ##
+    ## If you use this function, you don't need to use asyncdispatch.recv()
     ## or asyncdispatch.accept(), because they are using IOCP, please use
     ## nativesockets.recv() and nativesockets.accept() instead.
     ##
     ## Be sure your callback ``cb`` returns ``true``, if you want to remove
     ## watch of `read` notifications, and ``false``, if you want to continue
-    ## receiving notifies.
-    registerWaitableEvent(FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
+    ## receiving notifications.
+    registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
 
   proc addWrite*(fd: AsyncFD, cb: Callback) =
     ## Start watching the file descriptor for write availability and then call
@@ -900,43 +874,213 @@ when defined(windows) or defined(nimdoc):
     ##
     ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
     ## so if you can avoid it, please do it. Use `addWrite` only if really
-    ## need it (main usecase is adaptation of `unix like` libraries to be
+    ## need it (main usecase is adaptation of unix-like libraries to be
     ## asynchronous on Windows).
-    ## If you use this function, you dont need to use asyncdispatch.send()
+    ##
+    ## If you use this function, you don't need to use asyncdispatch.send()
     ## or asyncdispatch.connect(), because they are using IOCP, please use
     ## nativesockets.send() and nativesockets.connect() instead.
     ##
     ## Be sure your callback ``cb`` returns ``true``, if you want to remove
     ## watch of `write` notifications, and ``false``, if you want to continue
-    ## receiving notifies.
-    registerWaitableEvent(FD_WRITE or FD_CONNECT or FD_CLOSE)
+    ## receiving notifications.
+    registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE)
+
+  template registerWaitableHandle(p, hEvent, flags, pcd, timeout,
+                                  handleCallback) =
+    let handleFD = AsyncFD(hEvent)
+    pcd.ioPort = p.ioPort
+    pcd.handleFd = handleFD
+    var ol = PCustomOverlapped()
+    GC_ref(ol)
+    ol.data.fd = handleFD
+    ol.data.cb = handleCallback
+    # We need to protect our callback environment value, so GC will not free it
+    # accidentally.
+    ol.data.cell = system.protect(rawEnv(ol.data.cb))
+
+    pcd.ovl = ol
+    if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
+                                    cast[WAITORTIMERCALLBACK](waitableCallback),
+                                    cast[pointer](pcd), timeout.Dword, flags):
+      let err = osLastError()
+      GC_unref(ol)
+      deallocShared(cast[pointer](pcd))
+      discard closeHandle(hEvent)
+      raiseOSError(err)
+    p.handles.incl(handleFD)
+
+  template closeWaitable(handle: untyped) =
+    let waitFd = pcd.waitFd
+    deallocShared(cast[pointer](pcd))
+    p.handles.excl(fd)
+    if unregisterWait(waitFd) == 0:
+        let err = osLastError()
+        if err.int32 != ERROR_IO_PENDING:
+          discard closeHandle(handle)
+          raiseOSError(err)
+    if closeHandle(handle) == 0:
+      raiseOSError(osLastError())
+
+  proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
+    ## Registers callback ``cb`` to be called when timer expired.
+    ##
+    ## Parameters:
+    ##
+    ## * ``timeout`` - timeout value in milliseconds.
+    ## * ``oneshot``
+    ##   * `true` - generate only one timeout event
+    ##   * `false` - generate timeout events periodically
+
+    doAssert(timeout > 0)
+    let p = getGlobalDispatcher()
+
+    var hEvent = createEvent(nil, 1, 0, nil)
+    if hEvent == INVALID_HANDLE_VALUE:
+      raiseOSError(osLastError())
+
+    var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
+    var flags = WT_EXECUTEINWAITTHREAD.Dword
+    if oneshot: flags = flags or WT_EXECUTEONLYONCE
+
+    proc timercb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      let res = cb(fd)
+      if res or oneshot:
+        closeWaitable(hEvent)
+      else:
+        # if callback returned `false`, then it wants to be called again, so
+        # we need to ref and protect `pcd.ovl` again, because it will be
+        # unrefed and disposed in `poll()`.
+        GC_ref(pcd.ovl)
+        pcd.ovl.data.cell = system.protect(rawEnv(pcd.ovl.data.cb))
+
+    registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb)
+
+  proc addProcess*(pid: int, cb: Callback) =
+    ## Registers callback ``cb`` to be called when process with process ID
+    ## ``pid`` exited.
+    let p = getGlobalDispatcher()
+    let procFlags = SYNCHRONIZE
+    var hProcess = openProcess(procFlags, 0, pid.Dword)
+    if hProcess == INVALID_HANDLE_VALUE:
+      raiseOSError(osLastError())
+
+    var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
+    var flags = WT_EXECUTEINWAITTHREAD.Dword
+
+    proc proccb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      closeWaitable(hProcess)
+      discard cb(fd)
+
+    registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb)
+
+  proc newAsyncEvent*(): AsyncEvent =
+    ## Creates a new thread-safe ``AsyncEvent`` object.
+    ##
+    ## New ``AsyncEvent`` object is not automatically registered with             # TODO: Why? -- DP
+    ## dispatcher like ``AsyncSocket``.
+    var sa = SECURITY_ATTRIBUTES(
+      nLength: sizeof(SECURITY_ATTRIBUTES).cint,
+      bInheritHandle: 1
+    )
+    var event = createEvent(addr(sa), 0'i32, 0'i32, nil)
+    if event == INVALID_HANDLE_VALUE:
+      raiseOSError(osLastError())
+    result = cast[AsyncEvent](allocShared0(sizeof(AsyncEventImpl)))
+    result.hEvent = event
+
+  proc trigger*(ev: AsyncEvent) =
+    ## Set event ``ev`` to signaled state.
+    if setEvent(ev.hEvent) == 0:
+      raiseOSError(osLastError())
+
+  proc unregister*(ev: AsyncEvent) =
+    ## Unregisters event ``ev``.
+    doAssert(ev.hWaiter != 0, "Event is not registered in the queue!")
+    let p = getGlobalDispatcher()
+    p.handles.excl(AsyncFD(ev.hEvent))
+    if unregisterWait(ev.hWaiter) == 0:
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        raiseOSError(err)
+    ev.hWaiter = 0
+
+  proc close*(ev: AsyncEvent) =
+    ## Closes event ``ev``.
+    let res = closeHandle(ev.hEvent)
+    deallocShared(cast[pointer](ev))
+    if res == 0:
+      raiseOSError(osLastError())
+
+  proc addEvent*(ev: AsyncEvent, cb: Callback) =
+    ## Registers callback ``cb`` to be called when ``ev`` will be signaled
+    doAssert(ev.hWaiter == 0, "Event is already registered in the queue!")
+
+    let p = getGlobalDispatcher()
+    let hEvent = ev.hEvent
+
+    var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
+    var flags = WT_EXECUTEINWAITTHREAD.Dword
+
+    proc eventcb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      if ev.hWaiter != 0:
+        if cb(fd):
+          # we need this check to avoid exception, if `unregister(event)` was
+          # called in callback.
+          deallocShared(cast[pointer](pcd))
+          if ev.hWaiter != 0:
+            unregister(ev)
+        else:
+          # if callback returned `false`, then it wants to be called again, so
+          # we need to ref and protect `pcd.ovl` again, because it will be
+          # unrefed and disposed in `poll()`.
+          GC_ref(pcd.ovl)
+          pcd.ovl.data.cell = system.protect(rawEnv(pcd.ovl.data.cb))
+      else:
+        # if ev.hWaiter == 0, then event was unregistered before `poll()` call.
+        deallocShared(cast[pointer](pcd))
+
+    registerWaitableHandle(p, hEvent, flags, pcd, INFINITE, eventcb)
+    ev.hWaiter = pcd.waitFd
 
   initAll()
 else:
   import selectors
   from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
                     MSG_NOSIGNAL
-
+  const
+    InitCallbackListSize = 4         # initial size of callbacks sequence,
+                                     # associated with file/socket descriptor.
+    InitDelayedCallbackListSize = 64 # initial size of delayed callbacks
+                                     # queue.
   type
     AsyncFD* = distinct cint
     Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
 
-    PData* = ref object of RootRef
-      fd: AsyncFD
-      readCBs: seq[Callback]
-      writeCBs: seq[Callback]
+    AsyncData = object
+      readList: seq[Callback]
+      writeList: seq[Callback]
+
+    AsyncEvent* = distinct SelectEvent
 
     PDispatcher* = ref object of PDispatcherBase
-      selector: Selector
+      selector: Selector[AsyncData]
   {.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
 
   proc `==`*(x, y: AsyncFD): bool {.borrow.}
+  proc `==`*(x, y: AsyncEvent): bool {.borrow.}
+
+  template newAsyncData(): AsyncData =
+    AsyncData(
+      readList: newSeqOfCap[Callback](InitCallbackListSize),
+      writeList: newSeqOfCap[Callback](InitCallbackListSize)
+    )
 
   proc newDispatcher*(): PDispatcher =
     new result
-    result.selector = newSelector()
+    result.selector = newSelector[AsyncData]()
     result.timers.newHeapQueue()
-    result.callbacks = initDeque[proc ()](64)
+    result.callbacks = initDeque[proc ()](InitDelayedCallbackListSize)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
 
@@ -951,15 +1095,10 @@ else:
       setGlobalDispatcher(newDispatcher())
     result = gDisp
 
-  proc update(fd: AsyncFD, events: set[Event]) =
-    let p = getGlobalDispatcher()
-    assert fd.SocketHandle in p.selector
-    p.selector.update(fd.SocketHandle, events)
-
   proc register*(fd: AsyncFD) =
     let p = getGlobalDispatcher()
-    var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
-    p.selector.register(fd.SocketHandle, {}, data.RootRef)
+    var data = newAsyncData()
+    p.selector.registerHandle(fd.SocketHandle, {}, data)
 
   proc closeSocket*(sock: AsyncFD) =
     let disp = getGlobalDispatcher()
@@ -969,80 +1108,158 @@ else:
   proc unregister*(fd: AsyncFD) =
     getGlobalDispatcher().selector.unregister(fd.SocketHandle)
 
+  proc unregister*(ev: AsyncEvent) =
+    getGlobalDispatcher().selector.unregister(SelectEvent(ev))
+
   proc addRead*(fd: AsyncFD, cb: Callback) =
     let p = getGlobalDispatcher()
-    if fd.SocketHandle notin p.selector:
+    var newEvents = {Event.Read}
+    withData(p.selector, fd.SocketHandle, adata) do:
+      adata.readList.add(cb)
+      newEvents.incl(Event.Read)
+      if len(adata.writeList) != 0: newEvents.incl(Event.Write)
+    do:
       raise newException(ValueError, "File descriptor not registered.")
-    p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
-    update(fd, p.selector[fd.SocketHandle].events + {EvRead})
+    p.selector.updateHandle(fd.SocketHandle, newEvents)
 
   proc addWrite*(fd: AsyncFD, cb: Callback) =
     let p = getGlobalDispatcher()
-    if fd.SocketHandle notin p.selector:
+    var newEvents = {Event.Write}
+    withData(p.selector, fd.SocketHandle, adata) do:
+      adata.writeList.add(cb)
+      newEvents.incl(Event.Write)
+      if len(adata.readList) != 0: newEvents.incl(Event.Read)
+    do:
       raise newException(ValueError, "File descriptor not registered.")
-    p.selector[fd.SocketHandle].data.PData.writeCBs.add(cb)
-    update(fd, p.selector[fd.SocketHandle].events + {EvWrite})
-
-  template processCallbacks(callbacks: untyped) =
-    # Callback may add items to ``callbacks`` which causes issues if
-    # we are iterating over it at the same time. We therefore
-    # make a copy to iterate over.
-    let currentCBs = callbacks
-    callbacks = @[]
-    # Using another sequence because callbacks themselves can add
-    # other callbacks.
-    var newCBs: seq[Callback] = @[]
-    for cb in currentCBs:
-      if newCBs.len > 0:
-        # A callback has already returned with EAGAIN, don't call any
-        # others until next `poll`.
-        newCBs.add(cb)
-      else:
-        if not cb(data.fd):
-          # Callback wants to be called again.
-          newCBs.add(cb)
-    callbacks = newCBs & callbacks
+    p.selector.updateHandle(fd.SocketHandle, newEvents)
 
   proc hasPendingOperations*(): bool =
     let p = getGlobalDispatcher()
-    p.selector.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
-
-  proc poll*(timeout = 500) =
+    not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0
+
+  template processBasicCallbacks(ident, rwlist: untyped) =
+    # Process pending descriptor and AsyncEvent callbacks.
+    #
+    # Invoke every callback stored in `rwlist`, until one
+    # returns `false` (which means callback wants to stay
+    # alive). In such case all remaining callbacks will be added
+    # to `rwlist` again, in the order they have been inserted.
+    #
+    # `rwlist` associated with file descriptor MUST BE emptied before
+    # dispatching callback (See https://github.com/nim-lang/Nim/issues/5128),
+    # or it can be possible to fall into endless cycle.
+    var curList: seq[Callback]
+
+    withData(p.selector, ident, adata) do:
+      shallowCopy(curList, adata.rwlist)
+      adata.rwlist = newSeqOfCap[Callback](InitCallbackListSize)
+
+    let newLength = max(len(curList), InitCallbackListSize)
+    var newList = newSeqOfCap[Callback](newLength)
+
+    for cb in curList:
+      if len(newList) > 0:
+        # A callback has already returned with EAGAIN, don't call any others
+        # until next `poll`.
+        newList.add(cb)
+      else:
+        if not cb(fd.AsyncFD):
+          # Callback wants to be called again.
+          newList.add(cb)
+
+    withData(p.selector, ident, adata) do:
+      # descriptor still present in queue.
+      adata.rwlist = newList & adata.rwlist
+      rLength = len(adata.readList)
+      wLength = len(adata.writeList)
+    do:
+      # descriptor was unregistered in callback via `unregister()`.
+      rLength = -1
+      wLength = -1
+
+  template processCustomCallbacks(ident: untyped) =
+    # Process pending custom event callbacks. Custom events are
+    # {Event.Timer, Event.Signal, Event.Process, Event.Vnode}.
+    # There can be only one callback registered with one descriptor,
+    # so there is no need to iterate over list.
+    var curList: seq[Callback]
+
+    withData(p.selector, ident, adata) do:
+      shallowCopy(curList, adata.readList)
+      adata.readList = newSeqOfCap[Callback](InitCallbackListSize)
+
+    let newLength = len(curList)
+    var newList = newSeqOfCap[Callback](newLength)
+
+    var cb = curList[0]
+    if not cb(fd.AsyncFD):
+      newList.add(cb)
+
+    withData(p.selector, ident, adata) do:
+      # descriptor still present in queue.
+      adata.readList = newList & adata.readList
+      if len(adata.readList) == 0:
+        # if no callbacks registered with descriptor, unregister it.
+        p.selector.unregister(fd)
+    do:
+      # descriptor was unregistered in callback via `unregister()`.
+      discard
+
+  proc runOnce(timeout = 500): bool =
     let p = getGlobalDispatcher()
-    if p.selector.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
+    when ioselSupportedPlatform:
+      let customSet = {Event.Timer, Event.Signal, Event.Process,
+                       Event.Vnode}
+
+    if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
-    if p.selector.len > 0:
-      for info in p.selector.select(p.adjustedTimeout(timeout)):
-        let data = PData(info.key.data)
-        assert data.fd == info.key.fd.AsyncFD
-        #echo("In poll ", data.fd.cint)
-        # There may be EvError here, but we handle them in callbacks,
-        # so that exceptions can be raised from `send(...)` and
-        # `recv(...)` routines.
-
-        if EvRead in info.events or info.events == {EvError}:
-          processCallbacks(data.readCBs)
-
-        if EvWrite in info.events or info.events == {EvError}:
-          processCallbacks(data.writeCBs)
-
-        if info.key in p.selector:
-          var newEvents: set[Event]
-          if data.readCBs.len != 0: newEvents = {EvRead}
-          if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
-          if newEvents != info.key.events:
-            update(data.fd, newEvents)
-        else:
-          # FD no longer a part of the selector. Likely been closed
-          # (e.g. socket disconnected).
-          discard
+    result = false
+    if not p.selector.isEmpty():
+      var keys: array[64, ReadyKey]
+      var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
+      for i in 0..<count:
+        var custom = false
+        let fd = keys[i].fd
+        let events = keys[i].events
+        var rLength = 0 # len(data.readList) after callback
+        var wLength = 0 # len(data.writeList) after callback
+
+        if Event.Read in events or events == {Event.Error}:
+          processBasicCallbacks(fd, readList)
+          result = true
+
+        if Event.Write in events or events == {Event.Error}:
+          processBasicCallbacks(fd, writeList)
+          result = true
+
+        if Event.User in events:
+          processBasicCallbacks(fd, readList)
+          custom = true
+          if rLength == 0:
+            p.selector.unregister(fd)
+          result = true
+
+        when ioselSupportedPlatform:
+          if (customSet * events) != {}:
+            custom = true
+            processCustomCallbacks(fd)
+            result = true
+
+        # because state `data` can be modified in callback we need to update
+        # descriptor events with currently registered callbacks.
+        if not custom:
+          var newEvents: set[Event] = {}
+          if rLength != -1 and wLength != -1:
+            if rLength > 0: incl(newEvents, Event.Read)
+            if wLength > 0: incl(newEvents, Event.Write)
+            p.selector.updateHandle(SocketHandle(fd), newEvents)
 
     # Timer processing.
-    processTimers(p)
+    processTimers(p, result)
     # Callback queue processing
-    processPendingCallbacks(p)
+    processPendingCallbacks(p, result)
 
   proc recv*(socket: AsyncFD, size: int,
              flags = {SocketFlag.SafeDisconn}): Future[string] =
@@ -1075,7 +1292,7 @@ else:
     return retFuture
 
   proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
-                  flags = {SocketFlag.SafeDisconn}): Future[int] =
+                 flags = {SocketFlag.SafeDisconn}): Future[int] =
     var retFuture = newFuture[int]("recvInto")
 
     proc cb(sock: AsyncFD): bool =
@@ -1216,6 +1433,68 @@ else:
     addRead(socket, cb)
     return retFuture
 
+  when ioselSupportedPlatform:
+
+    proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
+      ## Start watching for timeout expiration, and then call the
+      ## callback ``cb``.
+      ## ``timeout`` - time in milliseconds,
+      ## ``oneshot`` - if ``true`` only one event will be dispatched,
+      ## if ``false`` continuous events every ``timeout`` milliseconds.
+      let p = getGlobalDispatcher()
+      var data = newAsyncData()
+      data.readList.add(cb)
+      p.selector.registerTimer(timeout, oneshot, data)
+
+    proc addSignal*(signal: int, cb: Callback) =
+      ## Start watching signal ``signal``, and when signal appears, call the
+      ## callback ``cb``.
+      let p = getGlobalDispatcher()
+      var data = newAsyncData()
+      data.readList.add(cb)
+      p.selector.registerSignal(signal, data)
+
+    proc addProcess*(pid: int, cb: Callback) =
+      ## Start watching for process exit with pid ``pid``, and then call
+      ## the callback ``cb``.
+      let p = getGlobalDispatcher()
+      var data = newAsyncData()
+      data.readList.add(cb)
+      p.selector.registerProcess(pid, data)
+
+  proc newAsyncEvent*(): AsyncEvent =
+    ## Creates new ``AsyncEvent``.
+    result = AsyncEvent(newSelectEvent())
+
+  proc trigger*(ev: AsyncEvent) =
+    ## Sets new ``AsyncEvent`` to signaled state.
+    trigger(SelectEvent(ev))
+
+  proc close*(ev: AsyncEvent) =
+    ## Closes ``AsyncEvent``
+    close(SelectEvent(ev))
+
+  proc addEvent*(ev: AsyncEvent, cb: Callback) =
+    ## Start watching for event ``ev``, and call callback ``cb``, when
+    ## ev will be set to signaled state.
+    let p = getGlobalDispatcher()
+    var data = newAsyncData()
+    data.readList.add(cb)
+    p.selector.registerEvent(SelectEvent(ev), data)
+
+proc drain*(timeout = 500) =
+  ## Waits for completion events and processes them. Raises ``ValueError``
+  ## if there are no pending operations. In contrast to ``poll`` this
+  ## processes as many events as are available.
+  if runOnce(timeout):
+    while hasPendingOperations() and runOnce(0): discard
+
+proc poll*(timeout = 500) =
+  ## Waits for completion events and processes them. Raises ``ValueError``
+  ## if there are no pending operations. This runs the underlying OS
+  ## `epoll`:idx: or `kqueue`:idx: primitive only once.
+  discard runOnce(timeout)
+
 # Common procedures between current and upcoming asyncdispatch
 include includes.asynccommon
 
@@ -1269,7 +1548,7 @@ proc send*(socket: AsyncFD, data: string,
 
   var copiedData = data
   GC_ref(copiedData) # we need to protect data until send operation is completed
-               # or failed.
+                     # or failed.
 
   let sendFut = socket.send(addr copiedData[0], data.len, flags)
   sendFut.callback =
@@ -1317,7 +1596,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
   ##
   ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
 
-  template addNLIfEmpty(): untyped =
+  template addNLIfEmpty(): typed =
     if result.len == 0:
       result.add("\c\L")
 
@@ -1353,3 +1632,5 @@ proc waitFor*[T](fut: Future[T]): T =
     poll()
 
   fut.read
+
+{.deprecated: [setEvent: trigger].}
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index bebd19611..bcc3ab613 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -1,4 +1,4 @@
-import os, tables, strutils, times, heapqueue, options, deques
+import os, tables, strutils, times, heapqueue, options, deques, cstrutils
 
 # TODO: This shouldn't need to be included, but should ideally be exported.
 type
@@ -217,17 +217,78 @@ proc `callback=`*[T](future: Future[T],
   ## If future has already completed then ``cb`` will be called immediately.
   future.callback = proc () = cb(future)
 
+proc getHint(entry: StackTraceEntry): string =
+  ## We try to provide some hints about stack trace entries that the user
+  ## may not be familiar with, in particular calls inside the stdlib.
+  result = ""
+  if entry.procname == "processPendingCallbacks":
+    if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
+      return "Executes pending callbacks"
+  elif entry.procname == "poll":
+    if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
+      return "Processes asynchronous completion events"
+
+  if entry.procname.endsWith("_continue"):
+    if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0:
+      return "Resumes an async procedure"
+
+proc `$`*(entries: seq[StackTraceEntry]): string =
+  result = ""
+  # Find longest filename & line number combo for alignment purposes.
+  var longestLeft = 0
+  for entry in entries:
+    if entry.procName.isNil: continue
+
+    let left = $entry.filename & $entry.line
+    if left.len > longestLeft:
+      longestLeft = left.len
+
+  var indent = 2
+  # Format the entries.
+  for entry in entries:
+    if entry.procName.isNil:
+      if entry.line == -10:
+        result.add(spaces(indent) & "#[\n")
+        indent.inc(2)
+      else:
+        indent.dec(2)
+        result.add(spaces(indent)& "]#\n")
+      continue
+
+    let left = "$#($#)" % [$entry.filename, $entry.line]
+    result.add((spaces(indent) & "$#$# $#\n") % [
+      left,
+      spaces(longestLeft - left.len + 2),
+      $entry.procName
+    ])
+    let hint = getHint(entry)
+    if hint.len > 0:
+      result.add(spaces(indent+2) & "## " & hint & "\n")
+
 proc injectStacktrace[T](future: Future[T]) =
-  # TODO: Come up with something better.
   when not defined(release):
-    var msg = ""
-    msg.add("\n  " & future.fromProc & "'s lead up to read of failed Future:")
+    const header = "\nAsync traceback:\n"
 
-    if not future.errorStackTrace.isNil and future.errorStackTrace != "":
-      msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
-    else:
-      msg.add("\n    Empty or nil stack trace.")
-    future.error.msg.add(msg)
+    var exceptionMsg = future.error.msg
+    if header in exceptionMsg:
+      # This is messy: extract the original exception message from the msg
+      # containing the async traceback.
+      let start = exceptionMsg.find(header)
+      exceptionMsg = exceptionMsg[0..<start]
+
+
+    var newMsg = exceptionMsg & header
+
+    let entries = getStackTraceEntries(future.error)
+    newMsg.add($entries)
+
+    newMsg.add("Exception message: " & exceptionMsg & "\n")
+    newMsg.add("Exception type:")
+
+    # # For debugging purposes
+    # for entry in getStackTraceEntries(future.error):
+    #   newMsg.add "\n" & $entry
+    future.error.msg = newMsg
 
 proc read*[T](future: Future[T] | FutureVar[T]): T =
   ## Retrieves the value of ``future``. Future must be finished otherwise
@@ -333,7 +394,7 @@ proc all*[T](futs: varargs[Future[T]]): auto =
     let totalFutures = len(futs)
 
     for fut in futs:
-      fut.callback = proc(f: Future[T]) =
+      fut.addCallback proc (f: Future[T]) =
         inc(completedFutures)
         if not retFuture.finished:
           if f.failed:
@@ -355,7 +416,7 @@ proc all*[T](futs: varargs[Future[T]]): auto =
 
     for i, fut in futs:
       proc setCallback(i: int) =
-        fut.callback = proc(f: Future[T]) =
+        fut.addCallback proc (f: Future[T]) =
           inc(completedFutures)
           if not retFuture.finished:
             if f.failed:
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 6d4b85145..ba1615651 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -58,15 +58,18 @@ type
     socket: AsyncSocket
     reuseAddr: bool
     reusePort: bool
+    maxBody: int ## The maximum content-length that will be read for the body.
 
 {.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
   THttpCode: HttpCode, THttpVersion: HttpVersion].}
 
-proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer =
+proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
+                         maxBody = 8388608): AsyncHttpServer =
   ## Creates a new ``AsyncHttpServer`` instance.
   new result
   result.reuseAddr = reuseAddr
   result.reusePort = reusePort
+  result.maxBody = maxBody
 
 proc addHeaders(msg: var string, headers: HttpHeaders) =
   for k, v in headers:
@@ -122,144 +125,157 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
     raise newException(ValueError, "Invalid request protocol. Got: " &
         protocol)
   result.orig = protocol
-  i.inc protocol.parseInt(result.major, i)
+  i.inc protocol.parseSaturatedNatural(result.major, i)
   i.inc # Skip .
-  i.inc protocol.parseInt(result.minor, i)
+  i.inc protocol.parseSaturatedNatural(result.minor, i)
 
 proc sendStatus(client: AsyncSocket, status: string): Future[void] =
   client.send("HTTP/1.1 " & status & "\c\L\c\L")
 
-proc processClient(client: AsyncSocket, address: string,
-                   callback: proc (request: Request):
+proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
+                    client: AsyncSocket,
+                    address: string, lineFut: FutureVar[string],
+                    callback: proc (request: Request):
                       Future[void] {.closure, gcsafe.}) {.async.} =
-  var request: Request
-  request.url = initUri()
-  request.headers = newHttpHeaders()
-  var lineFut = newFutureVar[string]("asynchttpserver.processClient")
-  lineFut.mget() = newStringOfCap(80)
-  var key, value = ""
 
-  while not client.isClosed:
-    # GET /path HTTP/1.1
-    # Header: val
-    # \n
-    request.headers.clear()
-    request.body = ""
-    request.hostname.shallowCopy(address)
-    assert client != nil
-    request.client = client
-
-    # We should skip at least one empty line before the request
-    # https://tools.ietf.org/html/rfc7230#section-3.5
-    for i in 0..1:
-      lineFut.mget().setLen(0)
-      lineFut.clean()
-      await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
-
-      if lineFut.mget == "":
-        client.close()
-        return
+  # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
+  template request(): Request =
+    req.mget()
+
+  # GET /path HTTP/1.1
+  # Header: val
+  # \n
+  request.headers.clear()
+  request.body = ""
+  request.hostname.shallowCopy(address)
+  assert client != nil
+  request.client = client
+
+  # We should skip at least one empty line before the request
+  # https://tools.ietf.org/html/rfc7230#section-3.5
+  for i in 0..1:
+    lineFut.mget().setLen(0)
+    lineFut.clean()
+    await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
+
+    if lineFut.mget == "":
+      client.close()
+      return
 
-      if lineFut.mget.len > maxLine:
-        await request.respondError(Http413)
-        client.close()
+    if lineFut.mget.len > maxLine:
+      await request.respondError(Http413)
+      client.close()
+      return
+    if lineFut.mget != "\c\L":
+      break
+
+  # First line - GET /path HTTP/1.1
+  var i = 0
+  for linePart in lineFut.mget.split(' '):
+    case i
+    of 0:
+      try:
+        # TODO: this is likely slow.
+        request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
+      except ValueError:
+        asyncCheck request.respondError(Http400)
         return
-      if lineFut.mget != "\c\L":
-        break
-
-    # First line - GET /path HTTP/1.1
-    var i = 0
-    for linePart in lineFut.mget.split(' '):
-      case i
-      of 0:
-        try:
-          # TODO: this is likely slow.
-          request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
-        except ValueError:
-          asyncCheck request.respondError(Http400)
-          continue
-      of 1: 
-        try:
-          parseUri(linePart, request.url)
-        except ValueError:
-          asyncCheck request.respondError(Http400) 
-          continue
-      of 2:
-        try:
-          request.protocol = parseProtocol(linePart)
-        except ValueError:
-          asyncCheck request.respondError(Http400)
-          continue
-      else:
-        await request.respondError(Http400)
-        continue
-      inc i
-
-    # Headers
-    while true:
-      i = 0
-      lineFut.mget.setLen(0)
-      lineFut.clean()
-      await client.recvLineInto(lineFut, maxLength=maxLine)
-
-      if lineFut.mget == "":
-        client.close(); return
-      if lineFut.mget.len > maxLine:
-        await request.respondError(Http413)
-        client.close(); return
-      if lineFut.mget == "\c\L": break
-      let (key, value) = parseHeader(lineFut.mget)
-      request.headers[key] = value
-      # Ensure the client isn't trying to DoS us.
-      if request.headers.len > headerLimit:
-        await client.sendStatus("400 Bad Request")
-        request.client.close()
+    of 1:
+      try:
+        parseUri(linePart, request.url)
+      except ValueError:
+        asyncCheck request.respondError(Http400)
         return
+    of 2:
+      try:
+        request.protocol = parseProtocol(linePart)
+      except ValueError:
+        asyncCheck request.respondError(Http400)
+        return
+    else:
+      await request.respondError(Http400)
+      return
+    inc i
 
-    if request.reqMethod == HttpPost:
-      # Check for Expect header
-      if request.headers.hasKey("Expect"):
-        if "100-continue" in request.headers["Expect"]:
-          await client.sendStatus("100 Continue")
-        else:
-          await client.sendStatus("417 Expectation Failed")
-
-    # Read the body
-    # - Check for Content-length header
-    if request.headers.hasKey("Content-Length"):
-      var contentLength = 0
-      if parseInt(request.headers["Content-Length"],
-                  contentLength) == 0:
-        await request.respond(Http400, "Bad Request. Invalid Content-Length.")
-        continue
-      else:
-        request.body = await client.recv(contentLength)
-        if request.body.len != contentLength:
-          await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
-          continue
-    elif request.reqMethod == HttpPost:
-      await request.respond(Http411, "Content-Length required.")
-      continue
-
-    # Call the user's callback.
-    await callback(request)
-
-    if "upgrade" in request.headers.getOrDefault("connection"):
+  # Headers
+  while true:
+    i = 0
+    lineFut.mget.setLen(0)
+    lineFut.clean()
+    await client.recvLineInto(lineFut, maxLength=maxLine)
+
+    if lineFut.mget == "":
+      client.close(); return
+    if lineFut.mget.len > maxLine:
+      await request.respondError(Http413)
+      client.close(); return
+    if lineFut.mget == "\c\L": break
+    let (key, value) = parseHeader(lineFut.mget)
+    request.headers[key] = value
+    # Ensure the client isn't trying to DoS us.
+    if request.headers.len > headerLimit:
+      await client.sendStatus("400 Bad Request")
+      request.client.close()
       return
 
-    # Persistent connections
-    if (request.protocol == HttpVer11 and
-        request.headers.getOrDefault("connection").normalize != "close") or
-       (request.protocol == HttpVer10 and
-        request.headers.getOrDefault("connection").normalize == "keep-alive"):
-      # In HTTP 1.1 we assume that connection is persistent. Unless connection
-      # header states otherwise.
-      # In HTTP 1.0 we assume that the connection should not be persistent.
-      # Unless the connection header states otherwise.
-      discard
+  if request.reqMethod == HttpPost:
+    # Check for Expect header
+    if request.headers.hasKey("Expect"):
+      if "100-continue" in request.headers["Expect"]:
+        await client.sendStatus("100 Continue")
+      else:
+        await client.sendStatus("417 Expectation Failed")
+
+  # Read the body
+  # - Check for Content-length header
+  if request.headers.hasKey("Content-Length"):
+    var contentLength = 0
+    if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
+      await request.respond(Http400, "Bad Request. Invalid Content-Length.")
+      return
     else:
-      request.client.close()
-      break
+      if contentLength > server.maxBody:
+        await request.respondError(Http413)
+        return
+      request.body = await client.recv(contentLength)
+      if request.body.len != contentLength:
+        await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
+        return
+  elif request.reqMethod == HttpPost:
+    await request.respond(Http411, "Content-Length required.")
+    return
+
+  # Call the user's callback.
+  await callback(request)
+
+  if "upgrade" in request.headers.getOrDefault("connection"):
+    return
+
+  # Persistent connections
+  if (request.protocol == HttpVer11 and
+      cmpIgnoreCase(request.headers.getOrDefault("connection"), "close") != 0) or
+     (request.protocol == HttpVer10 and
+      cmpIgnoreCase(request.headers.getOrDefault("connection"), "keep-alive") == 0):
+    # In HTTP 1.1 we assume that connection is persistent. Unless connection
+    # header states otherwise.
+    # In HTTP 1.0 we assume that the connection should not be persistent.
+    # Unless the connection header states otherwise.
+    discard
+  else:
+    request.client.close()
+    return
+
+proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
+                   callback: proc (request: Request):
+                      Future[void] {.closure, gcsafe.}) {.async.} =
+  var request = newFutureVar[Request]("asynchttpserver.processClient")
+  request.mget().url = initUri()
+  request.mget().headers = newHttpHeaders()
+  var lineFut = newFutureVar[string]("asynchttpserver.processClient")
+  lineFut.mget() = newStringOfCap(80)
+
+  while not client.isClosed:
+    await processRequest(server, request, client, address, lineFut, callback)
 
 proc serve*(server: AsyncHttpServer, port: Port,
             callback: proc (request: Request): Future[void] {.closure,gcsafe.},
@@ -280,7 +296,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
     # TODO: Causes compiler crash.
     #var (address, client) = await server.socket.acceptAddr()
     var fut = await server.socket.acceptAddr()
-    asyncCheck processClient(fut.client, fut.address, callback)
+    asyncCheck processClient(server, fut.client, fut.address, callback)
     #echo(f.isNil)
     #echo(f.repr)
 
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 6e7d7993f..8c679929d 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -25,10 +25,10 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
     result = node[0]
 
 template createCb(retFutureSym, iteratorNameSym,
-                  name, futureVarCompletions: untyped) =
+                  strName, identName, futureVarCompletions: untyped) =
   var nameIterVar = iteratorNameSym
   #{.push stackTrace: off.}
-  proc cb0 {.closure.} =
+  proc identName {.closure.} =
     try:
       if not nameIterVar.finished:
         var next = nameIterVar()
@@ -36,11 +36,11 @@ template createCb(retFutureSym, iteratorNameSym,
           if not retFutureSym.finished:
             let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " &
                     "`nil` Future?"
-            raise newException(AssertionError, msg % name)
+            raise newException(AssertionError, msg % strName)
         else:
           {.gcsafe.}:
             {.push hint[ConvFromXtoItselfNotNeeded]: off.}
-            next.callback = (proc() {.closure, gcsafe.})(cb0)
+            next.callback = (proc() {.closure, gcsafe.})(identName)
             {.pop.}
     except:
       futureVarCompletions
@@ -52,7 +52,7 @@ template createCb(retFutureSym, iteratorNameSym,
       else:
         retFutureSym.fail(getCurrentException())
 
-  cb0()
+  identName()
   #{.pop.}
 proc generateExceptionCheck(futSym,
     tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
@@ -61,14 +61,14 @@ proc generateExceptionCheck(futSym,
   else:
     var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
     let errorNode = newDotExpr(futSym, newIdentNode("error"))
-    for i in 1 .. <tryStmt.len:
+    for i in 1 ..< tryStmt.len:
       let exceptBranch = tryStmt[i]
       if exceptBranch[0].kind == nnkStmtList:
         exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
       else:
         var exceptIdentCount = 0
         var ifCond: NimNode
-        for i in 0 .. <exceptBranch.len:
+        for i in 0 ..< exceptBranch.len:
           let child = exceptBranch[i]
           if child.kind == nnkIdent:
             let cond = infix(errorNode, "of", child)
@@ -270,7 +270,7 @@ proc processBody(node, retFutureSym: NimNode,
     return
   else: discard
 
-  for i in 0 .. <result.len:
+  for i in 0 ..< result.len:
     result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
                             futureVarIdents, nil)
 
@@ -287,7 +287,7 @@ proc getName(node: NimNode): string {.compileTime.} =
 
 proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
   result = @[]
-  for i in 1 .. <len(params):
+  for i in 1 ..< len(params):
     expectKind(params[i], nnkIdentDefs)
     if params[i][1].kind == nnkBracketExpr and
        ($params[i][1][0].ident).normalize == "futurevar":
@@ -389,9 +389,12 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
     outerProcBody.add(closureIterator)
 
     # -> createCb(retFuture)
-    #var cbName = newIdentNode("cb")
+    # NOTE: The "_continue" suffix is checked for in asyncfutures.nim to produce
+    # friendlier stack traces:
+    var cbName = genSym(nskProc, prcName & "_continue")
     var procCb = getAst createCb(retFutureSym, iteratorNameSym,
                          newStrLitNode(prcName),
+                         cbName,
                          createFutureVarCompletions(futureVarIdents, nil))
     outerProcBody.add procCb
 
@@ -466,33 +469,19 @@ proc stripAwait(node: NimNode): NimNode =
       node[0][0] = emptyNoopSym
   else: discard
 
-  for i in 0 .. <result.len:
+  for i in 0 ..< result.len:
     result[i] = stripAwait(result[i])
 
 proc splitParamType(paramType: NimNode, async: bool): NimNode =
   result = paramType
   if paramType.kind == nnkInfix and $paramType[0].ident in ["|", "or"]:
-    let firstType = paramType[1]
-    let firstTypeName = $firstType.ident
-    let secondType = paramType[2]
-    let secondTypeName = $secondType.ident
-
-    # Make sure that at least one has the name `async`, otherwise we shouldn't
-    # touch it.
-    if not ("async" in firstTypeName.normalize or
-            "async" in secondTypeName.normalize):
-      return
-
-    if async:
-      if firstTypeName.normalize.startsWith("async"):
-        result = paramType[1]
-      elif secondTypeName.normalize.startsWith("async"):
-        result = paramType[2]
-    else:
-      if not firstTypeName.normalize.startsWith("async"):
-        result = paramType[1]
-      elif not secondTypeName.normalize.startsWith("async"):
-        result = paramType[2]
+    let firstAsync = "async" in ($paramType[1].ident).normalize
+    let secondAsync = "async" in ($paramType[2].ident).normalize
+
+    if firstAsync:
+      result = paramType[if async: 1 else: 2]
+    elif secondAsync:
+      result = paramType[if async: 2 else: 1]
 
 proc stripReturnType(returnType: NimNode): NimNode =
   # Strip out the 'Future' from 'Future[T]'.
@@ -512,7 +501,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) =
   # Retrieve the `T` inside `Future[T]`.
   let returnType = stripReturnType(result[0][3][0])
   result[0][3][0] = splitParamType(returnType, async=false)
-  for i in 1 .. <result[0][3].len:
+  for i in 1 ..< result[0][3].len:
     # Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
     result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false)
@@ -521,7 +510,7 @@ proc splitProc(prc: NimNode): (NimNode, NimNode) =
   result[1] = prc.copyNimTree()
   if result[1][3][0].kind == nnkBracketExpr:
     result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true)
-  for i in 1 .. <result[1][3].len:
+  for i in 1 ..< result[1][3].len:
     # Async proc (1) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
     result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true)
@@ -535,4 +524,4 @@ macro multisync*(prc: untyped): untyped =
   let (sync, asyncPrc) = splitProc(prc)
   result = newStmtList()
   result.add(asyncSingleProc(asyncPrc))
-  result.add(sync)
\ No newline at end of file
+  result.add(sync)
diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim
index d1207603d..3f213c5ea 100644
--- a/lib/pure/bitops.nim
+++ b/lib/pure/bitops.nim
@@ -181,7 +181,7 @@ elif useICC_builtins:
 
 
 proc countSetBits*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Counts the set bits in integer. (also called Hamming weight.)
+  ## Counts the set bits in integer. (also called `Hamming weight`:idx:.)
   # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
   # like GCC and MSVC
   when nimvm:
diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim
index c6a603318..6912b893c 100644
--- a/lib/pure/browsers.nim
+++ b/lib/pure/browsers.nim
@@ -21,24 +21,18 @@ proc openDefaultBrowser*(url: string) =
   ## opens `url` with the user's default browser. This does not block.
   ##
   ## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
-  ## command is used. Under Unix, it is checked if ``gnome-open`` exists and
-  ## used if it does. Next attempt is ``kde-open``, then ``xdg-open``.
-  ## Otherwise the environment variable ``BROWSER`` is used to determine the
-  ## default browser to use.
+  ## command is used. Under Unix, it is checked if ``xdg-open`` exists and
+  ## used if it does. Otherwise the environment variable ``BROWSER`` is 
+  ## used to determine the default browser to use.
   when defined(windows):
-    when useWinUnicode:
-      var o = newWideCString("open")
-      var u = newWideCString(url)
-      discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
-    else:
-      discard shellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
+    var o = newWideCString("open")
+    var u = newWideCString(url)
+    discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
   elif defined(macosx):
     discard execShellCmd("open " & quoteShell(url))
   else:
-    const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
     var u = quoteShell(url)
-    for a in items(attempts):
-      if execShellCmd(a & u) == 0: return
+    if execShellCmd("xdg-open " & u) == 0: return
     for b in getEnv("BROWSER").string.split(PathSep):
       try:
         # we use ``startProcess`` here because we don't want to block!
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 200a4adf1..5de6aa487 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -29,21 +29,8 @@
 ##    writeLine(stdout, "your password: " & myData["password"])
 ##    writeLine(stdout, "</body></html>")
 
-import strutils, os, strtabs, cookies
-
-proc encodeUrl*(s: string): string =
-  ## Encodes a value to be HTTP safe: This means that characters in the set
-  ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
-  ## a space is converted to ``'+'`` and every other character is encoded as
-  ## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
-  result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
-  for i in 0..s.len-1:
-    case s[i]
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
-    of ' ': add(result, '+')
-    else:
-      add(result, '%')
-      add(result, toHex(ord(s[i]), 2))
+import strutils, os, strtabs, cookies, uri
+export uri.encodeUrl, uri.decodeUrl
 
 proc handleHexChar(c: char, x: var int) {.inline.} =
   case c
@@ -52,30 +39,6 @@ proc handleHexChar(c: char, x: var int) {.inline.} =
   of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
   else: assert(false)
 
-proc decodeUrl*(s: string): string =
-  ## Decodes a value from its HTTP representation: This means that a ``'+'``
-  ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
-  ## value) is converted to the character with ordinal number ``xx``, and
-  ## and every other character is carried over.
-  result = newString(s.len)
-  var i = 0
-  var j = 0
-  while i < s.len:
-    case s[i]
-    of '%':
-      var x = 0
-      handleHexChar(s[i+1], x)
-      handleHexChar(s[i+2], x)
-      inc(i, 2)
-      result[j] = chr(x)
-    of '+': result[j] = ' '
-    else: result[j] = s[i]
-    inc(i)
-    inc(j)
-  setLen(result, j)
-
-{.deprecated: [URLDecode: decodeUrl, URLEncode: encodeUrl].}
-
 proc addXmlChar(dest: var string, c: char) {.inline.} =
   case c
   of '&': add(dest, "&amp;")
@@ -101,8 +64,7 @@ type
     methodPost,          ## query uses the POST method
     methodGet            ## query uses the GET method
 
-{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError,
-  XMLencode: xmlEncode].}
+{.deprecated: [TRequestMethod: RequestMethod, ECgi: CgiError].}
 
 proc cgiError*(msg: string) {.noreturn.} =
   ## raises an ECgi exception with message `msg`.
@@ -393,8 +355,3 @@ proc existsCookie*(name: string): bool =
   ## Checks if a cookie of `name` exists.
   if gcookies == nil: gcookies = parseCookies(getHttpCookie())
   result = hasKey(gcookies, name)
-
-when isMainModule:
-  const test1 = "abc\L+def xyz"
-  assert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
-  assert decodeUrl(encodeUrl(test1)) == test1
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index f70a12843..34f5c5470 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -141,8 +141,8 @@ proc excl*[T](c: var CritBitTree[T], key: string) =
 
 proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
   ## Returns true iff `c` does not contain the given `key`. If the key
-  ## does exist, c.excl(key) is performed. 
-  let oldCount = c.count 
+  ## does exist, c.excl(key) is performed.
+  let oldCount = c.count
   var n = exclImpl(c, key)
   result = c.count == oldCount
 
@@ -257,7 +257,7 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node
       p = p.child[dir]
       if q.byte < key.len: top = p
     if not longestMatch:
-      for i in 0 .. <key.len:
+      for i in 0 ..< key.len:
         if p.key[i] != key[i]: return
     result = top
 
@@ -326,7 +326,7 @@ proc `$`*[T](c: CritBitTree[T]): string =
       result.add($key)
       when T isnot void:
         result.add(": ")
-        result.add($val)
+        result.addQuoted(val)
     result.add("}")
 
 when isMainModule:
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index 1bbe9f1ad..328308a9b 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -185,7 +185,7 @@ proc `$`*[T](deq: Deque[T]): string =
   result = "["
   for x in deq:
     if result.len > 1: result.add(", ")
-    result.add($x)
+    result.addQuoted(x)
   result.add("]")
 
 when isMainModule:
@@ -207,9 +207,9 @@ when isMainModule:
   assert($deq == "[4, 56, 6, 789]")
 
   assert deq[0] == deq.peekFirst and deq.peekFirst == 4
-  assert deq[^1] == deq.peekLast and deq.peekLast == 789
+  #assert deq[^1] == deq.peekLast and deq.peekLast == 789
   deq[0] = 42
-  deq[^1] = 7
+  deq[deq.len - 1] = 7
 
   assert 6 in deq and 789 notin deq
   assert deq.find(6) >= 0
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index f86ba1d3f..60869142e 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -1,3 +1,12 @@
+
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2016 Yuriy Glukhov
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+
 ##[ Heap queue algorithm (a.k.a. priority queue). Ported from Python heapq.
 
 Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index f847ddd58..e69acc8d9 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -37,6 +37,14 @@ type
   DoublyLinkedRing*[T] = object ## a doubly linked ring
     head*: DoublyLinkedNode[T]
 
+  SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
+
+  SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T]
+
+  SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T]
+
+  SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
+
 {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
     PDoublyLinkedNode: DoublyLinkedNode,
     TSinglyLinkedNode: SinglyLinkedNodeObj,
@@ -86,137 +94,57 @@ template itemsRingImpl() {.dirty.} =
       it = it.next
       if it == L.head: break
 
-template nodesListImpl() {.dirty.} =
-  var it = L.head
-  while it != nil:
-    var nxt = it.next
-    yield it
-    it = nxt
-
-template nodesRingImpl() {.dirty.} =
-  var it = L.head
-  if it != nil:
-    while true:
-      var nxt = it.next
-      yield it
-      it = nxt
-      if it == L.head: break
-
-template findImpl() {.dirty.} =
-  for x in nodes(L):
-    if x.value == value: return x
-
-iterator items*[T](L: DoublyLinkedList[T]): T =
+iterator items*[T](L: SomeLinkedList[T]): T =
   ## yields every value of `L`.
   itemsListImpl()
 
-iterator items*[T](L: SinglyLinkedList[T]): T =
-  ## yields every value of `L`.
-  itemsListImpl()
-
-iterator items*[T](L: SinglyLinkedRing[T]): T =
-  ## yields every value of `L`.
-  itemsRingImpl()
-
-iterator items*[T](L: DoublyLinkedRing[T]): T =
+iterator items*[T](L: SomeLinkedRing[T]): T =
   ## yields every value of `L`.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedList[T]): var T =
+iterator mitems*[T](L: var SomeLinkedList[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsListImpl()
 
-iterator mitems*[T](L: var SinglyLinkedList[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsListImpl()
-
-iterator mitems*[T](L: var SinglyLinkedRing[T]): var T =
+iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsRingImpl()
-
-iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  while it != nil:
+    var nxt = it.next
+    yield it
+    it = nxt
 
-iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  if it != nil:
+    while true:
+      var nxt = it.next
+      yield it
+      it = nxt
+      if it == L.head: break
 
-template dollarImpl() {.dirty.} =
+proc `$`*[T](L: SomeLinkedCollection[T]): string =
+  ## turns a list into its string representation.
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
-    result.add($x.value)
+    result.addQuoted(x.value)
   result.add("]")
 
-proc `$`*[T](L: SinglyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: SinglyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
+proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
   ## searches in the list for a value. Returns nil if the value does not
   ## exist.
-  findImpl()
-
-proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
+  for x in nodes(L):
+    if x.value == value: return x
 
-proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
+proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
   ## searches in the list for a value. Returns false if the value does not
   ## exist, true otherwise.
   result = find(L, value) != nil
@@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if n.next != nil: n.next.prev = n.prev
   if n.prev != nil: n.prev.next = n.next
 
-
 proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   ## appends a node `n` to `L`. Efficiency: O(1).
   if L.head != nil:
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
index 401422162..ce792d6da 100644
--- a/lib/pure/collections/queues.nim
+++ b/lib/pure/collections/queues.nim
@@ -198,9 +198,8 @@ when isMainModule:
   assert($q == "[4, 56, 6, 789]")
 
   assert q[0] == q.front and q.front == 4
-  assert q[^1] == q.back and q.back == 789
   q[0] = 42
-  q[^1] = 7
+  q[q.len - 1] = 7
 
   assert 6 in q and 789 notin q
   assert q.find(6) >= 0
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index e8e725aa3..06e96ca36 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -13,12 +13,15 @@
 ## were inspired by functional programming languages.
 ##
 ## For functional style programming you may want to pass `anonymous procs
-## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
-## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
+## <manual.html#procedures-anonymous-procs>`_ to procs like ``filter`` to
+## reduce typing. Anonymous procs can use `the special do notation
+## <manual.html#procedures-do-notation>`_
 ## which is more convenient in certain situations.
 
 include "system/inclrtl"
 
+import macros
+
 when not defined(nimhygiene):
   {.pragma: dirty.}
 
@@ -43,12 +46,27 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
       result[i] = itm
       inc(i)
 
-proc cycle*[T](s: seq[T], n: Natural): seq[T] =
-  ## Returns a new sequence with the items of `s` repeated `n` times.
+proc count*[T](s: openArray[T], x: T): int =
+  ## Returns the number of occurrences of the item `x` in the container `s`.
+  ##
+  ## Example:
+  ##
+  ## .. code-block::
+  ##   let
+  ##     s = @[1, 2, 2, 3, 2, 4, 2]
+  ##     c = count(s, 2)
+  ##   assert c == 4
+  for itm in items(s):
+    if itm == x:
+      inc result
+
+proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
+  ## Returns a new sequence with the items of the container `s` repeated
+  ## `n` times.
   ##
   ## Example:
   ##
-  ## .. code-block:
+  ## .. code-block::
   ##
   ##   let
   ##     s = @[1, 2, 3]
@@ -56,7 +74,7 @@ proc cycle*[T](s: seq[T], n: Natural): seq[T] =
   ##   assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
   result = newSeq[T](n * s.len)
   var o = 0
-  for x in 0..<n:
+  for x in 0 ..< n:
     for e in s:
       result[o] = e
       inc o
@@ -66,18 +84,20 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
   ##
   ## Example:
   ##
-  ## .. code-block:
+  ## .. code-block::
   ##
   ##   let
   ##     total = repeat(5, 3)
   ##   assert total == @[5, 5, 5]
   result = newSeq[T](n)
-  for i in 0..<n:
+  for i in 0 ..< n:
     result[i] = x
 
-proc deduplicate*[T](seq1: seq[T]): seq[T] =
+proc deduplicate*[T](s: openArray[T]): seq[T] =
   ## Returns a new sequence without duplicates.
   ##
+  ## Example:
+  ##
   ## .. code-block::
   ##   let
   ##     dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
@@ -87,17 +107,17 @@ proc deduplicate*[T](seq1: seq[T]): seq[T] =
   ##   assert unique1 == @[1, 3, 4, 2, 8]
   ##   assert unique2 == @["a", "c", "d"]
   result = @[]
-  for itm in items(seq1):
+  for itm in items(s):
     if not result.contains(itm): result.add(itm)
 
-{.deprecated: [distnct: deduplicate].}
-
-proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
-  ## Returns a new sequence with a combination of the two input sequences.
+proc zip*[S, T](s1: openArray[S], s2: openArray[T]): seq[tuple[a: S, b: T]] =
+  ## Returns a new sequence with a combination of the two input containers.
   ##
   ## For convenience you can access the returned tuples through the named
-  ## fields `a` and `b`. If one sequence is shorter, the remaining items in the
-  ## longer sequence are discarded. Example:
+  ## fields `a` and `b`. If one container is shorter, the remaining items in
+  ## the longer container are discarded.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -110,15 +130,16 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
   ##   assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
   ##   assert zip1[2].b == 4
   ##   assert zip2[2].b == "three"
-  var m = min(seq1.len, seq2.len)
+  var m = min(s1.len, s2.len)
   newSeq(result, m)
-  for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
+  for i in 0 ..< m:
+    result[i] = (s1[i], s2[i])
 
 proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   ## Splits and distributes a sequence `s` into `num` sub sequences.
   ##
   ## Returns a sequence of `num` sequences. For some input values this is the
-  ## inverse of the `concat <#concat>`_ proc.  The proc will assert in debug
+  ## inverse of the `concat <#concat>`_ proc. The proc will assert in debug
   ## builds if `s` is nil or `num` is less than one, and will likely crash on
   ## release builds.  The input sequence `s` can be empty, which will produce
   ## `num` empty sequences.
@@ -159,48 +180,52 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
     # Use an algorithm which overcounts the stride and minimizes reading limits.
     if extra > 0: inc(stride)
 
-    for i in 0 .. <num:
+    for i in 0 ..< num:
       result[i] = newSeq[T]()
-      for g in first .. <min(s.len, first + stride):
+      for g in first ..< min(s.len, first + stride):
         result[i].add(s[g])
       first += stride
 
   else:
     # Use an undercounting algorithm which *adds* the remainder each iteration.
-    for i in 0 .. <num:
+    for i in 0 ..< num:
       last = first + stride
       if extra > 0:
         extra -= 1
         inc(last)
 
       result[i] = newSeq[T]()
-      for g in first .. <last:
+      for g in first ..< last:
         result[i].add(s[g])
       first = last
 
-
-proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}):
+proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}):
                                                             seq[S]{.inline.} =
   ## Returns a new sequence with the results of `op` applied to every item in
-  ## `data`.
+  ## the container `s`.
   ##
   ## Since the input is not modified you can use this version of ``map`` to
-  ## transform the type of the elements in the input sequence. Example:
+  ## transform the type of the elements in the input container.
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   let
   ##     a = @[1, 2, 3, 4]
   ##     b = map(a, proc(x: int): string = $x)
   ##   assert b == @["1", "2", "3", "4"]
-  newSeq(result, data.len)
-  for i in 0..data.len-1: result[i] = op(data[i])
+  newSeq(result, s.len)
+  for i in 0 ..< s.len:
+    result[i] = op(s[i])
 
-proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
+proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
                                                               {.deprecated.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## Applies `op` to every item in `s` modifying it directly.
   ##
   ## Note that this version of ``map`` requires your input and output types to
-  ## be the same, since they are modified in-place. Example:
+  ## be the same, since they are modified in-place.
+  ##
+  ## Example:
   ##
   ## .. code-block:: nim
   ##   var a = @["1", "2", "3", "4"]
@@ -210,15 +235,16 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
-  for i in 0..data.len-1: op(data[i])
+  for i in 0 ..< s.len: op(s[i])
 
-proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
+proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
                                                               {.inline.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## Applies `op` to every item in `s` modifying it directly.
   ##
   ## Note that this requires your input and output types to
   ## be the same, since they are modified in-place.
   ## The parameter function takes a ``var T`` type parameter.
+  ##
   ## Example:
   ##
   ## .. code-block:: nim
@@ -229,15 +255,16 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
-  for i in 0..data.len-1: op(data[i])
+  for i in 0 ..< s.len: op(s[i])
 
-proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
+proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
                                                               {.inline.} =
-  ## Applies `op` to every item in `data` modifying it directly.
+  ## Applies `op` to every item in `s` modifying it directly.
   ##
   ## Note that this requires your input and output types to
   ## be the same, since they are modified in-place.
   ## The parameter function takes and returns a ``T`` type variable.
+  ##
   ## Example:
   ##
   ## .. code-block:: nim
@@ -248,11 +275,10 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
   ##   echo repr(a)
   ##   # --> ["142", "242", "342", "442"]
   ##
-  for i in 0..data.len-1: data[i] = op(data[i])
-
+  for i in 0 ..< s.len: s[i] = op(s[i])
 
-iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
-  ## Iterates through a sequence and yields every item that fulfills the
+iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
+  ## Iterates through a container and yields every item that fulfills the
   ## predicate.
   ##
   ## Example:
@@ -262,11 +288,11 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
   ##   for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
   ##     echo($n)
   ##   # echoes 4, 8, 4 in separate lines
-  for i in 0..<seq1.len:
-    if pred(seq1[i]):
-      yield seq1[i]
+  for i in 0 ..< s.len:
+    if pred(s[i]):
+      yield s[i]
 
-proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
+proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
                                                                   {.inline.} =
   ## Returns a new sequence with all the items that fulfilled the predicate.
   ##
@@ -280,11 +306,11 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
   ##   assert f1 == @["red", "black"]
   ##   assert f2 == @["yellow"]
   result = newSeq[T]()
-  for i in 0..<seq1.len:
-    if pred(seq1[i]):
-      result.add(seq1[i])
+  for i in 0 ..< s.len:
+    if pred(s[i]):
+      result.add(s[i])
 
-proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
+proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
                                                                 {.inline.} =
   ## Keeps the items in the passed sequence if they fulfilled the predicate.
   ## Same as the ``filter`` proc, but modifies the sequence directly.
@@ -296,12 +322,12 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
   ##   keepIf(floats, proc(x: float): bool = x > 10)
   ##   assert floats == @[13.0, 12.5, 10.1]
   var pos = 0
-  for i in 0 .. <len(seq1):
-    if pred(seq1[i]):
+  for i in 0 ..< len(s):
+    if pred(s[i]):
       if pos != i:
-        shallowCopy(seq1[pos], seq1[i])
+        shallowCopy(s[pos], s[i])
       inc(pos)
-  setLen(seq1, pos)
+  setLen(s, pos)
 
 proc delete*[T](s: var seq[T]; first, last: Natural) =
   ## Deletes in `s` the items at position `first` .. `last`. This modifies
@@ -354,11 +380,12 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
     inc(j)
 
 
-template filterIt*(seq1, pred: untyped): untyped =
+template filterIt*(s, pred: untyped): untyped =
   ## Returns a new sequence with all the items that fulfilled the predicate.
   ##
   ## Unlike the `proc` version, the predicate needs to be an expression using
   ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
+  ##
   ## Example:
   ##
   ## .. code-block::
@@ -368,8 +395,8 @@ template filterIt*(seq1, pred: untyped): untyped =
   ##      notAcceptable = filterIt(temperatures, it > 50 or it < -10)
   ##    assert acceptable == @[-2.0, 24.5, 44.31]
   ##    assert notAcceptable == @[-272.15, 99.9, -113.44]
-  var result = newSeq[type(seq1[0])]()
-  for it {.inject.} in items(seq1):
+  var result = newSeq[type(s[0])]()
+  for it {.inject.} in items(s):
     if pred: result.add(it)
   result
 
@@ -378,6 +405,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
   ##
   ## Unlike the `proc` version, the predicate needs to be an expression using
   ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
+  ##
   ## Example:
   ##
   ## .. code-block::
@@ -385,7 +413,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
   ##   keepItIf(candidates, it.len == 3 and it[0] == 'b')
   ##   assert candidates == @["bar", "baz"]
   var pos = 0
-  for i in 0 .. <len(varSeq):
+  for i in 0 ..< len(varSeq):
     let it {.inject.} = varSeq[i]
     if pred:
       if pos != i:
@@ -393,8 +421,8 @@ template keepItIf*(varSeq: seq, pred: untyped) =
       inc(pos)
   setLen(varSeq, pos)
 
-proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
-  ## Iterates through a sequence and checks if every item fulfills the
+proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
+  ## Iterates through a container and checks if every item fulfills the
   ## predicate.
   ##
   ## Example:
@@ -403,12 +431,12 @@ proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
   ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
   ##   assert all(numbers, proc (x: int): bool = return x < 10) == true
   ##   assert all(numbers, proc (x: int): bool = return x < 9) == false
-  for i in seq1:
+  for i in s:
     if not pred(i):
       return false
   return true
 
-template allIt*(seq1, pred: untyped): bool =
+template allIt*(s, pred: untyped): bool =
   ## Checks if every item fulfills the predicate.
   ##
   ## Example:
@@ -418,14 +446,14 @@ template allIt*(seq1, pred: untyped): bool =
   ##   assert allIt(numbers, it < 10) == true
   ##   assert allIt(numbers, it < 9) == false
   var result = true
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     if not pred:
       result = false
       break
   result
 
-proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
-  ## Iterates through a sequence and checks if some item fulfills the
+proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
+  ## Iterates through a container and checks if some item fulfills the
   ## predicate.
   ##
   ## Example:
@@ -434,12 +462,12 @@ proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
   ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
   ##   assert any(numbers, proc (x: int): bool = return x > 8) == true
   ##   assert any(numbers, proc (x: int): bool = return x > 9) == false
-  for i in seq1:
+  for i in s:
     if pred(i):
       return true
   return false
 
-template anyIt*(seq1, pred: untyped): bool =
+template anyIt*(s, pred: untyped): bool =
   ## Checks if some item fulfills the predicate.
   ##
   ## Example:
@@ -449,7 +477,7 @@ template anyIt*(seq1, pred: untyped): bool =
   ##   assert anyIt(numbers, it > 8) == true
   ##   assert anyIt(numbers, it > 9) == false
   var result = false
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     if pred:
       result = true
       break
@@ -493,7 +521,9 @@ template foldl*(sequence, operation: untyped): untyped =
   ## variables ``a`` and ``b`` for each step of the fold. Since this is a left
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
-  ## 3).  Example:
+  ## 3).
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -527,6 +557,7 @@ template foldl*(sequence, operation, first): untyped =
   ## The ``operation`` parameter should be an expression which uses the variables
   ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the
   ## start value (the first ``a``) and therefor defines the type of the result.
+  ##
   ## Example:
   ##
   ## .. code-block::
@@ -555,7 +586,9 @@ template foldr*(sequence, operation: untyped): untyped =
   ## variables ``a`` and ``b`` for each step of the fold. Since this is a right
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
-  ## (3))). Example:
+  ## (3))).
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -580,13 +613,15 @@ template foldr*(sequence, operation: untyped): untyped =
     result = operation
   result
 
-template mapIt*(seq1, typ, op: untyped): untyped =
+template mapIt*(s, typ, op: untyped): untyped =
   ## Convenience template around the ``map`` proc to reduce typing.
   ##
   ## The template injects the ``it`` variable which you can use directly in an
   ## expression. You also need to pass as `typ` the type of the expression,
   ## since the new returned sequence can have a different type than the
-  ## original.  Example:
+  ## original.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -596,16 +631,18 @@ template mapIt*(seq1, typ, op: untyped): untyped =
   ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
   ##   template instead.
   var result: seq[typ] = @[]
-  for it {.inject.} in items(seq1):
+  for it {.inject.} in items(s):
     result.add(op)
   result
 
 
-template mapIt*(seq1, op: untyped): untyped =
+template mapIt*(s, op: untyped): untyped =
   ## Convenience template around the ``map`` proc to reduce typing.
   ##
   ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. Example:
+  ## expression.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   let
@@ -614,19 +651,19 @@ template mapIt*(seq1, op: untyped): untyped =
   ##   assert strings == @["4", "8", "12", "16"]
   type outType = type((
     block:
-      var it{.inject.}: type(items(seq1));
+      var it{.inject.}: type(items(s));
       op))
   var result: seq[outType]
-  when compiles(seq1.len):
-    let s = seq1
+  when compiles(s.len):
+    let t = s
     var i = 0
     result = newSeq[outType](s.len)
-    for it {.inject.} in s:
+    for it {.inject.} in t:
       result[i] = op
       i += 1
   else:
     result = @[]
-    for it {.inject.} in seq1:
+    for it {.inject.} in s:
       result.add(op)
   result
 
@@ -635,20 +672,23 @@ template applyIt*(varSeq, op: untyped) =
   ##
   ## The template injects the ``it`` variable which you can use directly in an
   ## expression. The expression has to return the same type as the sequence you
-  ## are mutating. Example:
+  ## are mutating.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   var nums = @[1, 2, 3, 4]
   ##   nums.applyIt(it * 3)
   ##   assert nums[0] + nums[3] == 15
-  for i in 0 .. <varSeq.len:
+  for i in 0 ..< varSeq.len:
     let it {.inject.} = varSeq[i]
     varSeq[i] = op
 
 
-
 template newSeqWith*(len: int, init: untyped): untyped =
-  ## creates a new sequence, calling `init` to initialize each value. Example:
+  ## creates a new sequence, calling `init` to initialize each value.
+  ##
+  ## Example:
   ##
   ## .. code-block::
   ##   var seq2D = newSeqWith(20, newSeq[bool](10))
@@ -660,10 +700,56 @@ template newSeqWith*(len: int, init: untyped): untyped =
   ##   var seqRand = newSeqWith(20, random(10))
   ##   echo seqRand
   var result = newSeq[type(init)](len)
-  for i in 0 .. <len:
+  for i in 0 ..< len:
     result[i] = init
   result
 
+proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
+                 filter = nnkLiterals): NimNode =
+  if constructor.kind in filter:
+    result = newNimNode(nnkCall, lineInfoFrom=constructor)
+    result.add op
+    result.add constructor
+  else:
+    result = newNimNode(constructor.kind, lineInfoFrom=constructor)
+    for v in constructor:
+      if nested or v.kind in filter:
+        result.add mapLitsImpl(v, op, nested, filter)
+      else:
+        result.add v
+
+macro mapLiterals*(constructor, op: untyped;
+                   nested = true): untyped =
+  ## applies ``op`` to each of the **atomic** literals like ``3``
+  ## or ``"abc"`` in the specified ``constructor`` AST. This can
+  ## be used to map every array element to some target type:
+  ##
+  ## Example:
+  ##
+  ## .. code-block::
+  ##   let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
+  ##   doAssert x is array[4, int]
+  ##
+  ## Short notation for:
+  ##
+  ## .. code-block::
+  ##   let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
+  ##
+  ## If ``nested`` is true, the literals are replaced everywhere
+  ## in the ``constructor`` AST, otherwise only the first level
+  ## is considered:
+  ##
+  ## .. code-block::
+  ##   mapLiterals((1, ("abc"), 2), float, nested=false)
+  ##
+  ## Produces::
+  ##
+  ##   (float(1), ("abc"), float(2))
+  ##
+  ## There are no constraints for the ``constructor`` AST, it
+  ## works for nested tuples of arrays of sets etc.
+  result = mapLitsImpl(constructor, op, nested.boolVal)
+
 when isMainModule:
   import strutils
   block: # concat test
@@ -674,45 +760,178 @@ when isMainModule:
       total = concat(s1, s2, s3)
     assert total == @[1, 2, 3, 4, 5, 6, 7]
 
-  block: # duplicates test
+  block: # count test
+    let
+      s1 = @[1, 2, 3, 2]
+      s2 = @['a', 'b', 'x', 'a']
+      a1 = [1, 2, 3, 2]
+      a2 = ['a', 'b', 'x', 'a']
+      r0 = count(s1, 0)
+      r1 = count(s1, 1)
+      r2 = count(s1, 2)
+      r3 = count(s2, 'y')
+      r4 = count(s2, 'x')
+      r5 = count(s2, 'a')
+      ar0 = count(a1, 0)
+      ar1 = count(a1, 1)
+      ar2 = count(a1, 2)
+      ar3 = count(a2, 'y')
+      ar4 = count(a2, 'x')
+      ar5 = count(a2, 'a')
+    assert r0 == 0
+    assert r1 == 1
+    assert r2 == 2
+    assert r3 == 0
+    assert r4 == 1
+    assert r5 == 2
+    assert ar0 == 0
+    assert ar1 == 1
+    assert ar2 == 2
+    assert ar3 == 0
+    assert ar4 == 1
+    assert ar5 == 2
+
+  block: # cycle tests
+    let
+      a = @[1, 2, 3]
+      b: seq[int] = @[]
+      c = [1, 2, 3]
+
+    doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+    doAssert a.cycle(0) == @[]
+    #doAssert a.cycle(-1) == @[] # will not compile!
+    doAssert b.cycle(3) == @[]
+    doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+    doAssert c.cycle(0) == @[]
+
+  block: # repeat tests
+    assert repeat(10, 5) == @[10, 10, 10, 10, 10]
+    assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
+    assert repeat([1,2,3], 2) == @[[1,2,3], [1,2,3]]
+
+  block: # deduplicates test
     let
       dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
       dup2 = @["a", "a", "c", "d", "d"]
+      dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4]
+      dup4 = ["a", "a", "c", "d", "d"]
       unique1 = deduplicate(dup1)
       unique2 = deduplicate(dup2)
+      unique3 = deduplicate(dup3)
+      unique4 = deduplicate(dup4)
     assert unique1 == @[1, 3, 4, 2, 8]
     assert unique2 == @["a", "c", "d"]
+    assert unique3 == @[1, 3, 4, 2, 8]
+    assert unique4 == @["a", "c", "d"]
 
   block: # zip test
     let
       short = @[1, 2, 3]
       long = @[6, 5, 4, 3, 2, 1]
       words = @["one", "two", "three"]
+      ashort = [1, 2, 3]
+      along = [6, 5, 4, 3, 2, 1]
+      awords = ["one", "two", "three"]
       zip1 = zip(short, long)
       zip2 = zip(short, words)
+      zip3 = zip(ashort, along)
+      zip4 = zip(ashort, awords)
+      zip5 = zip(ashort, words)
     assert zip1 == @[(1, 6), (2, 5), (3, 4)]
     assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip3 == @[(1, 6), (2, 5), (3, 4)]
+    assert zip4 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip5 == @[(1, "one"), (2, "two"), (3, "three")]
     assert zip1[2].b == 4
     assert zip2[2].b == "three"
+    assert zip3[2].b == 4
+    assert zip4[2].b == "three"
+    assert zip5[2].b == "three"
+
+  block: # distribute tests
+    let numbers = @[1, 2, 3, 4, 5, 6, 7]
+    doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert numbers.distribute(6)[0] == @[1, 2]
+    doAssert numbers.distribute(6)[5] == @[7]
+    let a = @[1, 2, 3, 4, 5, 6, 7]
+    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
+    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
+    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
+    doAssert a.distribute(6, false)  == @[
+      @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
+    doAssert a.distribute(8, false)  == a.distribute(8, true)
+    doAssert a.distribute(90, false) == a.distribute(90, true)
+    var b = @[0]
+    for f in 1 .. 25: b.add(f)
+    doAssert b.distribute(5, true)[4].len == 5
+    doAssert b.distribute(5, false)[4].len == 2
+
+  block: # map test
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      m1 = map(numbers, proc(x: int): int = 2*x)
+      m2 = map(anumbers, proc(x: int): int = 2*x)
+    assert m1 == @[2, 8, 10, 16, 18, 14, 8]
+    assert m2 == @[2, 8, 10, 16, 18, 14, 8]
+
+  block: # apply test
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: var string) = x &= "42")
+    assert a == @["142", "242", "342", "442"]
 
   block: # filter proc test
     let
       colors = @["red", "yellow", "black"]
+      acolors = ["red", "yellow", "black"]
       f1 = filter(colors, proc(x: string): bool = x.len < 6)
       f2 = filter(colors) do (x: string) -> bool : x.len > 5
+      f3 = filter(acolors, proc(x: string): bool = x.len < 6)
+      f4 = filter(acolors) do (x: string) -> bool : x.len > 5
     assert f1 == @["red", "black"]
     assert f2 == @["yellow"]
+    assert f3 == @["red", "black"]
+    assert f4 == @["yellow"]
 
   block: # filter iterator test
     let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    let anumbers = [1, 4, 5, 8, 9, 7, 4]
     assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
       @[4, 8, 4]
+    assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) ==
+      @[4, 8, 4]
 
   block: # keepIf test
     var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
     keepIf(floats, proc(x: float): bool = x > 10)
     assert floats == @[13.0, 12.5, 10.1]
 
+  block: # delete tests
+    let outcome = @[1,1,1,1,1,1,1,1]
+    var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    dest.delete(3, 8)
+    assert outcome == dest, """\
+    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    is [1,1,1,1,1,1,1,1]"""
+
+  block: # insert tests
+    var dest = @[1,1,1,1,1,1,1,1]
+    let
+      src = @[2,2,2,2,2,2]
+      outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    dest.insert(src, 3)
+    assert dest == outcome, """\
+    Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
+    at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
+
   block: # filterIt test
     let
       temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
@@ -726,37 +945,49 @@ when isMainModule:
     keepItIf(candidates, it.len == 3 and it[0] == 'b')
     assert candidates == @["bar", "baz"]
 
-  block: # any
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert any(numbers, proc (x: int): bool = return x > 8) == true
-    assert any(numbers, proc (x: int): bool = return x > 9) == false
-    assert any(len0seq, proc (x: int): bool = return true) == false
-
-  block: # anyIt
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert anyIt(numbers, it > 8) == true
-    assert anyIt(numbers, it > 9) == false
-    assert anyIt(len0seq, true) == false
-
   block: # all
     let
       numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
       len0seq : seq[int] = @[]
     assert all(numbers, proc (x: int): bool = return x < 10) == true
     assert all(numbers, proc (x: int): bool = return x < 9) == false
     assert all(len0seq, proc (x: int): bool = return false) == true
+    assert all(anumbers, proc (x: int): bool = return x < 10) == true
+    assert all(anumbers, proc (x: int): bool = return x < 9) == false
 
   block: # allIt
     let
       numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
       len0seq : seq[int] = @[]
     assert allIt(numbers, it < 10) == true
     assert allIt(numbers, it < 9) == false
     assert allIt(len0seq, false) == true
+    assert allIt(anumbers, it < 10) == true
+    assert allIt(anumbers, it < 9) == false
+
+  block: # any
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      len0seq : seq[int] = @[]
+    assert any(numbers, proc (x: int): bool = return x > 8) == true
+    assert any(numbers, proc (x: int): bool = return x > 9) == false
+    assert any(len0seq, proc (x: int): bool = return true) == false
+    assert any(anumbers, proc (x: int): bool = return x > 8) == true
+    assert any(anumbers, proc (x: int): bool = return x > 9) == false
+
+  block: # anyIt
+    let
+      numbers = @[1, 4, 5, 8, 9, 7, 4]
+      anumbers = [1, 4, 5, 8, 9, 7, 4]
+      len0seq : seq[int] = @[]
+    assert anyIt(numbers, it > 8) == true
+    assert anyIt(numbers, it > 9) == false
+    assert anyIt(len0seq, true) == false
+    assert anyIt(anumbers, it > 8) == true
+    assert anyIt(anumbers, it > 9) == false
 
   block: # toSeq test
     let
@@ -792,56 +1023,13 @@ when isMainModule:
     assert multiplication == 495, "Multiplication is (5*(9*(11)))"
     assert concatenation == "nimiscool"
 
-  block: # delete tests
-    let outcome = @[1,1,1,1,1,1,1,1]
-    var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-    dest.delete(3, 8)
-    assert outcome == dest, """\
-    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-    is [1,1,1,1,1,1,1,1]"""
-
-  block: # insert tests
-    var dest = @[1,1,1,1,1,1,1,1]
-    let
-      src = @[2,2,2,2,2,2]
-      outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-    dest.insert(src, 3)
-    assert dest == outcome, """\
-    Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
-    at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
-
   block: # mapIt tests
     var
       nums = @[1, 2, 3, 4]
       strings = nums.mapIt($(4 * it))
     nums.applyIt(it * 3)
     assert nums[0] + nums[3] == 15
-
-  block: # distribute tests
-    let numbers = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert numbers.distribute(6)[0] == @[1, 2]
-    doAssert numbers.distribute(6)[5] == @[7]
-    let a = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
-    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
-    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
-    doAssert a.distribute(6, false)  == @[
-      @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
-    doAssert a.distribute(8, false)  == a.distribute(8, true)
-    doAssert a.distribute(90, false) == a.distribute(90, true)
-    var b = @[0]
-    for f in 1 .. 25: b.add(f)
-    doAssert b.distribute(5, true)[4].len == 5
-    doAssert b.distribute(5, false)[4].len == 2
+    assert strings[2] == "12"
 
   block: # newSeqWith tests
     var seq2D = newSeqWith(4, newSeq[bool](2))
@@ -850,19 +1038,11 @@ when isMainModule:
     seq2D[0][1] = true
     doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
 
-  block: # cycle tests
-    let
-      a = @[1, 2, 3]
-      b: seq[int] = @[]
-
-    doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
-    doAssert a.cycle(0) == @[]
-    #doAssert a.cycle(-1) == @[] # will not compile!
-    doAssert b.cycle(3) == @[]
-
-  block: # repeat tests
-    assert repeat(10, 5) == @[10, 10, 10, 10, 10]
-    assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
+  block: # mapLiterals tests
+    let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
+    doAssert x is array[4, int]
+    doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
+    doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
 
   when not defined(testing):
     echo "Finished doc tests"
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index dbdf17514..9e9152fc8 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -46,7 +46,7 @@ template default[T](t: typedesc[T]): T =
   var v: T
   v
 
-proc clear*[A](s: var HashSet[A]) = 
+proc clear*[A](s: var HashSet[A]) =
   ## Clears the HashSet back to an empty state, without shrinking
   ## any of the existing storage. O(n) where n is the size of the hash bucket.
   s.counter = 0
@@ -406,7 +406,7 @@ template dollarImpl() {.dirty.} =
   result = "{"
   for key in items(s):
     if result.len > 1: result.add(", ")
-    result.add($key)
+    result.addQuoted(key)
   result.add("}")
 
 proc `$`*[A](s: HashSet[A]): string =
@@ -610,7 +610,7 @@ type
 
 {.deprecated: [TOrderedSet: OrderedSet].}
 
-proc clear*[A](s: var OrderedSet[A]) = 
+proc clear*[A](s: var OrderedSet[A]) =
   ## Clears the OrderedSet back to an empty state, without shrinking
   ## any of the existing storage. O(n) where n is the size of the hash bucket.
   s.counter = 0
@@ -911,13 +911,13 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
   ## Equality for ordered sets.
   if s.counter != t.counter: return false
   var h = s.first
-  var g = s.first
+  var g = t.first
   var compared = 0
   while h >= 0 and g >= 0:
     var nxh = s.data[h].next
     var nxg = t.data[g].next
-    if isFilled(s.data[h].hcode) and isFilled(s.data[g].hcode):
-      if s.data[h].key == s.data[g].key:
+    if isFilled(s.data[h].hcode) and isFilled(t.data[g].hcode):
+      if s.data[h].key == t.data[g].key:
         inc compared
       else:
         return false
@@ -1120,6 +1120,22 @@ when isMainModule and not defined(release):
       assert s.missingOrExcl(4) == true
       assert s.missingOrExcl(6) == false
 
+    block orderedSetEquality:
+      type pair = tuple[a, b: int]
+
+      var aa = initOrderedSet[pair]()
+      var bb = initOrderedSet[pair]()
+
+      var x = (a:1,b:2)
+      var y = (a:3,b:4)
+
+      aa.incl(x)
+      aa.incl(y)
+
+      bb.incl(x)
+      bb.incl(y)
+      assert aa == bb
+
     when not defined(testing):
       echo "Micro tests run successfully."
 
diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim
index e93ceb02f..b3e677b79 100644
--- a/lib/pure/collections/sharedlist.nim
+++ b/lib/pure/collections/sharedlist.nim
@@ -73,10 +73,10 @@ proc add*[A](x: var SharedList[A]; y: A) =
     node.d[node.dataLen] = y
     inc(node.dataLen)
 
-proc initSharedList*[A](): SharedList[A] =
-  initLock result.lock
-  result.head = nil
-  result.tail = nil
+proc init*[A](t: var SharedList[A]) =
+  initLock t.lock
+  t.head = nil
+  t.tail = nil
 
 proc clear*[A](t: var SharedList[A]) =
   withLock(t):
@@ -92,4 +92,11 @@ proc deinitSharedList*[A](t: var SharedList[A]) =
   clear(t)
   deinitLock t.lock
 
+proc initSharedList*[A](): SharedList[A] {.deprecated.} =
+  ## Deprecated. Use `init` instead.
+  ## This is not posix compliant, may introduce undefined behavior.
+  initLock result.lock
+  result.head = nil
+  result.tail = nil
+
 {.pop.}
diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim
index a9e194fb4..7e9de4b73 100644
--- a/lib/pure/collections/sharedstrings.nim
+++ b/lib/pure/collections/sharedstrings.nim
@@ -55,7 +55,7 @@ proc `[]=`*(s: var SharedString; i: Natural; value: char) =
   if i < s.len: s.buffer.data[i+s.first] = value
   else: raise newException(IndexError, "index out of bounds")
 
-proc `[]`*(s: SharedString; ab: Slice[int]): SharedString =
+proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
   #incRef(src.buffer)
   if ab.a < s.len:
     result.buffer = s.buffer
@@ -87,10 +87,10 @@ proc newSharedString*(s: string): SharedString =
   result.len = len
 
 when declared(atomicLoadN):
-  template load(x): expr = atomicLoadN(addr x, ATOMIC_SEQ_CST)
+  template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
 else:
   # XXX Fixme
-  template load(x): expr = x
+  template load(x): untyped = x
 
 proc add*(s: var SharedString; t: cstring; len: Natural) =
   if len == 0: return
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index fc50ea41c..4f311af87 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -183,6 +183,7 @@ proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
 
 proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) =
   ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## This can introduce duplicate keys into the table!
   withLock t:
     addImpl(enlarge)
 
@@ -191,19 +192,29 @@ proc del*[A, B](t: var SharedTable[A, B], key: A) =
   withLock t:
     delImpl()
 
-proc initSharedTable*[A, B](initialSize=64): SharedTable[A, B] =
+proc init*[A, B](t: var SharedTable[A, B], initialSize=64) =
   ## creates a new hash table that is empty.
   ##
   ## `initialSize` needs to be a power of two. If you need to accept runtime
   ## values for this you could use the ``nextPowerOfTwo`` proc from the
   ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
   assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  result.dataLen = initialSize
-  result.data = cast[KeyValuePairSeq[A, B]](allocShared0(
+  t.counter = 0
+  t.dataLen = initialSize
+  t.data = cast[KeyValuePairSeq[A, B]](allocShared0(
                                       sizeof(KeyValuePair[A, B]) * initialSize))
-  initLock result.lock
+  initLock t.lock
 
 proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) =
   deallocShared(t.data)
   deinitLock t.lock
+
+proc initSharedTable*[A, B](initialSize=64): SharedTable[A, B] {.deprecated.} =
+  ## Deprecated. Use `init` instead.
+  ## This is not posix compliant, may introduce undefined behavior.
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  result.dataLen = initialSize
+  result.data = cast[KeyValuePairSeq[A, B]](allocShared0(
+                                     sizeof(KeyValuePair[A, B]) * initialSize))
+  initLock result.lock
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index eec98fcaf..9a5bffcef 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -149,7 +149,7 @@ template delImpl() {.dirty.} =
   delImplIdx(t, i)
 
 template clearImpl() {.dirty.} =
-  for i in 0 .. <t.data.len:
+  for i in 0 ..< t.data.len:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
     t.data[i].key = default(type(t.data[i].key))
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 01a42efab..777beabc3 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -308,6 +308,7 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
 
 proc add*[A, B](t: var Table[A, B], key: A, val: B) =
   ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## This can introduce duplicate keys into the table!
   addImpl(enlarge)
 
 proc len*[A, B](t: TableRef[A, B]): int =
@@ -337,9 +338,9 @@ template dollarImpl(): untyped {.dirty.} =
     result = "{"
     for key, val in pairs(t):
       if result.len > 1: result.add(", ")
-      result.add($key)
+      result.addQuoted(key)
       result.add(": ")
-      result.add($val)
+      result.addQuoted(val)
     result.add("}")
 
 proc `$`*[A, B](t: Table[A, B]): string =
@@ -430,6 +431,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
 
 proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
   ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## This can introduce duplicate keys into the table!
   t[].add(key, val)
 
 proc del*[A, B](t: TableRef[A, B], key: A) =
@@ -604,6 +606,7 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
 
 proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
   ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## This can introduce duplicate keys into the table!
   addImpl(enlarge)
 
 proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
@@ -770,6 +773,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
 
 proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
   ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## This can introduce duplicate keys into the table!
   t[].add(key, val)
 
 proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
@@ -962,9 +966,10 @@ proc initCountTable*[A](initialSize=64): CountTable[A] =
   newSeq(result.data, initialSize)
 
 proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
-  ## creates a new count table with every key in `keys` having a count of 1.
+  ## creates a new count table with every key in `keys` having a count
+  ## of how many times it occurs in `keys`.
   result = initCountTable[A](rightSize(keys.len))
-  for key in items(keys): result[key] = 1
+  for key in items(keys): result.inc key
 
 proc `$`*[A](t: CountTable[A]): string =
   ## The `$` operator for count tables.
@@ -989,9 +994,10 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
   assert t.len > 0
-  var minIdx = 0
-  for h in 1..high(t.data):
-    if t.data[h].val > 0 and t.data[minIdx].val > t.data[h].val: minIdx = h
+  var minIdx = -1
+  for h in 0..high(t.data):
+    if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val):
+      minIdx = h
   result.key = t.data[minIdx].key
   result.val = t.data[minIdx].val
 
@@ -1325,3 +1331,7 @@ when isMainModule:
     assert((a == b) == true)
     assert((b == a) == true)
 
+  block: # CountTable.smallest
+    var t = initCountTable[int]()
+    for v in items([0, 0, 5, 5, 5]): t.inc(v)
+    doAssert t.smallest == (0, 2)
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 603fee080..f01488811 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -45,8 +45,25 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
   ## returns the numer of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
   when defined(windows):
-    var x = getEnv("NUMBER_OF_PROCESSORS")
-    if x.len > 0: result = parseInt(x.string)
+    type
+      SYSTEM_INFO {.final, pure.} = object
+        u1: int32
+        dwPageSize: int32
+        lpMinimumApplicationAddress: pointer
+        lpMaximumApplicationAddress: pointer
+        dwActiveProcessorMask: ptr int32
+        dwNumberOfProcessors: int32
+        dwProcessorType: int32
+        dwAllocationGranularity: int32
+        wProcessorLevel: int16
+        wProcessorRevision: int16
+
+    proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".}
+
+    var
+      si: SYSTEM_INFO
+    GetSystemInfo(si)
+    result = si.dwNumberOfProcessors
   elif defined(macosx) or defined(bsd):
     var
       mib: array[0..3, cint]
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 0f23b7e85..a5eaec86e 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -149,7 +149,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
 proc cleanFlowVars(w: ptr Worker) =
   let q = addr(w.q)
   acquire(q.lock)
-  for i in 0 .. <q.len:
+  for i in 0 ..< q.len:
     GC_unref(cast[RootRef](q.data[i]))
     #echo "GC_unref"
   q.len = 0
@@ -401,7 +401,7 @@ proc setup() =
     gCpus = p
   currentPoolSize = min(p, MaxThreadPoolSize)
   readyWorker = addr(workersData[0])
-  for i in 0.. <currentPoolSize: activateWorkerThread(i)
+  for i in 0..<currentPoolSize: activateWorkerThread(i)
 
 proc preferSpawn*(): bool =
   ## Use this proc to determine quickly if a 'spawn' or a direct call is
@@ -446,7 +446,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
   # implementation of 'spawn' that is used by the code generator.
   while true:
     if selectWorker(readyWorker, fn, data): return
-    for i in 0.. <currentPoolSize:
+    for i in 0..<currentPoolSize:
       if selectWorker(addr(workersData[i]), fn, data): return
 
     # determine what to do, but keep in mind this is expensive too:
@@ -543,7 +543,7 @@ proc sync*() =
   var toRelease = 0
   while true:
     var allReady = true
-    for i in 0 .. <currentPoolSize:
+    for i in 0 ..< currentPoolSize:
       if not allReady: break
       allReady = allReady and workersData[i].ready
     if allReady: break
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index 7d850798c..8f16717ac 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -13,6 +13,15 @@ import strtabs, times
 
 proc parseCookies*(s: string): StringTableRef =
   ## parses cookies into a string table.
+  ##
+  ## The proc is meant to parse the Cookie header set by a client, not the
+  ## "Set-Cookie" header set by servers.
+  ##
+  ## Example:
+  ##
+  ## .. code-block::Nim
+  ##     doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
+
   result = newStringTable(modeCaseInsensitive)
   var i = 0
   while true:
@@ -42,7 +51,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
   if secure: result.add("; Secure")
   if httpOnly: result.add("; HttpOnly")
 
-proc setCookie*(key, value: string, expires: TimeInfo,
+proc setCookie*(key, value: string, expires: DateTime,
                 domain = "", path = "", noName = false,
                 secure = false, httpOnly = false): string =
   ## Creates a command in the format of
@@ -54,9 +63,9 @@ proc setCookie*(key, value: string, expires: TimeInfo,
                    noname, secure, httpOnly)
 
 when isMainModule:
-  var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
+  var tim = fromUnix(getTime().toUnix + 76 * (60 * 60 * 24))
 
-  let cookie = setCookie("test", "value", tim.getGMTime())
+  let cookie = setCookie("test", "value", tim.utc)
   when not defined(testing):
     echo cookie
   let start = "Set-Cookie: test=value; Expires="
diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim
new file mode 100644
index 000000000..437140892
--- /dev/null
+++ b/lib/pure/cstrutils.nim
@@ -0,0 +1,79 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module supports helper routines for working with ``cstring``
+## without having to convert ``cstring`` to ``string`` in order to
+## save allocations.
+
+include "system/inclrtl"
+
+proc toLowerAscii(c: char): char {.inline.} =
+  if c in {'A'..'Z'}:
+    result = chr(ord(c) + (ord('a') - ord('A')))
+  else:
+    result = c
+
+proc startsWith*(s, prefix: cstring): bool {.noSideEffect,
+  rtl, extern: "csuStartsWith".} =
+  ## Returns true iff ``s`` starts with ``prefix``.
+  ##
+  ## If ``prefix == ""`` true is returned.
+  var i = 0
+  while true:
+    if prefix[i] == '\0': return true
+    if s[i] != prefix[i]: return false
+    inc(i)
+
+proc endsWith*(s, suffix: cstring): bool {.noSideEffect,
+  rtl, extern: "csuEndsWith".} =
+  ## Returns true iff ``s`` ends with ``suffix``.
+  ##
+  ## If ``suffix == ""`` true is returned.
+  let slen = s.len
+  var i = 0
+  var j = slen - len(suffix)
+  while i+j <% slen:
+    if s[i+j] != suffix[i]: return false
+    inc(i)
+  if suffix[i] == '\0': return true
+
+proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect,
+  rtl, extern: "csuCmpIgnoreStyle".} =
+  ## Compares two strings normalized (i.e. case and
+  ## underscores do not matter). Returns:
+  ##
+  ## | 0 iff a == b
+  ## | < 0 iff a < b
+  ## | > 0 iff a > b
+  var i = 0
+  var j = 0
+  while true:
+    while a[i] == '_': inc(i)
+    while b[j] == '_': inc(j) # BUGFIX: typo
+    var aa = toLowerAscii(a[i])
+    var bb = toLowerAscii(b[j])
+    result = ord(aa) - ord(bb)
+    if result != 0 or aa == '\0': break
+    inc(i)
+    inc(j)
+
+proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect,
+  rtl, extern: "csuCmpIgnoreCase".} =
+  ## Compares two strings in a case insensitive manner. Returns:
+  ##
+  ## | 0 iff a == b
+  ## | < 0 iff a < b
+  ## | > 0 iff a > b
+  var i = 0
+  while true:
+    var aa = toLowerAscii(a[i])
+    var bb = toLowerAscii(b[i])
+    result = ord(aa) - ord(bb)
+    if result != 0 or aa == '\0': break
+    inc(i)
diff --git a/lib/pure/future.nim b/lib/pure/future.nim
index 2a6d29933..1a3ab688d 100644
--- a/lib/pure/future.nim
+++ b/lib/pure/future.nim
@@ -22,7 +22,7 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
 
   case p.kind
   of nnkPar:
-    for i in 0 .. <p.len:
+    for i in 0 ..< p.len:
       let ident = p[i]
       var identDefs = newNimNode(nnkIdentDefs)
       case ident.kind
@@ -77,7 +77,7 @@ macro `=>`*(p, b: untyped): untyped =
         if c[0].kind == nnkIdent and c[0].ident == !"->":
           var procTy = createProcType(c[1], c[2])
           params[0] = procTy[0][0]
-          for i in 1 .. <procTy[0].len:
+          for i in 1 ..< procTy[0].len:
             params.add(procTy[0][i])
         else:
           error("Expected proc type (->) got (" & $c[0].ident & ").")
@@ -96,7 +96,7 @@ macro `=>`*(p, b: untyped): untyped =
     if p[0].kind == nnkIdent and p[0].ident == !"->":
       var procTy = createProcType(p[1], p[2])
       params[0] = procTy[0][0]
-      for i in 1 .. <procTy[0].len:
+      for i in 1 ..< procTy[0].len:
         params.add(procTy[0][i])
     else:
       error("Expected proc type (->) got (" & $p[0].ident & ").")
@@ -197,4 +197,4 @@ macro dump*(x: typed): untyped =
   let s = x.toStrLit
   let r = quote do:
     debugEcho `s`, " = ", `x`
-  return r
\ No newline at end of file
+  return r
diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim
deleted file mode 100644
index 928ff8fe0..000000000
--- a/lib/pure/gentabs.nim
+++ /dev/null
@@ -1,211 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## The ``gentabs`` module implements an efficient hash table that is a
-## key-value mapping. The keys are required to be strings, but the values
-## may be any Nim or user defined type. This module supports matching
-## of keys in case-sensitive, case-insensitive and style-insensitive modes.
-##
-## **Warning:** This module is deprecated, new code shouldn't use it!
-
-{.deprecated.}
-
-import
-  os, hashes, strutils
-
-type
-  GenTableMode* = enum     ## describes the table's key matching mode
-    modeCaseSensitive,     ## case sensitive matching of keys
-    modeCaseInsensitive,   ## case insensitive matching of keys
-    modeStyleInsensitive   ## style sensitive matching of keys
-
-  GenKeyValuePair[T] = tuple[key: string, val: T]
-  GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
-  GenTable*[T] = object of RootObj
-    counter: int
-    data: GenKeyValuePairSeq[T]
-    mode: GenTableMode
-
-  PGenTable*[T] = ref GenTable[T]     ## use this type to declare hash tables
-
-{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
-              TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
-
-const
-  growthFactor = 2
-  startSize = 64
-
-
-proc len*[T](tbl: PGenTable[T]): int {.inline.} =
-  ## returns the number of keys in `tbl`.
-  result = tbl.counter
-
-iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
-  ## iterates over any (key, value) pair in the table `tbl`.
-  for h in 0..high(tbl.data):
-    if not isNil(tbl.data[h].key):
-      yield (tbl.data[h].key, tbl.data[h].val)
-
-proc myhash[T](tbl: PGenTable[T], key: string): Hash =
-  case tbl.mode
-  of modeCaseSensitive: result = hashes.hash(key)
-  of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
-  of modeStyleInsensitive: result = hashes.hashIgnoreStyle(key)
-
-proc myCmp[T](tbl: PGenTable[T], a, b: string): bool =
-  case tbl.mode
-  of modeCaseSensitive: result = cmp(a, b) == 0
-  of modeCaseInsensitive: result = cmpIgnoreCase(a, b) == 0
-  of modeStyleInsensitive: result = cmpIgnoreStyle(a, b) == 0
-
-proc mustRehash(length, counter: int): bool =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
-  ## creates a new generic hash table that is empty.
-  new(result)
-  result.mode = mode
-  result.counter = 0
-  newSeq(result.data, startSize)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = ((5 * h) + 1) and maxHash
-
-proc rawGet[T](tbl: PGenTable[T], key: string): int =
-  var h: Hash
-  h = myhash(tbl, key) and high(tbl.data) # start with real hash value
-  while not isNil(tbl.data[h].key):
-    if myCmp(tbl, tbl.data[h].key, key):
-      return h
-    h = nextTry(h, high(tbl.data))
-  result = - 1
-
-proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
-                  key: string, val: T) =
-  var h: Hash
-  h = myhash(tbl, key) and high(data)
-  while not isNil(data[h].key):
-    h = nextTry(h, high(data))
-  data[h].key = key
-  data[h].val = val
-
-proc enlarge[T](tbl: PGenTable[T]) =
-  var n: GenKeyValuePairSeq[T]
-  newSeq(n, len(tbl.data) * growthFactor)
-  for i in countup(0, high(tbl.data)):
-    if not isNil(tbl.data[i].key):
-      rawInsert[T](tbl, n, tbl.data[i].key, tbl.data[i].val)
-  swap(tbl.data, n)
-
-proc hasKey*[T](tbl: PGenTable[T], key: string): bool =
-  ## returns true iff `key` is in the table `tbl`.
-  result = rawGet(tbl, key) >= 0
-
-proc `[]`*[T](tbl: PGenTable[T], key: string): T =
-  ## retrieves the value at ``tbl[key]``. If `key` is not in `tbl`,
-  ## default(T) is returned and no exception is raised. One can check
-  ## with ``hasKey`` whether the key exists.
-  var index = rawGet(tbl, key)
-  if index >= 0: result = tbl.data[index].val
-
-proc `[]=`*[T](tbl: PGenTable[T], key: string, val: T) =
-  ## puts a (key, value)-pair into `tbl`.
-  var index = rawGet(tbl, key)
-  if index >= 0:
-    tbl.data[index].val = val
-  else:
-    if mustRehash(len(tbl.data), tbl.counter): enlarge(tbl)
-    rawInsert(tbl, tbl.data, key, val)
-    inc(tbl.counter)
-
-
-when isMainModule:
-  #
-  # Verify tables of integer values (string keys)
-  #
-  var x = newGenTable[int](modeCaseInsensitive)
-  x["one"]   = 1
-  x["two"]   = 2
-  x["three"] = 3
-  x["four"]  = 4
-  x["five"]  = 5
-  assert(len(x) == 5)             # length procedure works
-  assert(x["one"] == 1)           # case-sensitive lookup works
-  assert(x["ONE"] == 1)           # case-insensitive should work for this table
-  assert(x["one"]+x["two"] == 3)  # make sure we're getting back ints
-  assert(x.hasKey("one"))         # hasKey should return 'true' for a key
-                                  # of "one"...
-  assert(not x.hasKey("NOPE"))    # ...but key "NOPE" is not in the table.
-  for k,v in pairs(x):            # make sure the 'pairs' iterator works
-    assert(x[k]==v)
-
-  #
-  # Verify a table of user-defined types
-  #
-  type
-    MyType = tuple[first, second: string] # a pair of strings
-  {.deprecated: [TMyType: MyType].}
-
-  var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
-                                                    # value is MyType tuple
-
-  #var junk: MyType = ("OK", "Here")
-
-  #echo junk.first, " ", junk.second
-
-  y["Hello"] = ("Hello", "World")
-  y["Goodbye"] = ("Goodbye", "Everyone")
-  #y["Hello"] = MyType( ("Hello", "World") )
-  #y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
-
-  assert( not isNil(y["Hello"].first) )
-  assert( y["Hello"].first == "Hello" )
-  assert( y["Hello"].second == "World" )
-
-  #
-  # Verify table of tables
-  #
-  var z: PGenTable[ PGenTable[int] ] # hash table where each value is
-                                     # a hash table of ints
-
-  z = newGenTable[PGenTable[int]](modeCaseInsensitive)
-  z["first"] = newGenTable[int](modeCaseInsensitive)
-  z["first"]["one"] = 1
-  z["first"]["two"] = 2
-  z["first"]["three"] = 3
-
-  z["second"] = newGenTable[int](modeCaseInsensitive)
-  z["second"]["red"] = 10
-  z["second"]["blue"] = 20
-
-  assert(len(z) == 2)               # length of outer table
-  assert(len(z["first"]) == 3)      # length of "first" table
-  assert(len(z["second"]) == 2)     # length of "second" table
-  assert( z["first"]["one"] == 1)   # retrieve from first inner table
-  assert( z["second"]["red"] == 10) # retrieve from second inner table
-
-  when false:
-    # disabled: depends on hash order:
-    var output = ""
-    for k, v in pairs(z):
-      output.add( "$# ($#) ->\L" % [k,$len(v)] )
-      for k2,v2 in pairs(v):
-        output.add( "  $# <-> $#\L" % [k2,$v2] )
-
-    let expected = unindent """
-      first (3) ->
-        two <-> 2
-        three <-> 3
-        one <-> 1
-      second (2) ->
-        red <-> 10
-        blue <-> 20
-    """
-    assert output == expected
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index ad199a215..c0934a45b 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -59,8 +59,8 @@ proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "",
 
   # copy the attributes; when iterating over them these lists
   # will be modified, so that each attribute is only given one value
-  var req = split(reqAttr)
-  var opt = split(optAttr)
+  var req = splitWhitespace(reqAttr)
+  var opt = splitWhitespace(optAttr)
   result = newNimNode(nnkBracket, e)
   result.add(newStrLitNode("<"))
   result.add(newStrLitNode(tag))
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index de1d332a3..54a8498fa 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -469,7 +469,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
   ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
   var r = if proxy == nil: parseUri(url) else: proxy.url
   var hostUrl = if proxy == nil: r else: parseUri(url)
-  var headers = httpMethod.toUpper()
+  var headers = httpMethod.toUpperAscii()
   # TODO: Use generateHeaders further down once it supports proxies.
 
   var s = newSocket()
@@ -713,10 +713,10 @@ proc downloadFile*(url: string, outputFilename: string,
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
-  result = httpMethod.toUpper()
+  result = httpMethod.toUpperAscii()
   result.add ' '
 
-  if proxy.isNil:
+  if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"):
     # /path?query
     if requestUrl.path[0] != '/': result.add '/'
     result.add(requestUrl.path)
@@ -1048,7 +1048,11 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
       client.currentURL.scheme != url.scheme or
       client.currentURL.port != url.port or
       (not client.connected):
-    let isSsl = url.scheme.toLowerAscii() == "https"
+    # Connect to proxy if specified
+    let connectionUrl =
+      if client.proxy.isNil: url else: client.proxy.url
+
+    let isSsl = connectionUrl.scheme.toLowerAscii() == "https"
 
     if isSsl and not defined(ssl):
       raise newException(HttpRequestError,
@@ -1056,31 +1060,55 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
 
     if client.connected:
       client.close()
+      client.connected = false
 
     # TODO: I should be able to write 'net.Port' here...
     let port =
-      if url.port == "":
+      if connectionUrl.port == "":
         if isSsl:
           nativesockets.Port(443)
         else:
           nativesockets.Port(80)
-      else: nativesockets.Port(url.port.parseInt)
+      else: nativesockets.Port(connectionUrl.port.parseInt)
 
     when client is HttpClient:
-      client.socket = await net.dial(url.hostname, port)
+      client.socket = await net.dial(connectionUrl.hostname, port)
     elif client is AsyncHttpClient:
-      client.socket = await asyncnet.dial(url.hostname, port)
+      client.socket = await asyncnet.dial(connectionUrl.hostname, port)
     else: {.fatal: "Unsupported client type".}
 
     when defined(ssl):
       if isSsl:
         try:
           client.sslContext.wrapConnectedSocket(
-            client.socket, handshakeAsClient, url.hostname)
+            client.socket, handshakeAsClient, connectionUrl.hostname)
         except:
           client.socket.close()
           raise getCurrentException()
 
+    # If need to CONNECT through proxy
+    if url.scheme == "https" and not client.proxy.isNil:
+      when defined(ssl):
+        # Pass only host:port for CONNECT
+        var connectUrl = initUri()
+        connectUrl.hostname = url.hostname
+        connectUrl.port = if url.port != "": url.port else: "443"
+
+        let proxyHeaderString = generateHeaders(connectUrl, $HttpConnect, newHttpHeaders(), "", client.proxy)
+        await client.socket.send(proxyHeaderString)
+        let proxyResp = await parseResponse(client, false)
+
+        if not proxyResp.status.startsWith("200"):
+          raise newException(HttpRequestError,
+                            "The proxy server rejected a CONNECT request, " &
+                            "so a secure connection could not be established.")
+        client.sslContext.wrapConnectedSocket(
+          client.socket, handshakeAsClient, url.hostname)
+      else:
+        raise newException(HttpRequestError,
+        "SSL support is not available. Cannot connect over SSL.")
+
+    # May be connected through proxy but remember actual URL being accessed
     client.currentURL = url
     client.connected = true
 
@@ -1100,32 +1128,9 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
                 headers: HttpHeaders = nil): Future[Response | AsyncResponse]
                 {.multisync.} =
   # Helper that actually makes the request. Does not handle redirects.
-  let connectionUrl =
-    if client.proxy.isNil: parseUri(url) else: client.proxy.url
   let requestUrl = parseUri(url)
 
-  let savedProxy = client.proxy # client's proxy may be overwritten.
-
-  if requestUrl.scheme == "https" and not client.proxy.isNil:
-    when defined(ssl):
-      client.proxy.url = connectionUrl
-      var connectUrl = requestUrl
-      connectUrl.scheme = "http"
-      connectUrl.port = "443"
-      let proxyResp = await requestAux(client, $connectUrl, $HttpConnect)
-
-      if not proxyResp.status.startsWith("200"):
-        raise newException(HttpRequestError,
-                           "The proxy server rejected a CONNECT request, " &
-                           "so a secure connection could not be established.")
-      client.sslContext.wrapConnectedSocket(
-        client.socket, handshakeAsClient, requestUrl.hostname)
-      client.proxy = nil
-    else:
-      raise newException(HttpRequestError,
-          "SSL support not available. Cannot connect to https site over proxy.")
-  else:
-    await newConnection(client, connectionUrl)
+  await newConnection(client, requestUrl)
 
   let effectiveHeaders = client.headers.override(headers)
 
@@ -1143,9 +1148,6 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
                 client.getBody
   result = await parseResponse(client, getBody)
 
-  # Restore the clients proxy in case it was overwritten.
-  client.proxy = savedProxy
-
 proc request*(client: HttpClient | AsyncHttpClient, url: string,
               httpMethod: string, body = "",
               headers: HttpHeaders = nil): Future[Response | AsyncResponse]
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index a5ab40ca4..f150fa1c1 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -113,6 +113,9 @@ proc newHttpHeaders*(keyValuePairs:
   new result
   result.table = newTable[string, seq[string]](pairs)
 
+proc `$`*(headers: HttpHeaders): string =
+  return $headers.table
+
 proc clear*(headers: HttpHeaders) =
   headers.table.clear()
 
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index 8d2fc235a..ae62a5c4e 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -94,7 +94,7 @@ proc findEnvVar(key: string): int =
     if startsWith(environment[i], temp): return i
   return -1
 
-proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
+proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
   ## Returns the value of the `environment variable`:idx: named `key`.
   ##
   ## If the variable does not exist, "" is returned. To distinguish
@@ -108,7 +108,7 @@ proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
       return TaintedString(substr(environment[i], find(environment[i], '=')+1))
     else:
       var env = c_getenv(key)
-      if env == nil: return TaintedString("")
+      if env == nil: return TaintedString(default)
       result = TaintedString($env)
 
 proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index dbb709f1b..0889d7383 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -56,9 +56,6 @@ proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
     raise newException(OSError, msg)
 {.pop.}
 
-when not defined(nimfix):
-  {.deprecated: [osError: raiseOSError].}
-
 proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
 proc `$`*(err: OSErrorCode): string {.borrow.}
 
diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim
deleted file mode 100644
index ef8072221..000000000
--- a/lib/pure/ioselectors.nim
+++ /dev/null
@@ -1,293 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2016 Eugene Kabanov
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module allows high-level and efficient I/O multiplexing.
-##
-## Supported OS primitives: ``epoll``, ``kqueue``, ``poll`` and
-## Windows ``select``.
-##
-## To use threadsafe version of this module, it needs to be compiled
-## with both ``-d:threadsafe`` and ``--threads:on`` options.
-##
-## Supported features: files, sockets, pipes, timers, processes, signals
-## and user events.
-##
-## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except
-## for Android).
-##
-## Partially supported OS: Windows (only sockets and user events),
-## Solaris (files, sockets, handles and user events).
-## Android (files, sockets, handles and user events).
-##
-## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
-
-import os
-
-const hasThreadSupport = compileOption("threads") and defined(threadsafe)
-
-const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
-                                defined(netbsd) or defined(openbsd) or
-                                defined(dragonfly) or
-                                (defined(linux) and not defined(android))
-  ## This constant is used to determine whether the destination platform is
-  ## fully supported by ``ioselectors`` module.
-
-const bsdPlatform = defined(macosx) or defined(freebsd) or
-                    defined(netbsd) or defined(openbsd) or
-                    defined(dragonfly)
-
-when defined(nimdoc):
-  type
-    Selector*[T] = ref object
-      ## An object which holds descriptors to be checked for read/write status
-
-    Event* {.pure.} = enum
-      ## An enum which hold event types
-      Read,        ## Descriptor is available for read
-      Write,       ## Descriptor is available for write
-      Timer,       ## Timer descriptor is completed
-      Signal,      ## Signal is raised
-      Process,     ## Process is finished
-      Vnode,       ## BSD specific file change happens
-      User,        ## User event is raised
-      Error,       ## Error happens while waiting, for descriptor
-      VnodeWrite,  ## NOTE_WRITE (BSD specific, write to file occurred)
-      VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred)
-      VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended)
-      VnodeAttrib, ## NOTE_ATTRIB (BSD specific, file attributes changed)
-      VnodeLink,   ## NOTE_LINK (BSD specific, file link count changed)
-      VnodeRename, ## NOTE_RENAME (BSD specific, file renamed)
-      VnodeRevoke  ## NOTE_REVOKE (BSD specific, file revoke occurred)
-
-    ReadyKey* = object
-      ## An object which holds result for descriptor
-      fd* : int ## file/socket descriptor
-      events*: set[Event] ## set of events
-
-    SelectEvent* = object
-      ## An object which holds user defined event
-
-  proc newSelector*[T](): Selector[T] =
-    ## Creates a new selector
-
-  proc close*[T](s: Selector[T]) =
-    ## Closes selector
-
-  proc registerHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event],
-                          data: T) =
-    ## Registers file/socket descriptor ``fd`` to selector ``s``
-    ## with events set in ``events``. The ``data`` is application-defined
-    ## data, which to be passed when event happens.
-
-  proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) =
-    ## Update file/socket descriptor ``fd``, registered in selector
-    ## ``s`` with new events set ``event``.
-
-  proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
-                         data: T): int {.discardable.} =
-    ## Registers timer notification with ``timeout`` in milliseconds
-    ## to selector ``s``.
-    ## If ``oneshot`` is ``true`` timer will be notified only once.
-    ## Set ``oneshot`` to ``false`` if your want periodic notifications.
-    ## The ``data`` is application-defined data, which to be passed, when
-    ## time limit expired.
-
-  proc registerSignal*[T](s: Selector[T], signal: int,
-                          data: T): int {.discardable.} =
-    ## Registers Unix signal notification with ``signal`` to selector
-    ## ``s``. The ``data`` is application-defined data, which to be
-    ## passed, when signal raises.
-    ##
-    ## This function is not supported for ``Windows``.
-
-  proc registerProcess*[T](s: Selector[T], pid: int,
-                           data: T): int {.discardable.} =
-    ## Registers process id (pid) notification when process has
-    ## exited to selector ``s``.
-    ## The ``data`` is application-defined data, which to be passed, when
-    ## process with ``pid`` has exited.
-
-  proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
-    ## Registers selector event ``ev`` to selector ``s``.
-    ## ``data`` application-defined data, which to be passed, when
-    ## ``ev`` happens.
-
-  proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event],
-                         data: T) =
-    ## Registers selector BSD/MacOSX specific vnode events for file
-    ## descriptor ``fd`` and events ``events``.
-    ## ``data`` application-defined data, which to be passed, when
-    ## vnode event happens.
-    ##
-    ## This function is supported only by BSD and MacOSX.
-
-  proc newSelectEvent*(): SelectEvent =
-    ## Creates new event ``SelectEvent``.
-
-  proc setEvent*(ev: SelectEvent) =
-    ## Trigger event ``ev``.
-
-  proc close*(ev: SelectEvent) =
-    ## Closes selector event ``ev``.
-
-  proc unregister*[T](s: Selector[T], ev: SelectEvent) =
-    ## Unregisters event ``ev`` from selector ``s``.
-
-  proc unregister*[T](s: Selector[T], fd: int|SocketHandle|cint) =
-    ## Unregisters file/socket descriptor ``fd`` from selector ``s``.
-
-  proc selectInto*[T](s: Selector[T], timeout: int,
-                      results: var openarray[ReadyKey]): int =
-    ## Process call waiting for events registered in selector ``s``.
-    ## The ``timeout`` argument specifies the minimum number of milliseconds
-    ## the function will be blocked, if no events are not ready. Specifying a
-    ## timeout of ``-1`` causes function to block indefinitely.
-    ## All available events will be stored in ``results`` array.
-    ##
-    ## Function returns number of triggered events.
-
-  proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
-    ## Process call waiting for events registered in selector ``s``.
-    ## The ``timeout`` argument specifies the minimum number of milliseconds
-    ## the function will be blocked, if no events are not ready. Specifying a
-    ## timeout of -1 causes function to block indefinitely.
-    ##
-    ## Function returns sequence of triggered events.
-
-  proc getData*[T](s: Selector[T], fd: SocketHandle|int): T =
-    ## Retrieves application-defined ``data`` associated with descriptor ``fd``.
-    ## If specified descriptor ``fd`` is not registered, empty/default value
-    ## will be returned.
-
-  proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool =
-    ## Associate application-defined ``data`` with descriptor ``fd``.
-    ##
-    ## Returns ``true``, if data was succesfully updated, ``false`` otherwise.
-
-  template isEmpty*[T](s: Selector[T]): bool =
-    ## Returns ``true``, if there no registered events or descriptors
-    ## in selector.
-
-  template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
-                        body: untyped) =
-    ## Retrieves the application-data assigned with descriptor ``fd``
-    ## to ``value``. This ``value`` can be modified in the scope of
-    ## the ``withData`` call.
-    ##
-    ## .. code-block:: nim
-    ##
-    ##   s.withData(fd, value) do:
-    ##     # block is executed only if ``fd`` registered in selector ``s``
-    ##     value.uid = 1000
-    ##
-
-  template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
-                        body1, body2: untyped) =
-    ## Retrieves the application-data assigned with descriptor ``fd``
-    ## to ``value``. This ``value`` can be modified in the scope of
-    ## the ``withData`` call.
-    ##
-    ## .. code-block:: nim
-    ##
-    ##   s.withData(fd, value) do:
-    ##     # block is executed only if ``fd`` registered in selector ``s``.
-    ##     value.uid = 1000
-    ##   do:
-    ##     # block is executed if ``fd`` not registered in selector ``s``.
-    ##     raise
-    ##
-
-else:
-  when hasThreadSupport:
-    import locks
-
-    type
-      SharedArray[T] = UncheckedArray[T]
-
-    proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
-      result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
-
-    proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
-      deallocShared(cast[pointer](sa))
-  type
-    Event* {.pure.} = enum
-      Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot,
-      Finished, VnodeWrite, VnodeDelete, VnodeExtend, VnodeAttrib, VnodeLink,
-      VnodeRename, VnodeRevoke
-
-  type
-    IOSelectorsException* = object of Exception
-
-    ReadyKey* = object
-      fd* : int
-      events*: set[Event]
-
-    SelectorKey[T] = object
-      ident: int
-      events: set[Event]
-      param: int
-      data: T
-
-  proc raiseIOSelectorsError[T](message: T) =
-    var msg = ""
-    when T is string:
-      msg.add(message)
-    elif T is OSErrorCode:
-      msg.add(osErrorMsg(message) & " (code: " & $int(message) & ")")
-    else:
-      msg.add("Internal Error\n")
-    var err = newException(IOSelectorsException, msg)
-    raise err
-
-  when not defined(windows):
-    import posix
-
-    proc setNonBlocking(fd: cint) {.inline.} =
-      var x = fcntl(fd, F_GETFL, 0)
-      if x == -1:
-        raiseIOSelectorsError(osLastError())
-      else:
-        var mode = x or O_NONBLOCK
-        if fcntl(fd, F_SETFL, mode) == -1:
-          raiseIOSelectorsError(osLastError())
-
-    template setKey(s, pident, pevents, pparam, pdata: untyped) =
-      var skey = addr(s.fds[pident])
-      skey.ident = pident
-      skey.events = pevents
-      skey.param = pparam
-      skey.data = data
-
-  when ioselSupportedPlatform:
-    template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
-      when hasThreadSupport:
-        if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
-          raiseIOSelectorsError(osLastError())
-      else:
-        if posix.sigprocmask(SIG_BLOCK, newmask, oldmask) == -1:
-          raiseIOSelectorsError(osLastError())
-
-    template unblockSignals(newmask: var Sigset, oldmask: var Sigset) =
-      when hasThreadSupport:
-        if posix.pthread_sigmask(SIG_UNBLOCK, newmask, oldmask) == -1:
-          raiseIOSelectorsError(osLastError())
-      else:
-        if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
-          raiseIOSelectorsError(osLastError())
-
-  when defined(linux):
-    include ioselects/ioselectors_epoll
-  elif bsdPlatform:
-    include ioselects/ioselectors_kqueue
-  elif defined(windows):
-    include ioselects/ioselectors_select
-  elif defined(solaris):
-    include ioselects/ioselectors_poll # need to replace it with event ports
-  else:
-    include ioselects/ioselectors_poll
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 3a5cbc87a..8827f239f 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -9,7 +9,7 @@
 
 # This module implements Linux epoll().
 
-import posix, times
+import posix, times, epoll
 
 # Maximum number of events that can be returned
 const MAX_EPOLL_EVENTS = 64
@@ -36,35 +36,6 @@ when not defined(android):
       ssi_addr*: uint64
       pad* {.importc: "__pad".}: array[0..47, uint8]
 
-type
-  eventFdData {.importc: "eventfd_t",
-                header: "<sys/eventfd.h>", pure, final.} = uint64
-  epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
-               pure, final.} = object
-    u64 {.importc: "u64".}: uint64
-  epoll_event {.importc: "struct epoll_event",
-                header: "<sys/epoll.h>", pure, final.} = object
-    events: uint32 # Epoll events
-    data: epoll_data # User data variable
-
-const
-  EPOLL_CTL_ADD = 1          # Add a file descriptor to the interface.
-  EPOLL_CTL_DEL = 2          # Remove a file descriptor from the interface.
-  EPOLL_CTL_MOD = 3          # Change file descriptor epoll_event structure.
-  EPOLLIN = 0x00000001
-  EPOLLOUT = 0x00000004
-  EPOLLERR = 0x00000008
-  EPOLLHUP = 0x00000010
-  EPOLLRDHUP = 0x00002000
-  EPOLLONESHOT = 1 shl 30
-
-proc epoll_create(size: cint): cint
-     {.importc: "epoll_create", header: "<sys/epoll.h>".}
-proc epoll_ctl(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint
-     {.importc: "epoll_ctl", header: "<sys/epoll.h>".}
-proc epoll_wait(epfd: cint; events: ptr epoll_event; maxevents: cint;
-                 timeout: cint): cint
-     {.importc: "epoll_wait", header: "<sys/epoll.h>".}
 proc timerfd_create(clock_id: ClockId, flags: cint): cint
      {.cdecl, importc: "timerfd_create", header: "<sys/timerfd.h>".}
 proc timerfd_settime(ufd: cint, flags: cint,
@@ -80,26 +51,26 @@ when not defined(android):
 var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
                     header: "<sys/resource.h>".}: cint
 type
-  rlimit {.importc: "struct rlimit",
+  RLimit {.importc: "struct rlimit",
            header: "<sys/resource.h>", pure, final.} = object
     rlim_cur: int
     rlim_max: int
-proc getrlimit(resource: cint, rlp: var rlimit): cint
+proc getrlimit(resource: cint, rlp: var RLimit): cint
      {.importc: "getrlimit",header: "<sys/resource.h>".}
 
 when hasThreadSupport:
   type
     SelectorImpl[T] = object
-      epollFD : cint
-      maxFD : int
+      epollFD: cint
+      maxFD: int
       fds: ptr SharedArray[SelectorKey[T]]
       count: int
     Selector*[T] = ptr SelectorImpl[T]
 else:
   type
     SelectorImpl[T] = object
-      epollFD : cint
-      maxFD : int
+      epollFD: cint
+      maxFD: int
       fds: seq[SelectorKey[T]]
       count: int
     Selector*[T] = ref SelectorImpl[T]
@@ -109,7 +80,8 @@ type
   SelectEvent* = ptr SelectEventImpl
 
 proc newSelector*[T](): Selector[T] =
-  var a = rlimit()
+  # Retrieve the maximum fd count (for current OS) via getrlimit()
+  var a = RLimit()
   if getrlimit(RLIMIT_NOFILE, a) != 0:
     raiseOsError(osLastError())
   var maxFD = int(a.rlim_max)
@@ -152,8 +124,8 @@ proc newSelectEvent*(): SelectEvent =
   result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
   result.efd = fdci
 
-proc setEvent*(ev: SelectEvent) =
-  var data : uint64 = 1
+proc trigger*(ev: SelectEvent) =
+  var data: uint64 = 1
   if posix.write(ev.efd, addr data, sizeof(uint64)) == -1:
     raiseIOSelectorsError(osLastError())
 
@@ -164,6 +136,8 @@ proc close*(ev: SelectEvent) =
     raiseIOSelectorsError(osLastError())
 
 template checkFd(s, f) =
+  # TODO: I don't see how this can ever happen. You won't be able to create an
+  # FD if there is too many. -- DP
   if f >= s.maxFD:
     raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
 
@@ -171,10 +145,10 @@ proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi)
   s.setKey(fdi, events, 0, data)
   if events != {}:
-    var epv = epoll_event(events: EPOLLRDHUP)
+    var epv = EpollEvent(events: EPOLLRDHUP)
     epv.data.u64 = fdi.uint
     if Event.Read in events: epv.events = epv.events or EPOLLIN
     if Event.Write in events: epv.events = epv.events or EPOLLOUT
@@ -189,10 +163,10 @@ proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) =
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
   doAssert(pkey.ident != 0,
-           "Descriptor [" & $fdi & "] is not registered in the queue!")
+           "Descriptor $# is not registered in the selector!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
   if pkey.events != events:
-    var epv = epoll_event(events: EPOLLRDHUP)
+    var epv = EpollEvent(events: EPOLLRDHUP)
     epv.data.u64 = fdi.uint
 
     if Event.Read in events: epv.events = epv.events or EPOLLIN
@@ -217,24 +191,25 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
   doAssert(pkey.ident != 0,
-           "Descriptor [" & $fdi & "] is not registered in the queue!")
+           "Descriptor $# is not registered in the selector!" % $fdi)
   if pkey.events != {}:
     when not defined(android):
       if pkey.events * {Event.Read, Event.Write} != {}:
-        var epv = epoll_event()
+        var epv = EpollEvent()
+        # TODO: Refactor all these EPOLL_CTL_DEL + dec(s.count) into a proc.
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
           raiseIOSelectorsError(osLastError())
         dec(s.count)
       elif Event.Timer in pkey.events:
         if Event.Finished notin pkey.events:
-          var epv = epoll_event()
+          var epv = EpollEvent()
           if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
             raiseIOSelectorsError(osLastError())
           dec(s.count)
         if posix.close(cint(fdi)) != 0:
           raiseIOSelectorsError(osLastError())
       elif Event.Signal in pkey.events:
-        var epv = epoll_event()
+        var epv = EpollEvent()
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
           raiseIOSelectorsError(osLastError())
         var nmask, omask: Sigset
@@ -247,7 +222,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
           raiseIOSelectorsError(osLastError())
       elif Event.Process in pkey.events:
         if Event.Finished notin pkey.events:
-          var epv = epoll_event()
+          var epv = EpollEvent()
           if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
             raiseIOSelectorsError(osLastError())
           var nmask, omask: Sigset
@@ -260,13 +235,13 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
           raiseIOSelectorsError(osLastError())
     else:
       if pkey.events * {Event.Read, Event.Write} != {}:
-        var epv = epoll_event()
+        var epv = EpollEvent()
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
           raiseIOSelectorsError(osLastError())
         dec(s.count)
       elif Event.Timer in pkey.events:
         if Event.Finished notin pkey.events:
-          var epv = epoll_event()
+          var epv = EpollEvent()
           if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
             raiseIOSelectorsError(osLastError())
           dec(s.count)
@@ -280,7 +255,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   var pkey = addr(s.fds[fdi])
   doAssert(pkey.ident != 0, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
-  var epv = epoll_event()
+  var epv = EpollEvent()
   if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
     raiseIOSelectorsError(osLastError())
   dec(s.count)
@@ -300,17 +275,18 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
   doAssert(s.fds[fdi].ident == 0)
 
   var events = {Event.Timer}
-  var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+  var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = fdi.uint
+
   if oneshot:
-    new_ts.it_interval.tv_sec = 0.Time
+    new_ts.it_interval.tv_sec = posix.Time(0)
     new_ts.it_interval.tv_nsec = 0
-    new_ts.it_value.tv_sec = (timeout div 1_000).Time
+    new_ts.it_value.tv_sec = posix.Time(timeout div 1_000)
     new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
     incl(events, Event.Oneshot)
     epv.events = epv.events or EPOLLONESHOT
   else:
-    new_ts.it_interval.tv_sec = (timeout div 1000).Time
+    new_ts.it_interval.tv_sec = posix.Time(timeout div 1000)
     new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
     new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
     new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec
@@ -343,7 +319,7 @@ when not defined(android):
     s.checkFd(fdi)
     doAssert(s.fds[fdi].ident == 0)
 
-    var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+    var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
     if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) != 0:
       raiseIOSelectorsError(osLastError())
@@ -370,7 +346,7 @@ when not defined(android):
     s.checkFd(fdi)
     doAssert(s.fds[fdi].ident == 0)
 
-    var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+    var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
     epv.events = EPOLLIN or EPOLLRDHUP
     if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) != 0:
@@ -383,7 +359,7 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = int(ev.efd)
   doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
   s.setKey(fdi, {Event.User}, 0, data)
-  var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+  var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = ev.efd.uint
   if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, ev.efd, addr epv) != 0:
     raiseIOSelectorsError(osLastError())
@@ -392,7 +368,7 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
 proc selectInto*[T](s: Selector[T], timeout: int,
                     results: var openarray[ReadyKey]): int =
   var
-    resTable: array[MAX_EPOLL_EVENTS, epoll_event]
+    resTable: array[MAX_EPOLL_EVENTS, EpollEvent]
     maxres = MAX_EPOLL_EVENTS
     i, k: int
 
@@ -482,7 +458,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
             rkey.events.incl(Event.User)
 
       if Event.Oneshot in pkey.events:
-        var epv = epoll_event()
+        var epv = EpollEvent()
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, cint(fdi), addr epv) != 0:
           raiseIOSelectorsError(osLastError())
         # we will not clear key until it will be unregistered, so
@@ -505,16 +481,19 @@ proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
 template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
-proc getData*[T](s: Selector[T], fd: SocketHandle|int): T =
+proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
+  return s.fds[fd].ident != 0
+
+proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     result = s.fds[fdi].data
 
 proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: T): bool =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     s.fds[fdi].data = data
     result = true
 
@@ -523,8 +502,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body
 
 template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
@@ -532,8 +511,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body1
   else:
     body2
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 7786de46a..af5aa15df 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -144,7 +144,7 @@ proc newSelectEvent*(): SelectEvent =
   result.rfd = fds[0]
   result.wfd = fds[1]
 
-proc setEvent*(ev: SelectEvent) =
+proc trigger*(ev: SelectEvent) =
   var data: uint64 = 1
   if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
     raiseIOSelectorsError(osLastError())
@@ -243,7 +243,7 @@ proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
   doAssert(pkey.ident != 0,
-           "Descriptor [" & $fdi & "] is not registered in the queue!")
+           "Descriptor $# is not registered in the queue!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
 
   if pkey.events != events:
@@ -452,10 +452,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
 
   if timeout != -1:
     if timeout >= 1000:
-      tv.tv_sec = (timeout div 1_000).Time
+      tv.tv_sec = posix.Time(timeout div 1_000)
       tv.tv_nsec = (timeout %% 1_000) * 1_000_000
     else:
-      tv.tv_sec = 0.Time
+      tv.tv_sec = posix.Time(0)
       tv.tv_nsec = timeout * 1_000_000
   else:
     ptv = nil
@@ -584,16 +584,19 @@ proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
 template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
-proc getData*[T](s: Selector[T], fd: SocketHandle|int): T =
+proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
+  return s.fds[fd].ident != 0
+
+proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     result = s.fds[fdi].data
 
 proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: T): bool =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     s.fds[fdi].data = data
     result = true
 
@@ -602,8 +605,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body
 
 template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
@@ -611,8 +614,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body1
   else:
     body2
diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim
index 1b90e0806..cc06aa592 100644
--- a/lib/pure/ioselects/ioselectors_poll.nim
+++ b/lib/pure/ioselects/ioselectors_poll.nim
@@ -208,7 +208,7 @@ proc newSelectEvent*(): SelectEvent =
   result.rfd = fds[0]
   result.wfd = fds[1]
 
-proc setEvent*(ev: SelectEvent) =
+proc trigger*(ev: SelectEvent) =
   var data: uint64 = 1
   if posix.write(ev.wfd, addr data, sizeof(uint64)) != sizeof(uint64):
     raiseIOSelectorsError(osLastError())
@@ -279,16 +279,19 @@ proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
 template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
-proc getData*[T](s: Selector[T], fd: SocketHandle|int): T =
+proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
+  return s.fds[fd].ident != 0
+
+proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     result = s.fds[fdi].data
 
 proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: T): bool =
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
+  if fdi in s:
     s.fds[fdi].data = data
     result = true
 
@@ -297,8 +300,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body
 
 template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
@@ -306,8 +309,8 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
   mixin checkFd
   let fdi = int(fd)
   s.checkFd(fdi)
-  if s.fds[fdi].ident != 0:
-    var value = addr(s.fds[fdi].data)
+  if fdi in s:
+    var value = addr(s.getData(fdi))
     body1
   else:
     body2
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index dc3451d52..c787f0070 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -154,7 +154,7 @@ when defined(windows):
     result.rsock = rsock
     result.wsock = wsock
 
-  proc setEvent*(ev: SelectEvent) =
+  proc trigger*(ev: SelectEvent) =
     var data: uint64 = 1
     if winlean.send(ev.wsock, cast[pointer](addr data),
                     cint(sizeof(uint64)), 0) != sizeof(uint64):
@@ -178,7 +178,7 @@ else:
     result.rsock = SocketHandle(fds[0])
     result.wsock = SocketHandle(fds[1])
 
-  proc setEvent*(ev: SelectEvent) =
+  proc trigger*(ev: SelectEvent) =
     var data: uint64 = 1
     if posix.write(cint(ev.wsock), addr data, sizeof(uint64)) != sizeof(uint64):
       raiseIOSelectorsError(osLastError())
@@ -279,8 +279,9 @@ proc updateHandle*[T](s: Selector[T], fd: SocketHandle,
         inc(s.count)
       pkey.events = events
 
-proc unregister*[T](s: Selector[T], fd: SocketHandle) =
+proc unregister*[T](s: Selector[T], fd: SocketHandle|int) =
   s.withSelectLock():
+    let fd = fd.SocketHandle
     var pkey = s.getKey(fd)
     if Event.Read in pkey.events:
       IOFD_CLR(fd, addr s.rSet)
@@ -379,6 +380,16 @@ proc flush*[T](s: Selector[T]) = discard
 template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
+proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
+  s.withSelectLock():
+    result = false
+
+    let fdi = int(fd)
+    for i in 0..<FD_SETSIZE:
+      if s.fds[i].ident == fdi:
+        return true
+      inc(i)
+
 when hasThreadSupport:
   template withSelectLock[T](s: Selector[T], body: untyped) =
     acquire(s.lock)
@@ -391,15 +402,12 @@ else:
   template withSelectLock[T](s: Selector[T], body: untyped) =
     body
 
-proc getData*[T](s: Selector[T], fd: SocketHandle|int): T =
+proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   s.withSelectLock():
     let fdi = int(fd)
-    var i = 0
-    while i < FD_SETSIZE:
+    for i in 0..<FD_SETSIZE:
       if s.fds[i].ident == fdi:
-        result = s.fds[i].data
-        break
-      inc(i)
+        return s.fds[i].data
 
 proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: T): bool =
   s.withSelectLock():
@@ -431,16 +439,17 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
                       body1, body2: untyped) =
   mixin withSelectLock
   s.withSelectLock():
-    var value: ptr T
-    let fdi = int(fd)
-    var i = 0
-    while i < FD_SETSIZE:
-      if s.fds[i].ident == fdi:
-        value = addr(s.fds[i].data)
-        break
-      inc(i)
-    if i != FD_SETSIZE:
-      body1
-    else:
-      body2
+    block:
+      var value: ptr T
+      let fdi = int(fd)
+      var i = 0
+      while i < FD_SETSIZE:
+        if s.fds[i].ident == fdi:
+          value = addr(s.fds[i].data)
+          break
+        inc(i)
+      if i != FD_SETSIZE:
+        body1
+      else:
+        body2
 
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index fd7a3af03..b5b84863a 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -823,13 +823,13 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
   of nnkBracket: # array
     if x.len == 0: return newCall(bindSym"newJArray")
     result = newNimNode(nnkBracket)
-    for i in 0 .. <x.len:
+    for i in 0 ..< x.len:
       result.add(toJson(x[i]))
     result = newCall(bindSym"%", result)
   of nnkTableConstr: # object
     if x.len == 0: return newCall(bindSym"newJObject")
     result = newNimNode(nnkTableConstr)
-    for i in 0 .. <x.len:
+    for i in 0 ..< x.len:
       x[i].expectKind nnkExprColonExpr
       result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
     result = newCall(bindSym"%", result)
@@ -1303,7 +1303,7 @@ else:
     case getVarType(x)
     of JArray:
       result = newJArray()
-      for i in 0 .. <x.len:
+      for i in 0 ..< x.len:
         result.add(x[i].convertObject())
     of JObject:
       result = newJObject()
@@ -1346,6 +1346,16 @@ proc createJsonIndexer(jsonNode: NimNode,
     indexNode
   )
 
+proc transformJsonIndexer(jsonNode: NimNode): NimNode =
+  case jsonNode.kind
+  of nnkBracketExpr:
+    result = newNimNode(nnkCurlyExpr)
+  else:
+    result = jsonNode.copy()
+
+  for child in jsonNode:
+    result.add(transformJsonIndexer(child))
+
 template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
                         ast: string) =
   if node.kind notin kinds:
@@ -1449,7 +1459,7 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
   # We need to build up a list of conditions from each ``of`` branch so that
   # we can then negate it to get ``else``.
   var cond = newIdentNode("false")
-  for i in 1 .. <len(recCaseNode):
+  for i in 1 ..< len(recCaseNode):
     if recCaseNode[i].kind == nnkElse:
       break
 
@@ -1511,7 +1521,7 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
     exprColonExpr.add(getEnumCall)
 
     # Iterate through each `of` branch.
-    for i in 1 .. <field.len:
+    for i in 1 ..< field.len:
       case field[i].kind
       of nnkOfBranch:
         result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
@@ -1524,6 +1534,35 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
 
   doAssert result.len > 0
 
+proc processFields(obj: NimNode,
+                   jsonNode: NimNode): seq[NimNode] {.compileTime.} =
+  ## Process all the fields of an ``ObjectTy`` and any of its
+  ## parent type's fields (via inheritance).
+  result = @[]
+  case obj.kind
+  of nnkObjectTy:
+    expectKind(obj[2], nnkRecList)
+    for field in obj[2]:
+      let nodes = processObjField(field, jsonNode)
+      result.add(nodes)
+
+    # process parent type fields
+    case obj[1].kind
+    of nnkBracketExpr:
+      assert $obj[1][0] == "ref"
+      result.add(processFields(getType(obj[1][1]), jsonNode))
+    of nnkSym:
+      result.add(processFields(getType(obj[1]), jsonNode))
+    else:
+      discard
+  of nnkTupleTy:
+    for identDefs in obj:
+      expectKind(identDefs, nnkIdentDefs)
+      let nodes = processObjField(identDefs[0], jsonNode)
+      result.add(nodes)
+  else:
+    doAssert false, "Unable to process field type: " & $obj.kind
+
 proc processType(typeName: NimNode, obj: NimNode,
                  jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
   ## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
@@ -1533,20 +1572,21 @@ proc processType(typeName: NimNode, obj: NimNode,
   ## .. code-block::plain
   ##     ObjectTy
   ##       Empty
-  ##       Empty
+  ##       InheritanceInformation
   ##       RecList
   ##         Sym "events"
   case obj.kind
-  of nnkObjectTy:
+  of nnkObjectTy, nnkTupleTy:
     # Create object constructor.
-    result = newNimNode(nnkObjConstr)
-    result.add(typeName) # Name of the type to construct.
+    result =
+      if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
+      else: newNimNode(nnkPar)
 
-    # Process each object field and add it as an exprColonExpr
-    expectKind(obj[2], nnkRecList)
-    for field in obj[2]:
-      let nodes = processObjField(field, jsonNode)
-      result.add(nodes)
+    if obj.kind == nnkObjectTy:
+      result.add(typeName) # Name of the type to construct.
+
+    # Process each object/tuple field and add it as an exprColonExpr
+    result.add(processFields(obj, jsonNode))
 
     # Object might be null. So we need to check for that.
     if isRef:
@@ -1569,25 +1609,14 @@ proc processType(typeName: NimNode, obj: NimNode,
         `getEnumCall`
       )
   of nnkSym:
-    case ($typeName).normalize
-    of "float":
-      result = quote do:
-        (
-          verifyJsonKind(`jsonNode`, {JFloat, JInt}, astToStr(`jsonNode`));
-          if `jsonNode`.kind == JFloat: `jsonNode`.fnum else: `jsonNode`.num.float
-        )
+    let name = ($typeName).normalize
+    case name
     of "string":
       result = quote do:
         (
           verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
           if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
         )
-    of "int":
-      result = quote do:
-        (
-          verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
-          `jsonNode`.num.int
-        )
     of "biggestint":
       result = quote do:
         (
@@ -1601,12 +1630,36 @@ proc processType(typeName: NimNode, obj: NimNode,
           `jsonNode`.bval
         )
     else:
-      doAssert false, "Unable to process nnkSym " & $typeName
+      if name.startsWith("int") or name.startsWith("uint"):
+        result = quote do:
+          (
+            verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
+            `jsonNode`.num.`obj`
+          )
+      elif name.startsWith("float"):
+        result = quote do:
+          (
+            verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
+            if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
+          )
+      else:
+        doAssert false, "Unable to process nnkSym " & $typeName
   else:
     doAssert false, "Unable to process type: " & $obj.kind
 
   doAssert(not result.isNil(), "processType not initialised.")
 
+import options
+proc workaroundMacroNone[T](): Option[T] =
+  none(T)
+
+proc depth(n: NimNode, current = 0): int =
+  result = 1
+  for child in n:
+    let d = 1 + child.depth(current + 1)
+    if d > result:
+      result = d
+
 proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
   ## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
   ##
@@ -1616,10 +1669,50 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
   # echo("--createConsuctor-- \n", treeRepr(typeSym))
   # echo()
 
+  if depth(jsonNode) > 150:
+    error("The `to` macro does not support ref objects with cycles.", jsonNode)
+
   case typeSym.kind
   of nnkBracketExpr:
     var bracketName = ($typeSym[0]).normalize
     case bracketName
+    of "option":
+      # TODO: Would be good to verify that this is Option[T] from
+      # options module I suppose.
+      let lenientJsonNode = transformJsonIndexer(jsonNode)
+
+      let optionGeneric = typeSym[1]
+      let value = createConstructor(typeSym[1], jsonNode)
+      let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
+
+      result = quote do:
+        (
+          if `lenientJsonNode`.isNil: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
+        )
+    of "table", "orderedtable":
+      let tableKeyType = typeSym[1]
+      if ($tableKeyType).cmpIgnoreStyle("string") != 0:
+        error("JSON doesn't support keys of type " & $tableKeyType)
+      let tableValueType = typeSym[2]
+
+      let forLoopKey = genSym(nskForVar, "key")
+      let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
+      let constructorNode = createConstructor(tableValueType, indexerNode)
+
+      let tableInit =
+        if bracketName == "table":
+          bindSym("initTable")
+        else:
+          bindSym("initOrderedTable")
+
+      # Create a statement expression containing a for loop.
+      result = quote do:
+        (
+          var map = `tableInit`[`tableKeyType`, `tableValueType`]();
+          verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
+          for `forLoopKey` in keys(`jsonNode`.fields): map[`forLoopKey`] = `constructorNode`;
+          map
+        )
     of "ref":
       # Ref type.
       var typeName = $typeSym[1]
@@ -1640,7 +1733,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
         (
           var list: `typeSym` = @[];
           verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`);
+          for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
           list
         )
     of "array":
@@ -1654,7 +1747,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
         (
           var list: `typeSym`;
           verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`;
+          for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`;
           list
         )
 
@@ -1663,12 +1756,23 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
       let obj = getType(typeSym)
       result = processType(typeSym, obj, jsonNode, false)
   of nnkSym:
+    # Handle JsonNode.
+    if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
+      return jsonNode
+
+    # Handle all other types.
     let obj = getType(typeSym)
     if obj.kind == nnkBracketExpr:
       # When `Sym "Foo"` turns out to be a `ref object`.
       result = createConstructor(obj, jsonNode)
     else:
       result = processType(typeSym, obj, jsonNode, false)
+  of nnkTupleTy:
+    result = processType(typeSym, typeSym, jsonNode, false)
+  of nnkPar:
+    # TODO: The fact that `jsonNode` here works to give a good line number
+    # is weird. Specifying typeSym should work but doesn't.
+    error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
   else:
     doAssert false, "Unable to create constructor for: " & $typeSym.kind
 
@@ -1683,7 +1787,7 @@ proc postProcessValue(value: NimNode): NimNode =
     result = postProcess(value)
   else:
     result = value
-    for i in 0 .. <len(result):
+    for i in 0 ..< len(result):
       result[i] = postProcessValue(result[i])
 
 proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
@@ -1796,10 +1900,18 @@ macro to*(node: JsonNode, T: typedesc): untyped =
   expectKind(typeNode, nnkBracketExpr)
   doAssert(($typeNode[0]).normalize == "typedesc")
 
-  result = createConstructor(typeNode[1], node)
+  # Create `temp` variable to store the result in case the user calls this
+  # on `parseJson` (see bug #6604).
+  result = newNimNode(nnkStmtListExpr)
+  let temp = genSym(nskLet, "temp")
+  result.add quote do:
+    let `temp` = `node`
+
+  let constructor = createConstructor(typeNode[1], temp)
   # TODO: Rename postProcessValue and move it (?)
-  result = postProcessValue(result)
+  result.add(postProcessValue(constructor))
 
+  # echo(treeRepr(result))
   # echo(toStrLit(result))
 
 when false:
@@ -1849,8 +1961,8 @@ when isMainModule:
       discard parseJson"""{ invalid"""
     except:
       discard
-  # memory diff should less than 2M
-  doAssert(abs(getOccupiedMem() - startMemory) < 2 * 1024 * 1024)
+  # memory diff should less than 4M
+  doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
 
 
   # test `$`
diff --git a/lib/pure/lenientops.nim b/lib/pure/lenientops.nim
new file mode 100644
index 000000000..b124a9512
--- /dev/null
+++ b/lib/pure/lenientops.nim
@@ -0,0 +1,60 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module offers implementations of common binary operations
+## like ``+``, ``-``, ``*``, ``/`` and comparison operations,
+## which work for mixed float/int operands.
+## All operations convert the integer operand into the
+## type of the float operand. For numerical expressions, the return
+## type is always the type of the float involved in the expresssion,
+## i.e., there is no auto conversion from float32 to float64.
+##
+## Note: In general, auto-converting from int to float loses
+## information, which is why these operators live in a separate
+## module. Use with care.
+##
+## Regarding binary comparison, this module only provides unequal operators.
+## The equality operator ``==`` is omitted, because depending on the use case
+## either casting to float or rounding to int might be preferred, and users
+## should make an explicit choice.
+
+import typetraits
+
+proc `+`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
+  F(i) + f
+proc `+`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
+  f + F(i)
+
+proc `-`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
+  F(i) - f
+proc `-`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
+  f - F(i)
+
+proc `*`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
+  F(i) * f
+proc `*`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
+  f * F(i)
+
+proc `/`*[I: SomeInteger, F: SomeReal](i: I, f: F): F {.noSideEffect, inline.} =
+  F(i) / f
+proc `/`*[I: SomeInteger, F: SomeReal](f: F, i: I): F {.noSideEffect, inline.} =
+  f / F(i)
+
+proc `<`*[I: SomeInteger, F: SomeReal](i: I, f: F): bool {.noSideEffect, inline.} =
+  F(i) < f
+proc `<`*[I: SomeInteger, F: SomeReal](f: F, i: I): bool {.noSideEffect, inline.} =
+  f < F(i)
+proc `<=`*[I: SomeInteger, F: SomeReal](i: I, f: F): bool {.noSideEffect, inline.} =
+  F(i) <= f
+proc `<=`*[I: SomeInteger, F: SomeReal](f: F, i: I): bool {.noSideEffect, inline.} =
+  f <= F(i)
+
+# Note that we must not defined `>=` and `>`, because system.nim already has a
+# template with signature (x, y: untyped): untyped, which would lead to
+# ambigous calls.
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index e2a5bed96..830820fd1 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -202,13 +202,17 @@ when not defined(js):
 
   proc countLogLines(logger: RollingFileLogger): int =
     result = 0
-    for line in logger.file.lines():
+    let fp = open(logger.baseName, fmRead)
+    for line in fp.lines():
       result.inc()
+    fp.close()
 
   proc countFiles(filename: string): int =
     # Example: file.log.1
     result = 0
-    let (dir, name, ext) = splitFile(filename)
+    var (dir, name, ext) = splitFile(filename)
+    if dir == "":
+      dir = "."
     for kind, path in walkDir(dir):
       if kind == pcFile:
         let llfn = name & ext & ExtSep
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index c4c731acf..6ee830786 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -283,7 +283,7 @@ proc to*[T](data: string): T =
   loadAny(newStringStream(data), toAny(result), tab)
 
 when not defined(testing) and isMainModule:
-  template testit(x: expr) = echo($$to[type(x)]($$x))
+  template testit(x: untyped) = echo($$to[type(x)]($$x))
 
   var x: array[0..4, array[0..4, string]] = [
     ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
index 7022c21d9..6366fee1a 100644
--- a/lib/pure/matchers.nim
+++ b/lib/pure/matchers.nim
@@ -48,7 +48,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
      "aero", "jobs", "museum": return true
   else: return false
 
-proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
+proc parseInt*(s: string, value: var int, validRange: HSlice[int, int]) {.
   noSideEffect, rtl, extern: "nmatchParseInt".} =
   ## parses `s` into an integer in the range `validRange`. If successful,
   ## `value` is modified to contain the result. Otherwise no exception is
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 8037b31b0..cbd04a145 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -184,6 +184,8 @@ when not defined(JS):
   proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
   proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
     ## computes x to power raised of y.
+    ##
+    ## To compute power between integers, use `^` e.g. 2 ^ 6
 
   proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
   proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
@@ -289,6 +291,8 @@ when not defined(JS):
     ##  echo fmod(-2.5, 0.3) ## -0.1
 
 else:
+  proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
+  proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
   proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.}
   proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.}
   proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.}
@@ -347,15 +351,19 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
     result = round0(x*mult)/mult
 
 when not defined(JS):
-  proc frexp*(x: float32, exponent: var int): float32 {.
+  proc c_frexp*(x: float32, exponent: var int32): float32 {.
     importc: "frexp", header: "<math.h>".}
-  proc frexp*(x: float64, exponent: var int): float64 {.
+  proc c_frexp*(x: float64, exponent: var int32): float64 {.
     importc: "frexp", header: "<math.h>".}
+  proc frexp*[T, U](x: T, exponent: var U): T =
     ## Split a number into mantissa and exponent.
     ## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
     ## and less than 1) and the integer value n such that `x` (the original
     ## float value) equals m * 2**n. frexp stores n in `exponent` and returns
     ## m.
+    var exp: int32
+    result = c_frexp(x, exp)
+    exponent = exp
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
     if x == 0.0:
@@ -364,9 +372,14 @@ else:
     elif x < 0.0:
       result = -frexp(-x, exponent)
     else:
-      var ex = floor(log2(x))
-      exponent = round(ex)
+      var ex = trunc(log2(x))
+      exponent = int(ex)
       result = x / pow(2.0, ex)
+      if abs(result) >= 1:
+        inc(exponent)
+        result = result / 2
+      if exponent == 1024 and result == 0.0:
+        result = 0.99999999999999988898
 
 proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## Breaks `x` into an integral and a fractional part.
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index 9b2d25267..5c73381ff 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -257,10 +257,13 @@ proc close*(f: var MemFile) =
   when defined(windows):
     if f.wasOpened:
       error = unmapViewOfFile(f.mem) == 0
-      lastErr = osLastError()
-      error = (closeHandle(f.mapHandle) == 0) or error
-      if f.fHandle != INVALID_HANDLE_VALUE:
-        error = (closeHandle(f.fHandle) == 0) or error
+      if not error:
+        error = closeHandle(f.mapHandle) == 0
+        if not error and f.fHandle != INVALID_HANDLE_VALUE:
+          discard closeHandle(f.fHandle)
+          f.fHandle = INVALID_HANDLE_VALUE
+      if error:
+        lastErr = osLastError()
   else:
     error = munmap(f.mem, f.size) != 0
     lastErr = osLastError()
diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim
index f18cf5b90..6ac0c4e56 100644
--- a/lib/pure/mersenne.nim
+++ b/lib/pure/mersenne.nim
@@ -17,7 +17,7 @@ type
 proc newMersenneTwister*(seed: uint32): MersenneTwister =
   result.index = 0
   result.mt[0] = seed
-  for i in 1..623'u32:
+  for i in 1'u32 .. 623'u32:
     result.mt[i] = (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
 
 proc generateNumbers(m: var MersenneTwister) =
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index 1e315afb4..b397ef47b 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -491,6 +491,8 @@ const mimes* = {
     "vrml": "x-world/x-vrml",
     "wrl": "x-world/x-vrml"}
 
+from strutils import startsWith
+
 proc newMimetypes*(): MimeDB =
   ## Creates a new Mimetypes database. The database will contain the most
   ## common mimetypes.
@@ -498,8 +500,11 @@ proc newMimetypes*(): MimeDB =
 
 proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
   ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
-  ## could not be found.
-  result = mimedb.mimes.getOrDefault(ext)
+  ## could not be found. ``ext`` can start with an optional dot which is ignored.
+  if ext.startsWith("."):
+    result = mimedb.mimes.getOrDefault(ext.substr(1))
+  else:
+    result = mimedb.mimes.getOrDefault(ext)
   if result == "":
     return default
 
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 215a301b6..aad6ab3e8 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -58,7 +58,7 @@
 ## You can then begin accepting connections using the ``accept`` procedure.
 ##
 ## .. code-block:: Nim
-##   var client = newSocket()
+##   var client = new Socket
 ##   var address = ""
 ##   while true:
 ##     socket.acceptAddr(client, address)
@@ -145,7 +145,7 @@ type
 
   SOBool* = enum ## Boolean socket options.
     OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
-    OptOOBInline, OptReuseAddr, OptReusePort
+    OptOOBInline, OptReuseAddr, OptReusePort, OptNoDelay
 
   ReadLineResult* = enum ## result for readLineAsync
     ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
@@ -857,15 +857,23 @@ proc close*(socket: Socket) =
         # shutdown i.e not wait for the peers "close notify" alert with a second
         # call to SSLShutdown
         let res = SSLShutdown(socket.sslHandle)
-        SSLFree(socket.sslHandle)
-        socket.sslHandle = nil
         if res == 0:
           discard
         elif res != 1:
           socketError(socket, res)
   finally:
+    when defineSsl:
+      if socket.isSSL and socket.sslHandle != nil:
+        SSLFree(socket.sslHandle)
+        socket.sslHandle = nil
+
     socket.fd.close()
 
+when defined(posix):
+  from posix import TCP_NODELAY
+else:
+  from winlean import TCP_NODELAY
+
 proc toCInt*(opt: SOBool): cint =
   ## Converts a ``SOBool`` into its Socket Option cint representation.
   case opt
@@ -877,6 +885,7 @@ proc toCInt*(opt: SOBool): cint =
   of OptOOBInline: SO_OOBINLINE
   of OptReuseAddr: SO_REUSEADDR
   of OptReusePort: SO_REUSEPORT
+  of OptNoDelay: TCP_NODELAY
 
 proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
   tags: [ReadIOEffect].} =
@@ -899,6 +908,12 @@ proc getPeerAddr*(socket: Socket): (string, Port) =
 proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
   tags: [WriteIOEffect].} =
   ## Sets option ``opt`` to a boolean value specified by ``value``.
+  ##
+  ## .. code-block:: Nim
+  ##   var socket = newSocket()
+  ##   socket.setSockOpt(OptReusePort, true)
+  ##   socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
+  ##
   var valuei = cint(if value: 1 else: 0)
   setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
 
@@ -1120,11 +1135,11 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
   ##
   ## When 0 is returned the socket's connection has been closed.
   ##
-  ## This function will throw an EOS exception when an error occurs. A value
+  ## This function will throw an OSError exception when an error occurs. A value
   ## lower than 0 is never returned.
   ##
   ## A timeout may be specified in milliseconds, if enough data is not received
-  ## within the time specified an ETimeout exception will be raised.
+  ## within the time specified an TimeoutError exception will be raised.
   ##
   ## **Note**: ``data`` must be initialised.
   ##
diff --git a/lib/pure/numeric.nim b/lib/pure/numeric.nim
deleted file mode 100644
index ccda3a146..000000000
--- a/lib/pure/numeric.nim
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Robert Persson
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## **Warning:** This module will be moved out of the stdlib and into a
-## Nimble package, don't use it.
-
-type OneVarFunction* = proc (x: float): float
-
-{.deprecated: [TOneVarFunction: OneVarFunction].}
-
-proc brent*(xmin,xmax:float, function:OneVarFunction, tol:float,maxiter=1000):
-  tuple[rootx, rooty: float, success: bool]=
-  ## Searches `function` for a root between `xmin` and `xmax`
-  ## using brents method. If the function value at `xmin`and `xmax` has the
-  ## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
-  ## and succes is set to false.
-  ## Otherwise there exists at least one root and success is set to true.
-  ## This root is searched for at most `maxiter` iterations.
-  ## If `tol` tolerance is reached within `maxiter` iterations
-  ## the root refinement stops and success=true.
-
-  # see http://en.wikipedia.org/wiki/Brent%27s_method
-  var
-    a=xmin
-    b=xmax
-    c=a
-    d=1.0e308
-    fa=function(a)
-    fb=function(b)
-    fc=fa
-    s=0.0
-    fs=0.0
-    mflag:bool
-    i=0
-    tmp2:float
-
-  if fa*fb>=0:
-    if abs(fa)<abs(fb):
-      return (a,fa,false)
-    else:
-      return (b,fb,false)
-
-  if abs(fa)<abs(fb):
-    swap(fa,fb)
-    swap(a,b)
-
-  while fb!=0.0 and abs(a-b)>tol:
-    if fa!=fc and fb!=fc: # inverse quadratic interpolation
-      s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
-    else: #secant rule
-      s = b - fb * (b - a) / (fb - fa)
-    tmp2 = (3.0 * a + b) / 4.0
-    if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or
-      (mflag and abs(s - b) >= (abs(b - c) / 2.0)) or
-      (not mflag and abs(s - b) >= abs(c - d) / 2.0):
-      s=(a+b)/2.0
-      mflag=true
-    else:
-      if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
-        s=(a+b)/2.0
-        mflag=true
-      else:
-        mflag=false
-    fs = function(s)
-    d = c
-    c = b
-    fc = fb
-    if fa * fs<0.0:
-      b=s
-      fb=fs
-    else:
-      a=s
-      fa=fs
-    if abs(fa)<abs(fb):
-      swap(a,b)
-      swap(fa,fb)
-    inc i
-    if i>maxiter:
-      break
-
-  return (b,fb,true)
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 60b53dbe0..427a68964 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -88,7 +88,7 @@ proc generatedTime*(oid: Oid): Time =
   var tmp: int32
   var dummy = oid.time
   bigEndian32(addr(tmp), addr(dummy))
-  result = Time(tmp)
+  result = fromUnix(tmp)
 
 when not defined(testing) and isMainModule:
   let xo = genOid()
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a1ae4e250..c18d03289 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -173,33 +173,33 @@ proc findExe*(exe: string, followSymlinks: bool = true;
         return x
   result = ""
 
-proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last modification time.
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_mtime
+    return fromUnix(res.st_mtime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64)
     findClose(h)
 
-proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last read or write access time.
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_atime
+    return fromUnix(res.st_atime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64)
     findClose(h)
 
-proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s creation time.
   ##
   ## **Note:** Under POSIX OS's, the returned time may actually be the time at
@@ -208,12 +208,12 @@ proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_ctime
+    return fromUnix(res.st_ctime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64)
     findClose(h)
 
 proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
@@ -630,7 +630,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   ## the process has finished. To execute a program without having a
   ## shell involved, use the `execProcess` proc of the `osproc`
   ## module.
-  when defined(linux):
+  when defined(posix):
     result = c_system(command) shr 8
   else:
     result = c_system(command)
@@ -816,32 +816,40 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
               k = getSymlinkFileKind(y)
             yield (k, y)
 
-iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
-  tags: [ReadDirEffect].} =
-  ## Recursively walks over the directory `dir` and yields for each file in `dir`.
-  ## The full path for each file is returned. Directories are not returned.
+iterator walkDirRec*(dir: string, yieldFilter = {pcFile},
+                     followFilter = {pcDir}): string {.tags: [ReadDirEffect].} =
+  ## Recursively walks over the directory `dir` and yields for each file 
+  ## or directory in `dir`.
+  ## The full path for each file or directory is returned.
   ## **Warning**:
   ## Modifying the directory structure while the iterator
   ## is traversing may result in undefined behavior!
   ##
-  ## Walking is recursive. `filter` controls the behaviour of the iterator:
+  ## Walking is recursive. `filters` controls the behaviour of the iterator:
   ##
   ## ---------------------   ---------------------------------------------
-  ## filter                  meaning
+  ## yieldFilter             meaning
   ## ---------------------   ---------------------------------------------
   ## ``pcFile``              yield real files
   ## ``pcLinkToFile``        yield symbolic links to files
+  ## ``pcDir``               yield real directories
+  ## ``pcLinkToDir``         yield symbolic links to directories
+  ## ---------------------   ---------------------------------------------
+  ##
+  ## ---------------------   ---------------------------------------------
+  ## followFilter            meaning
+  ## ---------------------   ---------------------------------------------
   ## ``pcDir``               follow real directories
   ## ``pcLinkToDir``         follow symbolic links to directories
   ## ---------------------   ---------------------------------------------
   ##
   var stack = @[dir]
   while stack.len > 0:
-    for k,p in walkDir(stack.pop()):
-      if k in filter:
-        case k
-        of pcFile, pcLinkToFile: yield p
-        of pcDir, pcLinkToDir: stack.add(p)
+    for k, p in walkDir(stack.pop()):
+      if k in {pcDir, pcLinkToDir} and k in followFilter:
+        stack.add(p)
+      if k in yieldFilter:
+        yield p
 
 proc rawRemoveDir(dir: string) =
   when defined(windows):
@@ -1195,14 +1203,15 @@ when defined(nimdoc):
     ## Returns the number of `command line arguments`:idx: given to the
     ## application.
     ##
-    ## If your binary was called without parameters this will return zero.  You
-    ## can later query each individual paramater with `paramStr() <#paramStr>`_
+    ## Unlike `argc`:idx: in C, if your binary was called without parameters this
+    ## will return zero.
+    ## You can query each individual paramater with `paramStr() <#paramStr>`_
     ## or retrieve all of them in one go with `commandLineParams()
     ## <#commandLineParams>`_.
     ##
-    ## **Availability**: On Posix there is no portable way to get the command
-    ## line from a DLL and thus the proc isn't defined in this environment. You
-    ## can test for its availability with `declared() <system.html#declared>`_.
+    ## **Availability**: When generating a dynamic library (see --app:lib) on
+    ## Posix this proc is not defined.
+    ## Test for availability using `declared() <system.html#declared>`_.
     ## Example:
     ##
     ## .. code-block:: nim
@@ -1219,13 +1228,14 @@ when defined(nimdoc):
     ## `paramCount() <#paramCount>`_ with this proc you can call the
     ## convenience `commandLineParams() <#commandLineParams>`_.
     ##
-    ## It is possible to call ``paramStr(0)`` but this will return OS specific
+    ## Similarly to `argv`:idx: in C,
+    ## it is possible to call ``paramStr(0)`` but this will return OS specific
     ## contents (usually the name of the invoked executable). You should avoid
     ## this and call `getAppFilename() <#getAppFilename>`_ instead.
     ##
-    ## **Availability**: On Posix there is no portable way to get the command
-    ## line from a DLL and thus the proc isn't defined in this environment. You
-    ## can test for its availability with `declared() <system.html#declared>`_.
+    ## **Availability**: When generating a dynamic library (see --app:lib) on
+    ## Posix this proc is not defined.
+    ## Test for availability using `declared() <system.html#declared>`_.
     ## Example:
     ##
     ## .. code-block:: nim
@@ -1441,7 +1451,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
     winlean.sleep(int32(milsecs))
   else:
     var a, b: Timespec
-    a.tv_sec = Time(milsecs div 1000)
+    a.tv_sec = posix.Time(milsecs div 1000)
     a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
     discard posix.nanosleep(a, b)
 
@@ -1479,16 +1489,17 @@ type
     size*: BiggestInt # Size of file.
     permissions*: set[FilePermission] # File permissions
     linkCount*: BiggestInt # Number of hard links the file object has.
-    lastAccessTime*: Time # Time file was last accessed.
-    lastWriteTime*: Time # Time file was last modified/written to.
-    creationTime*: Time # Time file was created. Not supported on all systems!
+    lastAccessTime*: times.Time # Time file was last accessed.
+    lastWriteTime*: times.Time # Time file was last modified/written to.
+    creationTime*: times.Time # Time file was created. Not supported on all systems!
 
 template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## Transforms the native file info structure into the one nim uses.
   ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
   ## or a 'Stat' structure on posix
   when defined(Windows):
-    template toTime(e: FILETIME): untyped {.gensym.} = winTimeToUnixTime(rdFileTime(e)) # local templates default to bind semantics
+    template toTime(e: FILETIME): untyped {.gensym.} =
+      fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics
     template merge(a, b): untyped = a or (b shl 32)
     formalInfo.id.device = rawInfo.dwVolumeSerialNumber
     formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
@@ -1520,9 +1531,9 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
     formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
     formalInfo.size = rawInfo.st_size
     formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
-    formalInfo.lastAccessTime = rawInfo.st_atime
-    formalInfo.lastWriteTime = rawInfo.st_mtime
-    formalInfo.creationTime = rawInfo.st_ctime
+    formalInfo.lastAccessTime = fromUnix(rawInfo.st_atime.int64)
+    formalInfo.lastWriteTime = fromUnix(rawInfo.st_mtime.int64)
+    formalInfo.creationTime = fromUnix(rawInfo.st_ctime.int64)
 
     result.permissions = {}
     checkAndIncludeMode(S_IRUSR, fpUserRead)
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index dcb785c83..0d638abb9 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -558,3 +558,67 @@ proc expandTilde*(path: string): string {.
     result = getHomeDir() / path.substr(2)
   else:
     result = path
+
+proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote s, so it can be safely passed to Windows API.
+  ## Based on Python's subprocess.list2cmdline
+  ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+  let needQuote = {' ', '\t'} in s or s.len == 0
+
+  result = ""
+  var backslashBuff = ""
+  if needQuote:
+    result.add("\"")
+
+  for c in s:
+    if c == '\\':
+      backslashBuff.add(c)
+    elif c == '\"':
+      result.add(backslashBuff)
+      result.add(backslashBuff)
+      backslashBuff.setLen(0)
+      result.add("\\\"")
+    else:
+      if backslashBuff.len != 0:
+        result.add(backslashBuff)
+        backslashBuff.setLen(0)
+      result.add(c)
+
+  if needQuote:
+    result.add("\"")
+
+proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote ``s``, so it can be safely passed to POSIX shell.
+  ## Based on Python's pipes.quote
+  const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
+                         '0'..'9', 'A'..'Z', 'a'..'z'}
+  if s.len == 0:
+    return "''"
+
+  let safe = s.allCharsInSet(safeUnixChars)
+
+  if safe:
+    return s
+  else:
+    return "'" & s.replace("'", "'\"'\"'") & "'"
+
+when defined(windows) or defined(posix):
+  proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+    ## Quote ``s``, so it can be safely passed to shell.
+    when defined(windows):
+      return quoteShellWindows(s)
+    else:
+      return quoteShellPosix(s)
+
+when isMainModule:
+  assert quoteShellWindows("aaa") == "aaa"
+  assert quoteShellWindows("aaa\"") == "aaa\\\""
+  assert quoteShellWindows("") == "\"\""
+
+  assert quoteShellPosix("aaa") == "aaa"
+  assert quoteShellPosix("aaa a") == "'aaa a'"
+  assert quoteShellPosix("") == "''"
+  assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
+
+  when defined(posix):
+    assert quoteShell("") == "''"
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 07429b9a9..1625845d1 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -15,6 +15,9 @@ include "system/inclrtl"
 import
   strutils, os, strtabs, streams, cpuinfo
 
+from ospaths import quoteShell, quoteShellWindows, quoteShellPosix
+export quoteShell, quoteShellWindows, quoteShellPosix
+
 when defined(windows):
   import winlean
 else:
@@ -38,10 +41,13 @@ type
                          ## Windows: Named pipes are used so that you can peek
                          ## at the process' output streams.
     poDemon              ## Windows: The program creates no Window.
+                         ## Unix: Start the program as a demon. This is still
+                         ## work in progress!
 
   ProcessObj = object of RootObj
     when defined(windows):
       fProcessHandle: Handle
+      fThreadHandle: Handle
       inHandle, outHandle, errHandle: FileHandle
       id: Handle
     else:
@@ -49,6 +55,7 @@ type
       inStream, outStream, errStream: Stream
       id: Pid
     exitStatus: cint
+    exitFlag: bool
     options: set[ProcessOption]
 
   Process* = ref ProcessObj ## represents an operating system process
@@ -60,58 +67,6 @@ type
 const poUseShell* {.deprecated.} = poUsePath
   ## Deprecated alias for poUsePath.
 
-proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote s, so it can be safely passed to Windows API.
-  ## Based on Python's subprocess.list2cmdline
-  ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
-  let needQuote = {' ', '\t'} in s or s.len == 0
-
-  result = ""
-  var backslashBuff = ""
-  if needQuote:
-    result.add("\"")
-
-  for c in s:
-    if c == '\\':
-      backslashBuff.add(c)
-    elif c == '\"':
-      result.add(backslashBuff)
-      result.add(backslashBuff)
-      backslashBuff.setLen(0)
-      result.add("\\\"")
-    else:
-      if backslashBuff.len != 0:
-        result.add(backslashBuff)
-        backslashBuff.setLen(0)
-      result.add(c)
-
-  if needQuote:
-    result.add("\"")
-
-proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote ``s``, so it can be safely passed to POSIX shell.
-  ## Based on Python's pipes.quote
-  const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
-                         '0'..'9', 'A'..'Z', 'a'..'z'}
-  if s.len == 0:
-    return "''"
-
-  let safe = s.allCharsInSet(safeUnixChars)
-
-  if safe:
-    return s
-  else:
-    return "'" & s.replace("'", "'\"'\"'") & "'"
-
-proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote ``s``, so it can be safely passed to shell.
-  when defined(Windows):
-    return quoteShellWindows(s)
-  elif defined(posix):
-    return quoteShellPosix(s)
-  else:
-    {.error:"quoteShell is not supported on your system".}
-
 proc execProcess*(command: string,
                   args: openArray[string] = [],
                   env: StringTableRef = nil,
@@ -216,8 +171,7 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
   ## On posix, if the process has exited because of a signal, 128 + signal
   ## number will be returned.
 
-
-proc peekExitCode*(p: Process): int {.tags: [].}
+proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
   ## return -1 if the process is still running. Otherwise the process' exit code
   ##
   ## On posix, if the process has exited because of a signal, 128 + signal
@@ -280,55 +234,98 @@ proc execProcesses*(cmds: openArray[string],
   ## executes the commands `cmds` in parallel. Creates `n` processes
   ## that execute in parallel. The highest return value of all processes
   ## is returned. Runs `beforeRunEvent` before running each command.
-  when defined(posix):
-    # poParentStreams causes problems on Posix, so we simply disable it:
-    var options = options - {poParentStreams}
 
   assert n > 0
   if n > 1:
-    var q: seq[Process]
-    newSeq(q, n)
-    var m = min(n, cmds.len)
-    for i in 0..m-1:
+    var i = 0
+    var q = newSeq[Process](n)
+
+    when defined(windows):
+      var w: WOHandleArray
+      var m = min(min(n, MAXIMUM_WAIT_OBJECTS), cmds.len)
+      var wcount = m
+    else:
+      var m = min(n, cmds.len)
+
+    while i < m:
       if beforeRunEvent != nil:
         beforeRunEvent(i)
-      q[i] = startProcess(cmds[i], options=options + {poEvalCommand})
-    when defined(noBusyWaiting):
-      var r = 0
-      for i in m..high(cmds):
-        when defined(debugExecProcesses):
-          var err = ""
-          var outp = outputStream(q[r])
-          while running(q[r]) or not atEnd(outp):
-            err.add(outp.readLine())
-            err.add("\n")
-          echo(err)
-        result = max(waitForExit(q[r]), result)
-        if afterRunEvent != nil: afterRunEvent(r, q[r])
-        if q[r] != nil: close(q[r])
-        if beforeRunEvent != nil:
-          beforeRunEvent(i)
-        q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
-        r = (r + 1) mod n
-    else:
-      var i = m
-      while i <= high(cmds):
-        sleep(50)
-        for r in 0..n-1:
-          if not running(q[r]):
-            #echo(outputStream(q[r]).readLine())
-            result = max(waitForExit(q[r]), result)
-            if afterRunEvent != nil: afterRunEvent(r, q[r])
-            if q[r] != nil: close(q[r])
-            if beforeRunEvent != nil:
-              beforeRunEvent(i)
-            q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
-            inc(i)
-            if i > high(cmds): break
-    for j in 0..m-1:
-      result = max(waitForExit(q[j]), result)
-      if afterRunEvent != nil: afterRunEvent(j, q[j])
-      if q[j] != nil: close(q[j])
+      q[i] = startProcess(cmds[i], options = options + {poEvalCommand})
+      when defined(windows):
+        w[i] = q[i].fProcessHandle
+      inc(i)
+
+    var ecount = len(cmds)
+    while ecount > 0:
+      var rexit = -1
+      when defined(windows):
+        # waiting for all children, get result if any child exits
+        var ret = waitForMultipleObjects(int32(wcount), addr(w), 0'i32,
+                                         INFINITE)
+        if ret == WAIT_TIMEOUT:
+          # must not be happen
+          discard
+        elif ret == WAIT_FAILED:
+          raiseOSError(osLastError())
+        else:
+          var status: int32
+          for r in 0..m-1:
+            if not isNil(q[r]) and q[r].fProcessHandle == w[ret]:
+              discard getExitCodeProcess(q[r].fProcessHandle, status)
+              q[r].exitFlag = true
+              q[r].exitStatus = status
+              rexit = r
+              break
+      else:
+        var status: cint = 1
+        # waiting for all children, get result if any child exits
+        let res = waitpid(-1, status, 0)
+        if res > 0:
+          for r in 0..m-1:
+            if not isNil(q[r]) and q[r].id == res:
+              if WIFEXITED(status) or WIFSIGNALED(status):
+                q[r].exitFlag = true
+                q[r].exitStatus = status
+                rexit = r
+                break
+        else:
+          let err = osLastError()
+          if err == OSErrorCode(ECHILD):
+            # some child exits, we need to check our childs exit codes
+            for r in 0..m-1:
+              if (not isNil(q[r])) and (not running(q[r])):
+                q[r].exitFlag = true
+                q[r].exitStatus = status
+                rexit = r
+                break
+          elif err == OSErrorCode(EINTR):
+            # signal interrupted our syscall, lets repeat it
+            continue
+          else:
+            # all other errors are exceptions
+            raiseOSError(err)
+
+      if rexit >= 0:
+        result = max(result, q[rexit].peekExitCode())
+        if afterRunEvent != nil: afterRunEvent(rexit, q[rexit])
+        close(q[rexit])
+        if i < len(cmds):
+          if beforeRunEvent != nil: beforeRunEvent(i)
+          q[rexit] = startProcess(cmds[i],
+                                  options = options + {poEvalCommand})
+          when defined(windows):
+            w[rexit] = q[rexit].fProcessHandle
+          inc(i)
+        else:
+          when defined(windows):
+            for k in 0..wcount - 1:
+              if w[k] == q[rexit].fProcessHandle:
+                w[k] = w[wcount - 1]
+                w[wcount - 1] = 0
+                dec(wcount)
+                break
+          q[rexit] = nil
+        dec(ecount)
   else:
     for i in 0..high(cmds):
       if beforeRunEvent != nil:
@@ -370,6 +367,8 @@ when not defined(useNimRtl):
       elif not running(p): break
     close(p)
 
+template streamAccess(p) =
+  assert poParentStreams notin p.options, "API usage error: stream access not allowed when you use poParentStreams"
 
 when defined(Windows) and not defined(useNimRtl):
   # We need to implement a handle stream for Windows:
@@ -513,6 +512,7 @@ when defined(Windows) and not defined(useNimRtl):
       hi, ho, he: Handle
     new(result)
     result.options = options
+    result.exitFlag = true
     si.cb = sizeof(si).cint
     if poParentStreams notin options:
       si.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
@@ -581,28 +581,31 @@ when defined(Windows) and not defined(useNimRtl):
             "Requested command not found: '$1'. OS error:" % command)
       else:
         raiseOSError(lastError, command)
-    # Close the handle now so anyone waiting is woken:
-    discard closeHandle(procInfo.hThread)
     result.fProcessHandle = procInfo.hProcess
+    result.fThreadHandle = procInfo.hThread
     result.id = procInfo.dwProcessId
+    result.exitFlag = false
 
   proc close(p: Process) =
-    if poInteractive in p.options:
-      # somehow this is not always required on Windows:
+    if poParentStreams notin p.options:
       discard closeHandle(p.inHandle)
       discard closeHandle(p.outHandle)
       discard closeHandle(p.errHandle)
-      #discard closeHandle(p.FProcessHandle)
+    discard closeHandle(p.fThreadHandle)
+    discard closeHandle(p.fProcessHandle)
 
   proc suspend(p: Process) =
-    discard suspendThread(p.fProcessHandle)
+    discard suspendThread(p.fThreadHandle)
 
   proc resume(p: Process) =
-    discard resumeThread(p.fProcessHandle)
+    discard resumeThread(p.fThreadHandle)
 
   proc running(p: Process): bool =
-    var x = waitForSingleObject(p.fProcessHandle, 50)
-    return x == WAIT_TIMEOUT
+    if p.exitFlag:
+      return false
+    else:
+      var x = waitForSingleObject(p.fProcessHandle, 0)
+      return x == WAIT_TIMEOUT
 
   proc terminate(p: Process) =
     if running(p):
@@ -612,28 +615,48 @@ when defined(Windows) and not defined(useNimRtl):
     terminate(p)
 
   proc waitForExit(p: Process, timeout: int = -1): int =
-    discard waitForSingleObject(p.fProcessHandle, timeout.int32)
-
-    var res: int32
-    discard getExitCodeProcess(p.fProcessHandle, res)
-    result = res
-    discard closeHandle(p.fProcessHandle)
+    if p.exitFlag:
+      return p.exitStatus
+
+    let res = waitForSingleObject(p.fProcessHandle, timeout.int32)
+    if res == WAIT_TIMEOUT:
+      terminate(p)
+    var status: int32
+    discard getExitCodeProcess(p.fProcessHandle, status)
+    if status != STILL_ACTIVE:
+      p.exitFlag = true
+      p.exitStatus = status
+      discard closeHandle(p.fThreadHandle)
+      discard closeHandle(p.fProcessHandle)
+      result = status
+    else:
+      result = -1
 
   proc peekExitCode(p: Process): int =
-    var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT
-    if b: result = -1
-    else:
-      var res: int32
-      discard getExitCodeProcess(p.fProcessHandle, res)
-      return res
+    if p.exitFlag:
+      return p.exitStatus
+
+    result = -1
+    var b = waitForSingleObject(p.fProcessHandle, 0) == WAIT_TIMEOUT
+    if not b:
+      var status: int32
+      discard getExitCodeProcess(p.fProcessHandle, status)
+      p.exitFlag = true
+      p.exitStatus = status
+      discard closeHandle(p.fThreadHandle)
+      discard closeHandle(p.fProcessHandle)
+      result = status
 
   proc inputStream(p: Process): Stream =
+    streamAccess(p)
     result = newFileHandleStream(p.inHandle)
 
   proc outputStream(p: Process): Stream =
+    streamAccess(p)
     result = newFileHandleStream(p.outHandle)
 
   proc errorStream(p: Process): Stream =
+    streamAccess(p)
     result = newFileHandleStream(p.errHandle)
 
   proc execCmd(command: string): int =
@@ -729,9 +752,7 @@ elif not defined(useNimRtl):
     sysEnv: cstringArray
     workingDir: cstring
     pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
-    optionPoUsePath: bool
-    optionPoParentStreams: bool
-    optionPoStdErrToStdOut: bool
+    options: set[ProcessOption]
   {.deprecated: [TStartProcessData: StartProcessData].}
 
   const useProcessAuxSpawn = declared(posix_spawn) and not defined(useFork) and
@@ -756,7 +777,8 @@ elif not defined(useNimRtl):
       pStdin, pStdout, pStderr: array[0..1, cint]
     new(result)
     result.options = options
-    result.exitStatus = -3 # for ``waitForExit``
+    result.exitFlag = true
+
     if poParentStreams notin options:
       if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
          pipe(pStderr) != 0'i32:
@@ -765,7 +787,9 @@ elif not defined(useNimRtl):
     var sysCommand: string
     var sysArgsRaw: seq[string]
     if poEvalCommand in options:
-      const useShPath {.strdefine.} = "/bin/sh"
+      const useShPath {.strdefine.} =
+        when not defined(android): "/bin/sh"
+        else: "/system/bin/sh"
       sysCommand = useShPath
       sysArgsRaw = @[sysCommand, "-c", command]
       assert args.len == 0, "`args` has to be empty when using poEvalCommand."
@@ -794,10 +818,8 @@ elif not defined(useNimRtl):
     data.pStdin = pStdin
     data.pStdout = pStdout
     data.pStderr = pStderr
-    data.optionPoParentStreams = poParentStreams in options
-    data.optionPoUsePath = poUsePath in options
-    data.optionPoStdErrToStdOut = poStdErrToStdOut in options
     data.workingDir = workingDir
+    data.options = options
 
     when useProcessAuxSpawn:
       var currentDir = getCurrentDir()
@@ -811,6 +833,7 @@ elif not defined(useNimRtl):
     if poEchoCmd in options:
       echo(command, " ", join(args, " "))
     result.id = pid
+    result.exitFlag = false
 
     if poParentStreams in options:
       # does not make much sense, but better than nothing:
@@ -837,7 +860,7 @@ elif not defined(useNimRtl):
       var attr: Tposix_spawnattr
       var fops: Tposix_spawn_file_actions
 
-      template chck(e: expr) =
+      template chck(e: untyped) =
         if e != 0'i32: raiseOSError(osLastError())
 
       chck posix_spawn_file_actions_init(fops)
@@ -846,19 +869,22 @@ elif not defined(useNimRtl):
       var mask: Sigset
       chck sigemptyset(mask)
       chck posix_spawnattr_setsigmask(attr, mask)
-      chck posix_spawnattr_setpgroup(attr, 0'i32)
+      if poDemon in data.options:
+        chck posix_spawnattr_setpgroup(attr, 0'i32)
 
-      chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
-                                          POSIX_SPAWN_SETSIGMASK or
-                                          POSIX_SPAWN_SETPGROUP)
+      var flags = POSIX_SPAWN_USEVFORK or
+                  POSIX_SPAWN_SETSIGMASK
+      if poDemon in data.options:
+        flags = flags or POSIX_SPAWN_SETPGROUP
+      chck posix_spawnattr_setflags(attr, flags)
 
-      if not data.optionPoParentStreams:
+      if not (poParentStreams in data.options):
         chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
         chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
         chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
         chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
         chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
-        if data.optionPoStdErrToStdOut:
+        if (poStdErrToStdOut in data.options):
           chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
         else:
           chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
@@ -868,7 +894,7 @@ elif not defined(useNimRtl):
         setCurrentDir($data.workingDir)
       var pid: Pid
 
-      if data.optionPoUsePath:
+      if (poUsePath in data.options):
         res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
       else:
         res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
@@ -930,7 +956,7 @@ elif not defined(useNimRtl):
     # Warning: no GC here!
     # Or anything that touches global structures - all called nim procs
     # must be marked with stackTrace:off. Inspect C code after making changes.
-    if not data.optionPoParentStreams:
+    if not (poParentStreams in data.options):
       discard close(data.pStdin[writeIdx])
       if dup2(data.pStdin[readIdx], readIdx) < 0:
         startProcessFail(data)
@@ -938,7 +964,7 @@ elif not defined(useNimRtl):
       if dup2(data.pStdout[writeIdx], writeIdx) < 0:
         startProcessFail(data)
       discard close(data.pStderr[readIdx])
-      if data.optionPoStdErrToStdOut:
+      if (poStdErrToStdOut in data.options):
         if dup2(data.pStdout[writeIdx], 2) < 0:
           startProcessFail(data)
       else:
@@ -952,7 +978,7 @@ elif not defined(useNimRtl):
     discard close(data.pErrorPipe[readIdx])
     discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
 
-    if data.optionPoUsePath:
+    if (poUsePath in data.options):
       when defined(uClibc) or defined(linux):
         # uClibc environment (OpenWrt included) doesn't have the full execvpe
         let exe = findExe(data.sysCommand)
@@ -984,19 +1010,22 @@ elif not defined(useNimRtl):
     if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
 
   proc running(p: Process): bool =
-    var ret : int
-    var status : cint = 1
-    ret = waitpid(p.id, status, WNOHANG)
-    if ret == int(p.id):
-      if isExitStatus(status):
-        p.exitStatus = status
-        return false
-      else:
-        return true
-    elif ret == 0:
-      return true # Can't establish status. Assume running.
-    else:
+    if p.exitFlag:
       return false
+    else:
+      var status: cint = 1
+      let ret = waitpid(p.id, status, WNOHANG)
+      if ret == int(p.id):
+        if isExitStatus(status):
+          p.exitFlag = true
+          p.exitStatus = status
+          return false
+        else:
+          return true
+      elif ret == 0:
+        return true # Can't establish status. Assume running.
+      else:
+        raiseOSError(osLastError())
 
   proc terminate(p: Process) =
     if kill(p.id, SIGTERM) != 0'i32:
@@ -1011,13 +1040,14 @@ elif not defined(useNimRtl):
     import kqueue, times
 
     proc waitForExit(p: Process, timeout: int = -1): int =
-      if p.exitStatus != -3:
+      if p.exitFlag:
         return exitStatus(p.exitStatus)
 
       if timeout == -1:
-        var status : cint = 1
+        var status: cint = 1
         if waitpid(p.id, status, 0) < 0:
           raiseOSError(osLastError())
+        p.exitFlag = true
         p.exitStatus = status
       else:
         var kqFD = kqueue()
@@ -1030,15 +1060,15 @@ elif not defined(useNimRtl):
         var tmspec: Timespec
 
         if timeout >= 1000:
-          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_sec = posix.Time(timeout div 1_000)
           tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
         else:
-          tmspec.tv_sec = 0.Time
+          tmspec.tv_sec = posix.Time(0)
           tmspec.tv_nsec = (timeout * 1_000_000)
 
         try:
           while true:
-            var status : cint = 1
+            var status: cint = 1
             var count = kevent(kqFD, addr(kevIn), 1, addr(kevOut), 1,
                                addr(tmspec))
             if count < 0:
@@ -1051,12 +1081,14 @@ elif not defined(useNimRtl):
                 raiseOSError(osLastError())
               if waitpid(p.id, status, 0) < 0:
                 raiseOSError(osLastError())
+              p.exitFlag = true
               p.exitStatus = status
               break
             else:
               if kevOut.ident == p.id.uint and kevOut.filter == EVFILT_PROC:
                 if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitFlag = true
                 p.exitStatus = status
                 break
               else:
@@ -1077,36 +1109,33 @@ elif not defined(useNimRtl):
         var b: Timespec
         b.tv_sec = e.tv_sec
         b.tv_nsec = e.tv_nsec
-        e.tv_sec = (e.tv_sec - s.tv_sec).Time
+        e.tv_sec = e.tv_sec - s.tv_sec
         if e.tv_nsec >= s.tv_nsec:
           e.tv_nsec -= s.tv_nsec
         else:
-          if e.tv_sec == 0.Time:
+          if e.tv_sec == posix.Time(0):
             raise newException(ValueError, "System time was modified")
           else:
             diff = s.tv_nsec - e.tv_nsec
             e.tv_nsec = 1_000_000_000 - diff
-        t.tv_sec = (t.tv_sec - e.tv_sec).Time
+        t.tv_sec = t.tv_sec - e.tv_sec
         if t.tv_nsec >= e.tv_nsec:
           t.tv_nsec -= e.tv_nsec
         else:
-          t.tv_sec = (int(t.tv_sec) - 1).Time
+          t.tv_sec = t.tv_sec - posix.Time(1)
           diff = e.tv_nsec - t.tv_nsec
           t.tv_nsec = 1_000_000_000 - diff
         s.tv_sec = b.tv_sec
         s.tv_nsec = b.tv_nsec
 
-      #if waitPid(p.id, p.exitStatus, 0) == int(p.id):
-      # ``waitPid`` fails if the process is not running anymore. But then
-      # ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is
-      # initialized with -3, wrong success exit codes are prevented.
-      if p.exitStatus != -3:
+      if p.exitFlag:
         return exitStatus(p.exitStatus)
 
       if timeout == -1:
-        var status : cint = 1
+        var status: cint = 1
         if waitpid(p.id, status, 0) < 0:
           raiseOSError(osLastError())
+        p.exitFlag = true
         p.exitStatus = status
       else:
         var nmask, omask: Sigset
@@ -1125,10 +1154,10 @@ elif not defined(useNimRtl):
             raiseOSError(osLastError())
 
         if timeout >= 1000:
-          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_sec = posix.Time(timeout div 1_000)
           tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
         else:
-          tmspec.tv_sec = 0.Time
+          tmspec.tv_sec = posix.Time(0)
           tmspec.tv_nsec = (timeout * 1_000_000)
 
         try:
@@ -1138,9 +1167,10 @@ elif not defined(useNimRtl):
             let res = sigtimedwait(nmask, sinfo, tmspec)
             if res == SIGCHLD:
               if sinfo.si_pid == p.id:
-                var status : cint = 1
+                var status: cint = 1
                 if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitFlag = true
                 p.exitStatus = status
                 break
               else:
@@ -1161,9 +1191,10 @@ elif not defined(useNimRtl):
                 # timeout expired, so we trying to kill process
                 if posix.kill(p.id, SIGKILL) == -1:
                   raiseOSError(osLastError())
-                var status : cint = 1
+                var status: cint = 1
                 if waitpid(p.id, status, 0) < 0:
                   raiseOSError(osLastError())
+                p.exitFlag = true
                 p.exitStatus = status
                 break
               else:
@@ -1181,12 +1212,13 @@ elif not defined(useNimRtl):
   proc peekExitCode(p: Process): int =
     var status = cint(0)
     result = -1
-    if p.exitStatus != -3:
+    if p.exitFlag:
       return exitStatus(p.exitStatus)
 
     var ret = waitpid(p.id, status, WNOHANG)
     if ret > 0:
       if isExitStatus(status):
+        p.exitFlag = true
         p.exitStatus = status
         result = exitStatus(status)
 
@@ -1197,16 +1229,19 @@ elif not defined(useNimRtl):
     stream = newFileStream(f)
 
   proc inputStream(p: Process): Stream =
+    streamAccess(p)
     if p.inStream == nil:
       createStream(p.inStream, p.inHandle, fmWrite)
     return p.inStream
 
   proc outputStream(p: Process): Stream =
+    streamAccess(p)
     if p.outStream == nil:
       createStream(p.outStream, p.outHandle, fmRead)
     return p.outStream
 
   proc errorStream(p: Process): Stream =
+    streamAccess(p)
     if p.errStream == nil:
       createStream(p.errStream, p.errHandle, fmRead)
     return p.errStream
@@ -1287,16 +1322,3 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
       result[1] = peekExitCode(p)
       if result[1] != -1: break
   close(p)
-
-when isMainModule:
-  assert quoteShellWindows("aaa") == "aaa"
-  assert quoteShellWindows("aaa\"") == "aaa\\\""
-  assert quoteShellWindows("") == "\"\""
-
-  assert quoteShellPosix("aaa") == "aaa"
-  assert quoteShellPosix("aaa a") == "'aaa a'"
-  assert quoteShellPosix("") == "''"
-  assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
-
-  when defined(posix):
-    assert quoteShell("") == "''"
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index 77b145a73..071858b7c 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -32,7 +32,7 @@
 ##   import parsecsv
 ##   import os
 ##   # Prepare a file
-##   var csv_content = """One,Two,Three,Four
+##   var content = """One,Two,Three,Four
 ##   1,2,3,4
 ##   10,20,30,40
 ##   100,200,300,400
@@ -72,7 +72,10 @@ proc raiseEInvalidCsv(filename: string, line, col: int,
                       msg: string) {.noreturn.} =
   var e: ref CsvError
   new(e)
-  e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
+  if filename.len == 0:
+    e.msg = "Error: " & msg
+  else:
+    e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
   raise e
 
 proc error(my: CsvParser, pos: int, msg: string) =
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
index 2e8dbe140..a2ff9bf0c 100644
--- a/lib/pure/parseopt2.nim
+++ b/lib/pure/parseopt2.nim
@@ -35,7 +35,7 @@ type
     cmd: seq[string]
     pos: int
     remainingShortOptions: string
-    kind*: CmdLineKind        ## the dected command line token
+    kind*: CmdLineKind        ## the detected command line token
     key*, val*: TaintedString ## key and value pair; ``key`` is the option
                               ## or the argument, ``value`` is not "" if
                               ## the option was given a value
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index 00d007d01..ae192ab9a 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -55,6 +55,13 @@ const
     ";", ":", ",", "(", ")", "[", "]", "."
   ]
 
+  reservedKeywords = @[
+    # statements
+    "select", "from", "where", "group", "limit", "having",
+    # functions
+    "count",
+  ]
+
 proc open(L: var SqlLexer, input: Stream, filename: string) =
   lexbase.open(L, input)
   L.filename = filename
@@ -274,16 +281,16 @@ proc getSymbol(c: var SqlLexer, tok: var Token) =
   c.bufpos = pos
   tok.kind = tkIdentifier
 
-proc getQuotedIdentifier(c: var SqlLexer, tok: var Token) =
+proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote='\"') =
   var pos = c.bufpos + 1
   var buf = c.buf
   tok.kind = tkQuotedIdentifier
   while true:
     var ch = buf[pos]
-    if ch == '\"':
-      if buf[pos+1] == '\"':
+    if ch == quote:
+      if buf[pos+1] == quote:
         inc(pos, 2)
-        add(tok.literal, '\"')
+        add(tok.literal, quote)
       else:
         inc(pos)
         break
@@ -442,7 +449,8 @@ proc getTok(c: var SqlLexer, tok: var Token) =
     add(tok.literal, '.')
   of '0'..'9': getNumeric(c, tok)
   of '\'': getString(c, tok, tkStringConstant)
-  of '"': getQuotedIdentifier(c, tok)
+  of '"': getQuotedIdentifier(c, tok, '"')
+  of '`': getQuotedIdentifier(c, tok, '`')
   of lexbase.EndOfFile:
     tok.kind = tkEof
     tok.literal = "[EOF]"
@@ -450,7 +458,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
      '\128'..'\255':
     getSymbol(c, tok)
   of '+', '-', '*', '/', '<', '>', '=', '~', '!', '@', '#', '%',
-     '^', '&', '|', '`', '?':
+     '^', '&', '|', '?':
     getOperator(c, tok)
   else:
     add(tok.literal, c.buf[c.bufpos])
@@ -462,27 +470,27 @@ proc errorStr(L: SqlLexer, msg: string): string =
 
 # ----------------------------- parser ----------------------------------------
 
-# Operator/Element	Associativity	Description
-# .	                left    	table/column name separator
-# ::            	left	        PostgreSQL-style typecast
-# [ ]	                left    	array element selection
-# -	                right	        unary minus
-# ^                     left	        exponentiation
-# * / %	                left	        multiplication, division, modulo
-# + -	                left	        addition, subtraction
-# IS	 	IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
-# ISNULL	 	test for null
-# NOTNULL	 	test for not null
-# (any other)	        left    	all other native and user-defined oprs
-# IN	          	set membership
-# BETWEEN	 	range containment
-# OVERLAPS	 	time interval overlap
-# LIKE ILIKE SIMILAR	 	string pattern matching
-# < >	 	less than, greater than
-# =	                right	        equality, assignment
-# NOT	                right	        logical negation
-# AND	                left	        logical conjunction
-# OR              	left	        logical disjunction
+# Operator/Element   Associativity  Description
+# .                  left           table/column name separator
+# ::                 left           PostgreSQL-style typecast
+# [ ]                left           array element selection
+# -                  right          unary minus
+# ^                  left           exponentiation
+# * / %              left           multiplication, division, modulo
+# + -                left           addition, subtraction
+# IS                                IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
+# ISNULL                            test for null
+# NOTNULL                           test for not null
+# (any other)        left           all other native and user-defined oprs
+# IN                                set membership
+# BETWEEN                           range containment
+# OVERLAPS                          time interval overlap
+# LIKE ILIKE SIMILAR                string pattern matching
+# < >                               less than, greater than
+# =                  right          equality, assignment
+# NOT                right          logical negation
+# AND                left           logical conjunction
+# OR                 left           logical disjunction
 
 type
   SqlNodeKind* = enum ## kind of SQL abstract syntax tree
@@ -504,6 +512,7 @@ type
     nkPrefix,
     nkInfix,
     nkCall,
+    nkPrGroup,
     nkColumnReference,
     nkReferences,
     nkDefault,
@@ -518,11 +527,15 @@ type
     nkSelect,
     nkSelectDistinct,
     nkSelectColumns,
+    nkSelectPair,
     nkAsgn,
     nkFrom,
+    nkFromItemPair,
     nkGroup,
+    nkLimit,
     nkHaving,
     nkOrder,
+    nkJoin,
     nkDesc,
     nkUnion,
     nkIntersect,
@@ -658,10 +671,12 @@ proc getPrecedence(p: SqlParser): int =
   elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or
        isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or
        isKeyw(p, "like"):
-    result = 3
+    result = 4
   elif isKeyw(p, "and"):
-    result = 2
+    result = 3
   elif isKeyw(p, "or"):
+    result = 2
+  elif isKeyw(p, "between"):
     result = 1
   elif p.tok.kind == tkOperator:
     # user-defined operator:
@@ -670,6 +685,7 @@ proc getPrecedence(p: SqlParser): int =
     result = - 1
 
 proc parseExpr(p: var SqlParser): SqlNode
+proc parseSelect(p: var SqlParser): SqlNode
 
 proc identOrLiteral(p: var SqlParser): SqlNode =
   case p.tok.kind
@@ -693,7 +709,8 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
     getTok(p)
   of tkParLe:
     getTok(p)
-    result = parseExpr(p)
+    result = newNode(nkPrGroup)
+    result.add(parseExpr(p))
     eat(p, tkParRi)
   else:
     sqlError(p, "expression expected")
@@ -745,7 +762,7 @@ proc lowestExprAux(p: var SqlParser, v: var SqlNode, limit: int): int =
   result = opPred
   while opPred > limit:
     node = newNode(nkInfix)
-    opNode = newNode(nkIdent, p.tok.literal)
+    opNode = newNode(nkIdent, p.tok.literal.toLower())
     getTok(p)
     result = lowestExprAux(p, v2, opPred)
     node.add(opNode)
@@ -921,6 +938,19 @@ proc parseWhere(p: var SqlParser): SqlNode =
   result = newNode(nkWhere)
   result.add(parseExpr(p))
 
+proc parseFromItem(p: var SqlParser): SqlNode =
+  result = newNode(nkFromItemPair)
+  if p.tok.kind == tkParLe:
+    getTok(p)
+    var select = parseSelect(p)
+    result.add(select)
+    eat(p, tkParRi)
+  else:
+    result.add(parseExpr(p))
+  if isKeyw(p, "as"):
+    getTok(p)
+    result.add(parseExpr(p))
+
 proc parseIndexDef(p: var SqlParser): SqlNode =
   result = parseIfNotExists(p, nkCreateIndex)
   if isKeyw(p, "primary"):
@@ -956,6 +986,7 @@ proc parseInsert(p: var SqlParser): SqlNode =
   if p.tok.kind == tkParLe:
     var n = newNode(nkColumnList)
     parseParIdentList(p, n)
+    result.add n
   else:
     result.add(nil)
   if isKeyw(p, "default"):
@@ -996,6 +1027,8 @@ proc parseUpdate(p: var SqlParser): SqlNode =
 
 proc parseDelete(p: var SqlParser): SqlNode =
   getTok(p)
+  if isOpr(p, "*"):
+    getTok(p)
   result = newNode(nkDelete)
   eat(p, "from")
   result.add(primary(p))
@@ -1018,7 +1051,12 @@ proc parseSelect(p: var SqlParser): SqlNode =
       a.add(newNode(nkIdent, "*"))
       getTok(p)
     else:
-      a.add(parseExpr(p))
+      var pair = newNode(nkSelectPair)
+      pair.add(parseExpr(p))
+      a.add(pair)
+      if isKeyw(p, "as"):
+        getTok(p)
+        pair.add(parseExpr(p))
     if p.tok.kind != tkComma: break
     getTok(p)
   result.add(a)
@@ -1026,7 +1064,7 @@ proc parseSelect(p: var SqlParser): SqlNode =
     var f = newNode(nkFrom)
     while true:
       getTok(p)
-      f.add(parseExpr(p))
+      f.add(parseFromItem(p))
       if p.tok.kind != tkComma: break
     result.add(f)
   if isKeyw(p, "where"):
@@ -1040,6 +1078,11 @@ proc parseSelect(p: var SqlParser): SqlNode =
       if p.tok.kind != tkComma: break
       getTok(p)
     result.add(g)
+  if isKeyw(p, "limit"):
+    getTok(p)
+    var l = newNode(nkLimit)
+    l.add(parseExpr(p))
+    result.add(l)
   if isKeyw(p, "having"):
     var h = newNode(nkHaving)
     while true:
@@ -1072,6 +1115,19 @@ proc parseSelect(p: var SqlParser): SqlNode =
       if p.tok.kind != tkComma: break
       getTok(p)
     result.add(n)
+  if isKeyw(p, "join") or isKeyw(p, "inner") or isKeyw(p, "outer") or isKeyw(p, "cross"):
+    var join = newNode(nkJoin)
+    result.add(join)
+    if isKeyw(p, "join"):
+      join.add(newNode(nkIdent, ""))
+      getTok(p)
+    else:
+      join.add(newNode(nkIdent, p.tok.literal.toLower()))
+      getTok(p)
+      eat(p, "join")
+    join.add(parseFromItem(p))
+    eat(p, "on")
+    join.add(parseExpr(p))
 
 proc parseStmt(p: var SqlParser; parent: SqlNode) =
   if isKeyw(p, "create"):
@@ -1103,7 +1159,7 @@ proc parseStmt(p: var SqlParser; parent: SqlNode) =
   elif isKeyw(p, "begin"):
     getTok(p)
   else:
-    sqlError(p, "CREATE expected")
+    sqlError(p, "SELECT, CREATE, UPDATE or DELETE expected")
 
 proc open(p: var SqlParser, input: Stream, filename: string) =
   ## opens the parser `p` and assigns the input stream `input` to it.
@@ -1115,13 +1171,13 @@ proc open(p: var SqlParser, input: Stream, filename: string) =
 
 proc parse(p: var SqlParser): SqlNode =
   ## parses the content of `p`'s input stream and returns the SQL AST.
-  ## Syntax errors raise an `EInvalidSql` exception.
+  ## Syntax errors raise an `SqlParseError` exception.
   result = newNode(nkStmtList)
   while p.tok.kind != tkEof:
     parseStmt(p, result)
+    if p.tok.kind == tkEof:
+      break
     eat(p, tkSemicolon)
-  if result.len == 1:
-    result = result.sons[0]
 
 proc close(p: var SqlParser) =
   ## closes the parser `p`. The associated input stream is closed too.
@@ -1130,7 +1186,7 @@ proc close(p: var SqlParser) =
 proc parseSQL*(input: Stream, filename: string): SqlNode =
   ## parses the SQL from `input` into an AST and returns the AST.
   ## `filename` is only used for error messages.
-  ## Syntax errors raise an `EInvalidSql` exception.
+  ## Syntax errors raise an `SqlParseError` exception.
   var p: SqlParser
   open(p, input, filename)
   try:
@@ -1138,29 +1194,74 @@ proc parseSQL*(input: Stream, filename: string): SqlNode =
   finally:
     close(p)
 
-proc ra(n: SqlNode, s: var string, indent: int)
+proc parseSQL*(input: string, filename=""): SqlNode =
+  ## parses the SQL from `input` into an AST and returns the AST.
+  ## `filename` is only used for error messages.
+  ## Syntax errors raise an `SqlParseError` exception.
+  parseSQL(newStringStream(input), "")
+
+
+type
+  SqlWriter = object
+    indent: int
+    upperCase: bool
+    buffer: string
+
+proc add(s: var SqlWriter, thing: char) =
+  s.buffer.add(thing)
+
+proc add(s: var SqlWriter, thing: string) =
+  if s.buffer.len > 0 and s.buffer[^1] notin {' ', '\L', '(', '.'}:
+    s.buffer.add(" ")
+  s.buffer.add(thing)
+
+proc addKeyw(s: var SqlWriter, thing: string) =
+  var keyw = thing
+  if s.upperCase:
+    keyw = keyw.toUpper()
+  s.add(keyw)
+
+proc addIden(s: var SqlWriter, thing: string) =
+  var iden = thing
+  if iden.toLower() in reservedKeywords:
+    iden = '"' & iden & '"'
+  s.add(iden)
+
+proc ra(n: SqlNode, s: var SqlWriter)
+
+proc rs(n: SqlNode, s: var SqlWriter, prefix = "(", suffix = ")", sep = ", ") =
+  if n.len > 0:
+    s.add(prefix)
+    for i in 0 .. n.len-1:
+      if i > 0: s.add(sep)
+      ra(n.sons[i], s)
+    s.add(suffix)
+
+proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',') =
+  if n.len > 0:
+    for i in 0 .. n.len-1:
+      if i > 0: s.add(sep)
+      ra(n.sons[i], s)
 
-proc rs(n: SqlNode, s: var string, indent: int,
-        prefix = "(", suffix = ")",
-        sep = ", ") =
+proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',', prefix, suffix: char) =
   if n.len > 0:
     s.add(prefix)
     for i in 0 .. n.len-1:
       if i > 0: s.add(sep)
-      ra(n.sons[i], s, indent)
+      ra(n.sons[i], s)
     s.add(suffix)
 
-proc ra(n: SqlNode, s: var string, indent: int) =
+proc ra(n: SqlNode, s: var SqlWriter) =
   if n == nil: return
   case n.kind
   of nkNone: discard
   of nkIdent:
-    if allCharsInSet(n.strVal, {'\33'..'\127'}):
+    if allCharsInSet(n.strVal, {'\33'..'\127'}) and n.strVal.toLower() notin reservedKeywords:
       s.add(n.strVal)
     else:
       s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"")
   of nkStringLit:
-    s.add(escape(n.strVal, "e'", "'"))
+    s.add(escape(n.strVal, "'", "'"))
   of nkBitStringLit:
     s.add("b'" & n.strVal & "'")
   of nkHexStringLit:
@@ -1168,217 +1269,206 @@ proc ra(n: SqlNode, s: var string, indent: int) =
   of nkIntegerLit, nkNumericLit:
     s.add(n.strVal)
   of nkPrimaryKey:
-    s.add(" primary key")
-    rs(n, s, indent)
+    s.addKeyw("primary key")
+    rs(n, s)
   of nkForeignKey:
-    s.add(" foreign key")
-    rs(n, s, indent)
+    s.addKeyw("foreign key")
+    rs(n, s)
   of nkNotNull:
-    s.add(" not null")
+    s.addKeyw("not null")
   of nkNull:
-    s.add(" null")
+    s.addKeyw("null")
   of nkDot:
-    ra(n.sons[0], s, indent)
-    s.add(".")
-    ra(n.sons[1], s, indent)
+    ra(n.sons[0], s)
+    s.add('.')
+    ra(n.sons[1], s)
   of nkDotDot:
-    ra(n.sons[0], s, indent)
+    ra(n.sons[0], s)
     s.add(". .")
-    ra(n.sons[1], s, indent)
+    ra(n.sons[1], s)
   of nkPrefix:
-    s.add('(')
-    ra(n.sons[0], s, indent)
+    ra(n.sons[0], s)
     s.add(' ')
-    ra(n.sons[1], s, indent)
-    s.add(')')
+    ra(n.sons[1], s)
   of nkInfix:
-    s.add('(')
-    ra(n.sons[1], s, indent)
+    ra(n.sons[1], s)
     s.add(' ')
-    ra(n.sons[0], s, indent)
+    ra(n.sons[0], s)
     s.add(' ')
-    ra(n.sons[2], s, indent)
-    s.add(')')
+    ra(n.sons[2], s)
   of nkCall, nkColumnReference:
-    ra(n.sons[0], s, indent)
+    ra(n.sons[0], s)
     s.add('(')
     for i in 1..n.len-1:
-      if i > 1: s.add(", ")
-      ra(n.sons[i], s, indent)
+      if i > 1: s.add(',')
+      ra(n.sons[i], s)
+    s.add(')')
+  of nkPrGroup:
+    s.add('(')
+    s.addMulti(n)
     s.add(')')
   of nkReferences:
-    s.add(" references ")
-    ra(n.sons[0], s, indent)
+    s.addKeyw("references")
+    ra(n.sons[0], s)
   of nkDefault:
-    s.add(" default ")
-    ra(n.sons[0], s, indent)
+    s.addKeyw("default")
+    ra(n.sons[0], s)
   of nkCheck:
-    s.add(" check ")
-    ra(n.sons[0], s, indent)
+    s.addKeyw("check")
+    ra(n.sons[0], s)
   of nkConstraint:
-    s.add(" constraint ")
-    ra(n.sons[0], s, indent)
-    s.add(" check ")
-    ra(n.sons[1], s, indent)
+    s.addKeyw("constraint")
+    ra(n.sons[0], s)
+    s.addKeyw("check")
+    ra(n.sons[1], s)
   of nkUnique:
-    s.add(" unique")
-    rs(n, s, indent)
+    s.addKeyw("unique")
+    rs(n, s)
   of nkIdentity:
-    s.add(" identity")
+    s.addKeyw("identity")
   of nkColumnDef:
-    s.add("\n  ")
-    rs(n, s, indent, "", "", " ")
+    rs(n, s, "", "", " ")
   of nkStmtList:
     for i in 0..n.len-1:
-      ra(n.sons[i], s, indent)
-      s.add("\n")
+      ra(n.sons[i], s)
+      s.add(';')
   of nkInsert:
     assert n.len == 3
-    s.add("insert into ")
-    ra(n.sons[0], s, indent)
-    ra(n.sons[1], s, indent)
+    s.addKeyw("insert into")
+    ra(n.sons[0], s)
+    s.add(' ')
+    ra(n.sons[1], s)
     if n.sons[2].kind == nkDefault:
-      s.add("default values")
+      s.addKeyw("default values")
     else:
-      s.add("\nvalues ")
-      ra(n.sons[2], s, indent)
-    s.add(';')
+      ra(n.sons[2], s)
   of nkUpdate:
-    s.add("update ")
-    ra(n.sons[0], s, indent)
-    s.add(" set ")
+    s.addKeyw("update")
+    ra(n.sons[0], s)
+    s.addKeyw("set")
     var L = n.len
     for i in 1 .. L-2:
       if i > 1: s.add(", ")
       var it = n.sons[i]
       assert it.kind == nkAsgn
-      ra(it, s, indent)
-    ra(n.sons[L-1], s, indent)
-    s.add(';')
+      ra(it, s)
+    ra(n.sons[L-1], s)
   of nkDelete:
-    s.add("delete from ")
-    ra(n.sons[0], s, indent)
-    ra(n.sons[1], s, indent)
-    s.add(';')
+    s.addKeyw("delete from")
+    ra(n.sons[0], s)
+    ra(n.sons[1], s)
   of nkSelect, nkSelectDistinct:
-    s.add("select ")
+    s.addKeyw("select")
     if n.kind == nkSelectDistinct:
-      s.add("distinct ")
-    rs(n.sons[0], s, indent, "", "", ", ")
-    for i in 1 .. n.len-1: ra(n.sons[i], s, indent)
-    s.add(';')
+      s.addKeyw("distinct")
+    s.addMulti(n.sons[0])
+    for i in 1 .. n.len-1:
+      ra(n.sons[i], s)
   of nkSelectColumns:
     assert(false)
+  of nkSelectPair:
+    ra(n.sons[0], s)
+    if n.sons.len == 2:
+      s.addKeyw("as")
+      ra(n.sons[1], s)
+  of nkFromItemPair:
+    if n.sons[0].kind == nkIdent:
+      ra(n.sons[0], s)
+    else:
+      assert n.sons[0].kind == nkSelect
+      s.add('(')
+      ra(n.sons[0], s)
+      s.add(')')
+    if n.sons.len == 2:
+      s.addKeyw("as")
+      ra(n.sons[1], s)
   of nkAsgn:
-    ra(n.sons[0], s, indent)
+    ra(n.sons[0], s)
     s.add(" = ")
-    ra(n.sons[1], s, indent)
+    ra(n.sons[1], s)
   of nkFrom:
-    s.add("\nfrom ")
-    rs(n, s, indent, "", "", ", ")
+    s.addKeyw("from")
+    s.addMulti(n)
   of nkGroup:
-    s.add("\ngroup by")
-    rs(n, s, indent, "", "", ", ")
+    s.addKeyw("group by")
+    s.addMulti(n)
+  of nkLimit:
+    s.addKeyw("limit")
+    s.addMulti(n)
   of nkHaving:
-    s.add("\nhaving")
-    rs(n, s, indent, "", "", ", ")
+    s.addKeyw("having")
+    s.addMulti(n)
   of nkOrder:
-    s.add("\norder by ")
-    rs(n, s, indent, "", "", ", ")
+    s.addKeyw("order by")
+    s.addMulti(n)
+  of nkJoin:
+    var joinType = n.sons[0].strVal
+    if joinType == "":
+      joinType = "join"
+    else:
+      joinType &= " " & "join"
+    s.addKeyw(joinType)
+    ra(n.sons[1], s)
+    s.addKeyw("on")
+    ra(n.sons[2], s)
   of nkDesc:
-    ra(n.sons[0], s, indent)
-    s.add(" desc")
+    ra(n.sons[0], s)
+    s.addKeyw("desc")
   of nkUnion:
-    s.add(" union")
+    s.addKeyw("union")
   of nkIntersect:
-    s.add(" intersect")
+    s.addKeyw("intersect")
   of nkExcept:
-    s.add(" except")
+    s.addKeyw("except")
   of nkColumnList:
-    rs(n, s, indent)
+    rs(n, s)
   of nkValueList:
-    s.add("values ")
-    rs(n, s, indent)
+    s.addKeyw("values")
+    rs(n, s)
   of nkWhere:
-    s.add("\nwhere ")
-    ra(n.sons[0], s, indent)
+    s.addKeyw("where")
+    ra(n.sons[0], s)
   of nkCreateTable, nkCreateTableIfNotExists:
-    s.add("create table ")
+    s.addKeyw("create table")
     if n.kind == nkCreateTableIfNotExists:
-      s.add("if not exists ")
-    ra(n.sons[0], s, indent)
+      s.addKeyw("if not exists")
+    ra(n.sons[0], s)
     s.add('(')
     for i in 1..n.len-1:
-      if i > 1: s.add(", ")
-      ra(n.sons[i], s, indent)
+      if i > 1: s.add(',')
+      ra(n.sons[i], s)
     s.add(");")
   of nkCreateType, nkCreateTypeIfNotExists:
-    s.add("create type ")
+    s.addKeyw("create type")
     if n.kind == nkCreateTypeIfNotExists:
-      s.add("if not exists ")
-    ra(n.sons[0], s, indent)
-    s.add(" as ")
-    ra(n.sons[1], s, indent)
-    s.add(';')
+      s.addKeyw("if not exists")
+    ra(n.sons[0], s)
+    s.addKeyw("as")
+    ra(n.sons[1], s)
   of nkCreateIndex, nkCreateIndexIfNotExists:
-    s.add("create index ")
+    s.addKeyw("create index")
     if n.kind == nkCreateIndexIfNotExists:
-      s.add("if not exists ")
-    ra(n.sons[0], s, indent)
-    s.add(" on ")
-    ra(n.sons[1], s, indent)
+      s.addKeyw("if not exists")
+    ra(n.sons[0], s)
+    s.addKeyw("on")
+    ra(n.sons[1], s)
     s.add('(')
     for i in 2..n.len-1:
       if i > 2: s.add(", ")
-      ra(n.sons[i], s, indent)
+      ra(n.sons[i], s)
     s.add(");")
   of nkEnumDef:
-    s.add("enum ")
-    rs(n, s, indent)
+    s.addKeyw("enum")
+    rs(n, s)
 
-# What I want:
-#
-#select(columns = [T1.all, T2.name],
-#       fromm = [T1, T2],
-#       where = T1.name ==. T2.name,
-#       orderby = [name]):
-#
-#for row in dbQuery(db, """select x, y, z
-#                          from a, b
-#                          where a.name = b.name"""):
-#
-
-#select x, y, z:
-#  fromm: Table1, Table2
-#  where: x.name == y.name
-#db.select(fromm = [t1, t2], where = t1.name == t2.name):
-#for x, y, z in db.select(fromm = a, b where = a.name == b.name):
-#  writeLine x, y, z
-
-proc renderSQL*(n: SqlNode): string =
+proc renderSQL*(n: SqlNode, upperCase=false): string =
   ## Converts an SQL abstract syntax tree to its string representation.
-  result = ""
-  ra(n, result, 0)
+  var s: SqlWriter
+  s.buffer = ""
+  s.upperCase = upperCase
+  ra(n, s)
+  return s.buffer
 
 proc `$`*(n: SqlNode): string =
   ## an alias for `renderSQL`.
   renderSQL(n)
-
-when not defined(testing) and isMainModule:
-  echo(renderSQL(parseSQL(newStringStream("""
-      CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
-      CREATE TABLE holidays (
-         num_weeks int,
-         happiness happiness
-      );
-      CREATE INDEX table1_attr1 ON table1(attr1);
-
-      SELECT * FROM myTab WHERE col1 = 'happy';
-  """), "stdin")))
-
-# CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
-# CREATE TABLE holidays (
-#    num_weeks int,
-#    happiness happiness
-# );
-# CREATE INDEX table1_attr1 ON table1(attr1)
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index b78e8d000..57387e62e 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -8,6 +8,8 @@
 #
 
 ## This module contains helpers for parsing tokens, numbers, identifiers, etc.
+##
+## To unpack raw bytes look at the `streams <streams.html>`_ module.
 
 {.deadCodeElim: on.}
 
@@ -85,6 +87,23 @@ proc parseOct*(s: string, number: var int, start = 0): int  {.
     inc(i)
   if foundDigit: result = i-start
 
+proc parseBin*(s: string, number: var int, start = 0): int  {.
+  rtl, extern: "npuParseBin", noSideEffect.} =
+  ## parses an binary number and stores its value in ``number``. Returns
+  ## the number of the parsed characters or 0 in case of an error.
+  var i = start
+  var foundDigit = false
+  if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
+  while true:
+    case s[i]
+    of '_': discard
+    of '0'..'1':
+      number = number shl 1 or (ord(s[i]) - ord('0'))
+      foundDigit = true
+    else: break
+    inc(i)
+  if foundDigit: result = i-start
+
 proc parseIdent*(s: string, ident: var string, start = 0): int =
   ## parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
@@ -250,6 +269,31 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
   elif result != 0:
     number = int(res)
 
+proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
+  ## parses a natural number into ``b``. This cannot raise an overflow
+  ## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
+  ## The number of processed character is returned.
+  ## This is usually what you really want to use instead of `parseInt`:idx:.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   var res = 0
+  ##   discard parseSaturatedNatural("848", res)
+  ##   doAssert res == 848
+  var i = start
+  if s[i] == '+': inc(i)
+  if s[i] in {'0'..'9'}:
+    b = 0
+    while s[i] in {'0'..'9'}:
+      let c = ord(s[i]) - ord('0')
+      if b <= (high(int) - c) div 10:
+        b = b * 10 + c
+      else:
+        b = high(int)
+      inc(i)
+      while s[i] == '_': inc(i) # underscores are allowed and ignored
+    result = i - start
+
 # overflowChecks doesn't work with BiggestUInt
 proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
   var
@@ -391,16 +435,43 @@ when isMainModule:
   let input = "$test{}  $this is ${an{  example}}  "
   let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
                    (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
-  assert toSeq(interpolatedFragments(input)) == expected
+  doAssert toSeq(interpolatedFragments(input)) == expected
 
   var value = 0
   discard parseHex("0x38", value)
-  assert value == 56
+  doAssert value == 56
   discard parseHex("0x34", value)
-  assert value == 56 * 256 + 52
+  doAssert value == 56 * 256 + 52
   value = -1
   discard parseHex("0x38", value)
-  assert value == -200
+  doAssert value == -200
+
+  value = -1
+  doAssert(parseSaturatedNatural("848", value) == 3)
+  doAssert value == 848
+
+  value = -1
+  discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
+  doAssert value == high(int)
 
+  value = -1
+  discard parseSaturatedNatural("9223372036854775808", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("9223372036854775807", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("18446744073709551616", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("18446744073709551615", value)
+  doAssert value == high(int)
+
+  value = -1
+  doAssert(parseSaturatedNatural("1_000_000", value) == 9)
+  doAssert value == 1_000_000
 
 {.pop.}
diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim
deleted file mode 100644
index e286c5d17..000000000
--- a/lib/pure/poly.nim
+++ /dev/null
@@ -1,371 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Robert Persson
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## **Warning:** This module will be moved out of the stdlib and into a
-## Nimble package, don't use it.
-
-import math
-import strutils
-import numeric
-
-type
-  Poly* = object
-      cofs:seq[float]
-
-{.deprecated: [TPoly: Poly].}
-
-proc degree*(p:Poly):int=
-  ## Returns the degree of the polynomial,
-  ## that is the number of coefficients-1
-  return p.cofs.len-1
-
-
-proc eval*(p:Poly,x:float):float=
-  ## Evaluates a polynomial function value for `x`
-  ## quickly using Horners method
-  var n=p.degree
-  result=p.cofs[n]
-  dec n
-  while n>=0:
-    result = result*x+p.cofs[n]
-    dec n
-
-proc `[]` *(p:Poly;idx:int):float=
-  ## Gets a coefficient of the polynomial.
-  ## p[2] will returns the quadric term, p[3] the cubic etc.
-  ## Out of bounds index will return 0.0.
-  if idx<0 or idx>p.degree:
-      return 0.0
-  return p.cofs[idx]
-
-proc `[]=` *(p:var Poly;idx:int,v:float)=
-  ## Sets an coefficient of the polynomial by index.
-  ## p[2] set the quadric term, p[3] the cubic etc.
-  ## If index is out of range for the coefficients,
-  ## the polynomial grows to the smallest needed degree.
-  assert(idx>=0)
-
-  if idx>p.degree:  #polynomial must grow
-    var oldlen=p.cofs.len
-    p.cofs.setLen(idx+1)
-    for q in oldlen.. <high(p.cofs):
-      p.cofs[q]=0.0 #new-grown coefficients set to zero
-
-  p.cofs[idx]=v
-
-
-iterator items*(p:Poly):float=
-  ## Iterates through the coefficients of the polynomial.
-  var i=p.degree
-  while i>=0:
-    yield p[i]
-    dec i
-
-proc clean*(p:var Poly;zerotol=0.0)=
-  ## Removes leading zero coefficients of the polynomial.
-  ## An optional tolerance can be given for what's considered zero.
-  var n=p.degree
-  var relen=false
-
-  while n>0 and abs(p[n])<=zerotol:    # >0 => keep at least one coefficient
-    dec n
-    relen=true
-
-  if relen: p.cofs.setLen(n+1)
-
-
-proc `$` *(p:Poly):string =
-  ## Gets a somewhat reasonable string representation of the polynomial
-  ## The format should be compatible with most online function plotters,
-  ## for example directly in google search
-  result=""
-  var first=true #might skip + sign if first coefficient
-
-  for idx in countdown(p.degree,0):
-    let a=p[idx]
-
-    if a==0.0:
-      continue
-
-    if a>= 0.0 and not first:
-      result.add('+')
-    first=false
-
-    if a!=1.0 or idx==0:
-      result.add(formatFloat(a,ffDefault,0))
-    if idx>=2:
-      result.add("x^" & $idx)
-    elif idx==1:
-      result.add("x")
-
-  if result=="":
-      result="0"
-
-
-proc derivative*(p: Poly): Poly=
-  ## Returns a new polynomial, which is the derivative of `p`
-  newSeq[float](result.cofs,p.degree)
-  for idx in 0..high(result.cofs):
-    result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
-
-proc diff*(p:Poly,x:float):float=
-  ## Evaluates the differentiation of a polynomial with
-  ## respect to `x` quickly using a modifed Horners method
-  var n=p.degree
-  result=p[n]*float(n)
-  dec n
-  while n>=1:
-    result = result*x+p[n]*float(n)
-    dec n
-
-proc integral*(p:Poly):Poly=
-  ## Returns a new polynomial which is the indefinite
-  ## integral of `p`. The constant term is set to 0.0
-  newSeq(result.cofs,p.cofs.len+1)
-  result.cofs[0]=0.0  #constant arbitrary term, use 0.0
-  for i in 1..high(result.cofs):
-    result.cofs[i]=p.cofs[i-1]/float(i)
-
-
-proc integrate*(p:Poly;xmin,xmax:float):float=
-  ## Computes the definite integral of `p` between `xmin` and `xmax`
-  ## quickly using a modified version of Horners method
-  var
-    n=p.degree
-    s1=p[n]/float(n+1)
-    s2=s1
-    fac:float
-
-  dec n
-  while n>=0:
-    fac=p[n]/float(n+1)
-    s1 = s1*xmin+fac
-    s2 = s2*xmax+fac
-    dec n
-
-  result=s2*xmax-s1*xmin
-
-proc initPoly*(cofs:varargs[float]):Poly=
-  ## Initializes a polynomial with given coefficients.
-  ## The most significant coefficient is first, so to create x^2-2x+3:
-  ## intiPoly(1.0,-2.0,3.0)
-  if len(cofs)<=0:
-      result.cofs= @[0.0]  #need at least one coefficient
-  else:
-    # reverse order of coefficients so indexing matches degree of
-    # coefficient...
-    result.cofs= @[]
-    for idx in countdown(cofs.len-1,0):
-      result.cofs.add(cofs[idx])
-
-  result.clean #remove leading zero terms
-
-
-proc divMod*(p,d:Poly;q,r:var Poly)=
-  ## Divides `p` with `d`, and stores the quotinent in `q` and
-  ## the remainder in `d`
-  var
-    pdeg=p.degree
-    ddeg=d.degree
-    power=p.degree-d.degree
-    ratio:float
-
-  r.cofs = p.cofs #initial remainder=numerator
-  if power<0: #denominator is larger than numerator
-    q.cofs= @ [0.0] #quotinent is 0.0
-    return # keep remainder as numerator
-
-  q.cofs=newSeq[float](power+1)
-
-  for i in countdown(pdeg,ddeg):
-    ratio=r.cofs[i]/d.cofs[ddeg]
-
-    q.cofs[i-ddeg]=ratio
-    r.cofs[i]=0.0
-
-    for j in countup(0,<ddeg):
-        var idx=i-ddeg+j
-        r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
-
-  r.clean # drop zero coefficients in remainder
-
-proc `+` *(p1:Poly,p2:Poly):Poly=
-  ## Adds two polynomials
-  var n=max(p1.cofs.len,p2.cofs.len)
-  newSeq(result.cofs,n)
-
-  for idx in countup(0,n-1):
-      result[idx]=p1[idx]+p2[idx]
-
-  result.clean # drop zero coefficients in remainder
-
-proc `*` *(p1:Poly,p2:Poly):Poly=
-  ## Multiplies the polynomial `p1` with `p2`
-  var
-    d1=p1.degree
-    d2=p2.degree
-    n=d1+d2
-    idx:int
-
-  newSeq(result.cofs,n)
-
-  for i1 in countup(0,d1):
-    for i2 in countup(0,d2):
-      idx=i1+i2
-      result[idx]=result[idx]+p1[i1]*p2[i2]
-
-  result.clean
-
-proc `*` *(p:Poly,f:float):Poly=
-  ## Multiplies the polynomial `p` with a real number
-  newSeq(result.cofs,p.cofs.len)
-  for i in 0..high(p.cofs):
-    result[i]=p.cofs[i]*f
-  result.clean
-
-proc `*` *(f:float,p:Poly):Poly=
-  ## Multiplies a real number with a polynomial
-  return p*f
-
-proc `-`*(p:Poly):Poly=
-  ## Negates a polynomial
-  result=p
-  for i in countup(0,<result.cofs.len):
-    result.cofs[i]= -result.cofs[i]
-
-proc `-` *(p1:Poly,p2:Poly):Poly=
-  ## Subtract `p1` with `p2`
-  var n=max(p1.cofs.len,p2.cofs.len)
-  newSeq(result.cofs,n)
-
-  for idx in countup(0,n-1):
-      result[idx]=p1[idx]-p2[idx]
-
-  result.clean # drop zero coefficients in remainder
-
-proc `/`*(p:Poly,f:float):Poly=
-  ## Divides polynomial `p` with a real number `f`
-  newSeq(result.cofs,p.cofs.len)
-  for i in 0..high(p.cofs):
-    result[i]=p.cofs[i]/f
-  result.clean
-
-proc `/` *(p,q:Poly):Poly=
-  ## Divides polynomial `p` with polynomial `q`
-  var dummy:Poly
-  p.divMod(q,result,dummy)
-
-proc `mod` *(p,q:Poly):Poly=
-  ## Computes the polynomial modulo operation,
-  ## that is the remainder of `p`/`q`
-  var dummy:Poly
-  p.divMod(q,dummy,result)
-
-
-proc normalize*(p:var Poly)=
-  ## Multiplies the polynomial inplace by a term so that
-  ## the leading term is 1.0.
-  ## This might lead to an unstable polynomial
-  ## if the leading term is zero.
-  p=p/p[p.degree]
-
-
-proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
-  ## Solves the quadric equation `ax^2+bx+c`, with a possible
-  ## tolerance `zerotol` to find roots of curves just 'touching'
-  ## the x axis. Returns sequence with 0,1 or 2 solutions.
-
-  var p,q,d:float
-
-  p=b/(2.0*a)
-
-  if p==Inf or p==NegInf: #linear equation..
-    var linrt= -c/b
-    if linrt==Inf or linrt==NegInf: #constant only
-      return @[]
-    return @[linrt]
-
-  q=c/a
-  d=p*p-q
-
-  if d<0.0:
-    #check for inside zerotol range for neg. roots
-    var err=a*p*p-b*p+c #evaluate error at parabola center axis
-    if(err<=zerotol): return @[-p]
-    return @[]
-  else:
-    var sr=sqrt(d)
-    result= @[-sr-p,sr-p]
-
-proc getRangeForRoots(p:Poly):tuple[xmin,xmax:float]=
-  ## helper function for `roots` function
-  ## quickly computes a range, guaranteed to contain
-  ## all the real roots of the polynomial
-  # see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
-
-  var deg=p.degree
-  var d=p[deg]
-  var bound1,bound2:float
-
-  for i in countup(0,deg):
-    var c=abs(p.cofs[i]/d)
-    bound1=max(bound1,c+1.0)
-    bound2=bound2+c
-
-  bound2=max(1.0,bound2)
-  result.xmax=min(bound1,bound2)
-  result.xmin= -result.xmax
-
-
-proc addRoot(p:Poly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
-  ## helper function for `roots` function
-  ## try to do a numeric search for a single root in range xp0-xp1,
-  ## adding it to `res` (allocating `res` if nil)
-  var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
-  if br.success:
-    if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
-      res.add(br.rootx)
-  else:
-    #this might be a 'touching' case, check function value against
-    #zero tolerance
-    if abs(br.rooty)<=zerotol:
-      if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
-        res.add(br.rootx)
-
-
-proc roots*(p:Poly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
-  ## Computes the real roots of the polynomial `p`
-  ## `tol` is the tolerance used to break searching for each root when reached.
-  ## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
-  ## and is used to find roots for curves that only 'touch' the x-axis.
-  ## `mergetol` is the tolerance, of which two x-values are considered being the same root.
-  ## `maxiter` can be used to limit the number of iterations for each root.
-  ## Returns a (possibly empty) sorted sequence with the solutions.
-  var deg=p.degree
-  if deg<=0: #constant only => no roots
-    return @[]
-  elif p.degree==1: #linear
-    var linrt= -p.cofs[0]/p.cofs[1]
-    if linrt==Inf or linrt==NegInf:
-      return @[] #constant only => no roots
-    return @[linrt]
-  elif p.degree==2:
-    return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
-  else:
-    # degree >=3 , find min/max points of polynomial with recursive
-    # derivative and do a numerical search for root between each min/max
-    var range=p.getRangeForRoots()
-    var minmax=p.derivative.roots(tol,zerotol,mergetol)
-    result= @[]
-    if minmax!=nil: #ie. we have minimas/maximas in this function
-      for x in minmax.items:
-        addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
-        range.xmin=x
-    addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)
-
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 27fbfad45..de419b9fb 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -7,16 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
-## Nim's standard random number generator. Based on the ``xoroshiro128+`` (xor/rotate/shift/rotate) library.
+## Nim's standard random number generator. Based on
+## the ``xoroshiro128+`` (xor/rotate/shift/rotate) library.
 ## * More information: http://xoroshiro.di.unimi.it/
 ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
 ##
-## Do not use this module for cryptographic use!
+## **Do not use this module for cryptographic purposes!**
 
 include "system/inclrtl"
 {.push debugger:off.}
 
-# XXX Expose RandomGenState
 when defined(JS):
   type ui = uint32
 
@@ -27,31 +27,34 @@ else:
   const randMax = 18_446_744_073_709_551_615u64
 
 type
-  RandomGenState = object
+  Rand* = object ## State of the random number generator.
+                 ## The procs that use the default state
+                 ## are **not** thread-safe!
     a0, a1: ui
 
 when defined(JS):
-  var state = RandomGenState(
+  var state = Rand(
     a0: 0x69B4C98Cu32,
     a1: 0xFED1DD30u32) # global for backwards compatibility
 else:
   # racy for multi-threading but good enough for now:
-  var state = RandomGenState(
+  var state = Rand(
     a0: 0x69B4C98CB8530805u64,
     a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
 
 proc rotl(x, k: ui): ui =
   result = (x shl k) or (x shr (ui(64) - k))
 
-proc next(s: var RandomGenState): uint64 =
-  let s0 = s.a0
-  var s1 = s.a1
+proc next*(r: var Rand): uint64 =
+  ## Uses the state to compute a new ``uint64`` random number.
+  let s0 = r.a0
+  var s1 = r.a1
   result = s0 + s1
   s1 = s1 xor s0
-  s.a0 = rotl(s0, 55) xor s1 xor (s1 shl 14) # a, b
-  s.a1 = rotl(s1, 36) # c
+  r.a0 = rotl(s0, 55) xor s1 xor (s1 shl 14) # a, b
+  r.a1 = rotl(s1, 36) # c
 
-proc skipRandomNumbers(s: var RandomGenState) =
+proc skipRandomNumbers*(s: var Rand) =
   ## This is the jump function for the generator. It is equivalent
   ## to 2^64 calls to next(); it can be used to generate 2^64
   ## non-overlapping subsequences for parallel computations.
@@ -71,21 +74,23 @@ proc skipRandomNumbers(s: var RandomGenState) =
   s.a0 = s0
   s.a1 = s1
 
-proc random*(max: int): int {.benign.} =
+proc random*(max: int): int {.benign, deprecated.} =
   ## Returns a random number in the range 0..max-1. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+  ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
+  ## Use ``rand`` instead.
   while true:
     let x = next(state)
     if x < randMax - (randMax mod ui(max)):
       return int(x mod uint64(max))
 
-proc random*(max: float): float {.benign.} =
+proc random*(max: float): float {.benign, deprecated.} =
   ## Returns a random number in the range 0..<max. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+  ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
+  ## Use ``rand`` instead.
   let x = next(state)
   when defined(JS):
     result = (float(x) / float(high(uint32))) * max
@@ -93,38 +98,100 @@ proc random*(max: float): float {.benign.} =
     let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
     result = (cast[float](u) - 1.0) * max
 
-proc random*[T](x: Slice[T]): T =
+proc random*[T](x: HSlice[T, T]): T {.deprecated.} =
   ## For a slice `a .. b` returns a value in the range `a .. b-1`.
+  ## **Deprecated since version 0.18.0**.
+  ## Use ``rand`` instead.
   result = T(random(x.b - x.a)) + x.a
 
-proc random*[T](a: openArray[T]): T =
+proc random*[T](a: openArray[T]): T {.deprecated.} =
   ## returns a random element from the openarray `a`.
+  ## **Deprecated since version 0.18.0**.
+  ## Use ``rand`` instead.
   result = a[random(a.low..a.len)]
 
+proc rand*(r: var Rand; max: int): int {.benign.} =
+  ## Returns a random number in the range 0..max. The sequence of
+  ## random number is always the same, unless `randomize` is called
+  ## which initializes the random number generator with a "random"
+  ## number, i.e. a tickcount.
+  while true:
+    let x = next(r)
+    if x <= randMax - (randMax mod ui(max)):
+      return int(x mod (uint64(max)+1u64))
+
+proc rand*(max: int): int {.benign.} =
+  ## Returns a random number in the range 0..max. The sequence of
+  ## random number is always the same, unless `randomize` is called
+  ## which initializes the random number generator with a "random"
+  ## number, i.e. a tickcount.
+  rand(state, max)
+
+proc rand*(r: var Rand; max: float): float {.benign.} =
+  ## Returns a random number in the range 0..max. The sequence of
+  ## random number is always the same, unless `randomize` is called
+  ## which initializes the random number generator with a "random"
+  ## number, i.e. a tickcount.
+  let x = next(r)
+  when defined(JS):
+    result = (float(x) / float(high(uint32))) * max
+  else:
+    let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
+    result = (cast[float](u) - 1.0) * max
+
+proc rand*(max: float): float {.benign.} =
+  ## Returns a random number in the range 0..max. The sequence of
+  ## random number is always the same, unless `randomize` is called
+  ## which initializes the random number generator with a "random"
+  ## number, i.e. a tickcount.
+  rand(state, max)
+
+proc rand*[T](r: var Rand; x: HSlice[T, T]): T =
+  ## For a slice `a .. b` returns a value in the range `a .. b`.
+  result = T(rand(r, x.b - x.a)) + x.a
+
+proc rand*[T](x: HSlice[T, T]): T =
+  ## For a slice `a .. b` returns a value in the range `a .. b`.
+  result = rand(state, x)
+
+proc rand*[T](r: var Rand; a: openArray[T]): T =
+  ## returns a random element from the openarray `a`.
+  result = a[rand(r, a.low..a.high)]
+
+proc rand*[T](a: openArray[T]): T =
+  ## returns a random element from the openarray `a`.
+  result = a[rand(a.low..a.high)]
+
+
+proc initRand*(seed: int64): Rand =
+  ## Creates a new ``Rand`` state from ``seed``.
+  result.a0 = ui(seed shr 16)
+  result.a1 = ui(seed and 0xffff)
+  discard next(result)
+
 proc randomize*(seed: int64) {.benign.} =
-  ## Initializes the random number generator with a specific seed.
-  state.a0 = ui(seed shr 16)
-  state.a1 = ui(seed and 0xffff)
-  discard next(state)
+  ## Initializes the default random number generator
+  ## with a specific seed.
+  state = initRand(seed)
 
-proc shuffle*[T](x: var openArray[T]) =
-  ## Will randomly swap the positions of elements in a sequence.
+proc shuffle*[T](r: var Rand; x: var openArray[T]) =
+  ## Swaps the positions of elements in a sequence randomly.
   for i in countdown(x.high, 1):
-    let j = random(i + 1)
+    let j = r.rand(i)
     swap(x[i], x[j])
 
+proc shuffle*[T](x: var openArray[T]) =
+  ## Swaps the positions of elements in a sequence randomly.
+  shuffle(state, x)
+
 when not defined(nimscript):
   import times
 
   proc randomize*() {.benign.} =
     ## Initializes the random number generator with a "random"
     ## number, i.e. a tickcount. Note: Does not work for NimScript.
-    when defined(JS):
-      proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
-      randomize(getMil times.getTime())
-    else:
-      let time = int64(times.epochTime() * 1_000_000_000)
-      randomize(time)
+    let time = int64(times.epochTime() * 1_000_000_000)
+    randomize(time)
 
 {.pop.}
 
@@ -134,12 +201,12 @@ when isMainModule:
 
     var x = 8234
     for i in 0..100_000:
-      x = random(len(occur)) # myrand(x)
+      x = rand(high(occur))
       inc occur[x]
     for i, oc in occur:
       if oc < 69:
         doAssert false, "too few occurrences of " & $i
-      elif oc > 130:
+      elif oc > 150:
         doAssert false, "too many occurrences of " & $i
 
     var a = [0, 1]
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index c2ba2b1f3..7907b4e6c 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -39,47 +39,13 @@ proc toRational*[T:SomeInteger](x: T): Rational[T] =
   result.num = x
   result.den = 1
 
-proc toRationalSub(x: float, n: int): Rational[int] =
-  var
-    a = 0'i64
-    b, c, d = 1'i64
-  result = 0 // 1   # rational 0
-  while b <= n and d <= n:
-    let ac = (a+c)
-    let bd = (b+d)
-    # scale by 1000 so not overflow for high precision
-    let mediant = (ac.float/1000) / (bd.float/1000)
-    if x == mediant:
-      if bd <= n:
-        result.num = ac.int
-        result.den = bd.int
-        return result
-      elif d > b:
-        result.num = c.int
-        result.den = d.int
-        return result
-      else:
-        result.num = a.int
-        result.den = b.int
-        return result
-    elif x > mediant:
-      a = ac
-      b = bd
-    else:
-      c = ac
-      d = bd
-  if (b > n):
-    return initRational(c.int, d.int)
-  return initRational(a.int, b.int)
-
-proc toRational*(x: float, n: int = high(int)): Rational[int] =
-  ## Calculate the best rational numerator and denominator
+proc toRational*(x: float, n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] =
+  ## Calculates the best rational numerator and denominator
   ## that approximates to `x`, where the denominator is
   ## smaller than `n` (default is the largest possible
-  ## int to give maximum resolution)
+  ## int to give maximum resolution).
   ##
-  ## The algorithm is based on the Farey sequence named
-  ## after John Farey
+  ## The algorithm is based on the theory of continued fractions.
   ##
   ## .. code-block:: Nim
   ##  import math, rationals
@@ -88,13 +54,24 @@ proc toRational*(x: float, n: int = high(int)): Rational[int] =
   ##    let x = toRational(PI, t)
   ##    let newPI = x.num / x.den
   ##    echo x, " ", newPI, " error: ", PI - newPI, "  ", t
-  if x > 1:
-    result = toRationalSub(1.0/x, n)
-    swap(result.num, result.den)
-  elif x == 1.0:
-    result = 1 // 1
-  else:
-    result = toRationalSub(x, n)
+
+  # David Eppstein / UC Irvine / 8 Aug 1993
+  # With corrections from Arno Formella, May 2008
+  var
+    m11, m22 = 1
+    m12, m21 = 0
+    ai = int(x)
+    x = x
+  while m21 * ai + m22 <= n:
+    swap m12, m11
+    swap m22, m21
+    m11 = m12 * ai + m11
+    m21 = m22 * ai + m21
+    if x == float(ai): break  # division by zero
+    x = 1/(x - float(ai))
+    if x > float(high(int32)): break  # representation failure
+    ai = int(x)
+  result = m11 // m21
 
 proc toFloat*[T](x: Rational[T]): float =
   ## Convert a rational number `x` to a float.
@@ -346,7 +323,19 @@ when isMainModule:
   assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7
   assert toInt(z) == 0
 
-  assert toRational(0.98765432) == 12345679 // 12500000
-  assert toRational(0.1, 1000000) == 1 // 10
-  assert toRational(0.9, 1000000) == 9 // 10
-  #assert toRational(PI) == 80143857 // 25510582
+  when sizeof(int) == 8:
+    assert toRational(0.98765432) == 2111111029 // 2137499919
+    assert toRational(PI) == 817696623 // 260280919
+  when sizeof(int) == 4:
+    assert toRational(0.98765432) == 80 // 81
+    assert toRational(PI) == 355 // 113
+
+  assert toRational(0.1) == 1 // 10
+  assert toRational(0.9) == 9 // 10
+
+  assert toRational(0.0) == 0 // 1
+  assert toRational(-0.25) == 1 // -4
+  assert toRational(3.2) == 16 // 5
+  assert toRational(0.33) == 33 // 100
+  assert toRational(0.22) == 11 // 50
+  assert toRational(10.0) == 10 // 1
diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim
deleted file mode 100644
index aa047d1cc..000000000
--- a/lib/pure/romans.nim
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2011 Philippe Lhoste
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Module for converting an integer to a Roman numeral.
-## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
-##
-## **Warning:** This module will be moved out of the stdlib and into a
-## Nimble package, don't use it.
-
-const
-  RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
-    'D', 'd', 'M', 'm'} ## set of all characters a Roman numeral may consist of
-
-proc romanToDecimal*(romanVal: string): int =
-  ## Converts a Roman numeral to its int representation.
-  result = 0
-  var prevVal = 0
-  for i in countdown(romanVal.len - 1, 0):
-    var val = 0
-    case romanVal[i]
-    of 'I', 'i': val = 1
-    of 'V', 'v': val = 5
-    of 'X', 'x': val = 10
-    of 'L', 'l': val = 50
-    of 'C', 'c': val = 100
-    of 'D', 'd': val = 500
-    of 'M', 'm': val = 1000
-    else:
-      raise newException(EInvalidValue, "invalid roman numeral: " & $romanVal)
-    if val >= prevVal:
-      inc(result, val)
-    else:
-      dec(result, val)
-    prevVal = val
-
-proc decimalToRoman*(number: range[1..3_999]): string =
-  ## Converts a number to a Roman numeral.
-  const romanComposites = [
-    ("M", 1000), ("CM", 900),
-    ("D", 500), ("CD", 400), ("C", 100),
-    ("XC", 90), ("L", 50), ("XL", 40), ("X", 10), ("IX", 9),
-    ("V", 5), ("IV", 4), ("I", 1)]
-  result = ""
-  var decVal: int = number
-  for key, val in items(romanComposites):
-    while decVal >= val:
-      dec(decVal, val)
-      result.add(key)
-
-when isMainModule:
-  for i in 1 .. 3_999:
-    assert i == i.decimalToRoman.romanToDecimal
-
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index 6e97237e0..6ddd61afa 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -17,6 +17,7 @@
 ## runtime efficiency.
 
 include "system/inclrtl"
+import streams
 
 {.deadCodeElim: on.}
 
@@ -130,7 +131,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
       result.left = t
       t.right = nil
 
-proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} =
+proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
   ## Converts a string to a rope.
   if s.len == 0:
     result = nil
@@ -242,10 +243,13 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} =
   ## writes a rope to a file.
   for s in leaves(r): write(f, s)
 
+proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} =
+  ## writes a rope to a stream.
+  for rs in leaves(r): write(s, rs)
+
 proc `$`*(r: Rope): string  {.rtl, extern: "nroToString".}=
   ## converts a rope back to a string.
-  result = newString(r.len)
-  setLen(result, 0)
+  result = newStringOfCap(r.len)
   for s in leaves(r): add(result, s)
 
 when false:
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
index 711e4a897..1ff26954e 100644
--- a/lib/pure/scgi.nim
+++ b/lib/pure/scgi.nim
@@ -95,7 +95,7 @@ type
   AsyncScgiState* = ref AsyncScgiStateObj
 
 {.deprecated: [EScgi: ScgiError, TScgiState: ScgiState,
-   PAsyncScgiState: AsyncScgiState, scgiError: raiseScgiError].}
+   PAsyncScgiState: AsyncScgiState].}
 
 proc recvBuffer(s: var ScgiState, L: int) =
   if L > s.bufLen:
diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim
index c19146669..57c1f3631 100644
--- a/lib/pure/securehash.nim
+++ b/lib/pure/securehash.nim
@@ -181,7 +181,7 @@ proc `$`*(self: SecureHash): string =
     result.add(toHex(int(v), 2))
 
 proc parseSecureHash*(hash: string): SecureHash =
-  for i in 0.. <Sha1DigestSize:
+  for i in 0 ..< Sha1DigestSize:
     Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
 
 proc `==`*(a, b: SecureHash): bool =
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 506b2cec0..518cc4bd5 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -1,420 +1,313 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2015 Dominik Picheta
+#        (c) Copyright 2016 Eugene Kabanov
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-# TODO: Docs.
-
-import os, hashes
-
-when defined(linux):
-  import posix, epoll
-elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
-  import posix, kqueue, times
-elif defined(windows):
-  import winlean
-else:
-  import posix
-
-const MultiThreaded = defined(useStdlibThreading)
-
-when MultiThreaded:
-  import sharedtables
-
-  type SelectorData = pointer
-else:
-  import tables
-
-  type SelectorData = RootRef
-
-proc hash*(x: SocketHandle): Hash {.borrow.}
-proc `$`*(x: SocketHandle): string {.borrow.}
-
-type
-  Event* = enum
-    EvRead, EvWrite, EvError
-
-  SelectorKey* = object
-    fd*: SocketHandle
-    events*: set[Event] ## The events which ``fd`` listens for.
-    data*: SelectorData ## User object.
-
-  ReadyInfo* = tuple[key: SelectorKey, events: set[Event]]
+## This module allows high-level and efficient I/O multiplexing.
+##
+## Supported OS primitives: ``epoll``, ``kqueue``, ``poll`` and
+## Windows ``select``.
+##
+## To use threadsafe version of this module, it needs to be compiled
+## with both ``-d:threadsafe`` and ``--threads:on`` options.
+##
+## Supported features: files, sockets, pipes, timers, processes, signals
+## and user events.
+##
+## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except
+## for Android).
+##
+## Partially supported OS: Windows (only sockets and user events),
+## Solaris (files, sockets, handles and user events).
+## Android (files, sockets, handles and user events).
+##
+## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
+
+import os, strutils, nativesockets
+
+const hasThreadSupport = compileOption("threads") and defined(threadsafe)
+
+const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
+                                defined(netbsd) or defined(openbsd) or
+                                defined(dragonfly) or
+                                (defined(linux) and not defined(android))
+  ## This constant is used to determine whether the destination platform is
+  ## fully supported by ``ioselectors`` module.
+
+const bsdPlatform = defined(macosx) or defined(freebsd) or
+                    defined(netbsd) or defined(openbsd) or
+                    defined(dragonfly)
 
 when defined(nimdoc):
   type
-    Selector* = ref object
-      ## An object which holds file descriptors to be checked for read/write
-      ## status.
-
-  proc register*(s: Selector, fd: SocketHandle, events: set[Event],
-                 data: SelectorData): SelectorKey {.discardable.} =
-    ## Registers file descriptor ``fd`` to selector ``s`` with a set of Event
-    ## ``events``.
-
-  proc update*(s: Selector, fd: SocketHandle,
-               events: set[Event]): SelectorKey {.discardable.} =
-    ## Updates the events which ``fd`` wants notifications for.
-
-  proc unregister*(s: Selector, fd: SocketHandle): SelectorKey {.discardable.} =
-    ## Unregisters file descriptor ``fd`` from selector ``s``.
-
-  proc close*(s: Selector) =
-    ## Closes the selector
-
-  proc select*(s: Selector, timeout: int): seq[ReadyInfo] =
-    ## The ``events`` field of the returned ``key`` contains the original events
-    ## for which the ``fd`` was bound. This is contrary to the ``events`` field
-    ## of the ``ReadyInfo`` tuple which determines which events are ready
-    ## on the ``fd``.
-
-  proc newSelector*(): Selector =
+    Selector*[T] = ref object
+      ## An object which holds descriptors to be checked for read/write status
+
+    Event* {.pure.} = enum
+      ## An enum which hold event types
+      Read,        ## Descriptor is available for read
+      Write,       ## Descriptor is available for write
+      Timer,       ## Timer descriptor is completed
+      Signal,      ## Signal is raised
+      Process,     ## Process is finished
+      Vnode,       ## BSD specific file change happens
+      User,        ## User event is raised
+      Error,       ## Error happens while waiting, for descriptor
+      VnodeWrite,  ## NOTE_WRITE (BSD specific, write to file occurred)
+      VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred)
+      VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended)
+      VnodeAttrib, ## NOTE_ATTRIB (BSD specific, file attributes changed)
+      VnodeLink,   ## NOTE_LINK (BSD specific, file link count changed)
+      VnodeRename, ## NOTE_RENAME (BSD specific, file renamed)
+      VnodeRevoke  ## NOTE_REVOKE (BSD specific, file revoke occurred)
+
+    ReadyKey* = object
+      ## An object which holds result for descriptor
+      fd* : int ## file/socket descriptor
+      events*: set[Event] ## set of events
+
+    SelectEvent* = object
+      ## An object which holds user defined event
+
+  proc newSelector*[T](): Selector[T] =
     ## Creates a new selector
 
-  proc contains*(s: Selector, fd: SocketHandle): bool =
+  proc close*[T](s: Selector[T]) =
+    ## Closes the selector.
+
+  proc registerHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event],
+                          data: T) =
+    ## Registers file/socket descriptor ``fd`` to selector ``s``
+    ## with events set in ``events``. The ``data`` is application-defined
+    ## data, which will be passed when an event is triggered.
+
+  proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) =
+    ## Update file/socket descriptor ``fd``, registered in selector
+    ## ``s`` with new events set ``event``.
+
+  proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
+                         data: T): int {.discardable.} =
+    ## Registers timer notification with ``timeout`` (in milliseconds)
+    ## to selector ``s``.
+    ##
+    ## If ``oneshot`` is ``true``, timer will be notified only once.
+    ##
+    ## Set ``oneshot`` to ``false`` if you want periodic notifications.
+    ##
+    ## The ``data`` is application-defined data, which will be passed, when
+    ## the timer is triggered.
+    ##
+    ## Returns the file descriptor for the registered timer.
+
+  proc registerSignal*[T](s: Selector[T], signal: int,
+                          data: T): int {.discardable.} =
+    ## Registers Unix signal notification with ``signal`` to selector
+    ## ``s``.
+    ##
+    ## The ``data`` is application-defined data, which will be
+    ## passed when signal raises.
+    ##
+    ## Returns the file descriptor for the registered signal.
+    ##
+    ## **Note:** This function is not supported on ``Windows``.
+
+  proc registerProcess*[T](s: Selector[T], pid: int,
+                           data: T): int {.discardable.} =
+    ## Registers a process id (pid) notification (when process has
+    ## exited) in selector ``s``.
+    ##
+    ## The ``data`` is application-defined data, which will be passed when
+    ## process with ``pid`` has exited.
+    ##
+    ## Returns the file descriptor for the registered signal.
+
+  proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
+    ## Registers selector event ``ev`` in selector ``s``.
+    ##
+    ## The ``data`` is application-defined data, which will be passed when
+    ## ``ev`` happens.
+
+  proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event],
+                         data: T) =
+    ## Registers selector BSD/MacOSX specific vnode events for file
+    ## descriptor ``fd`` and events ``events``.
+    ## ``data`` application-defined data, which to be passed, when
+    ## vnode event happens.
+    ##
+    ## **Note:** This function is supported only by BSD and MacOSX.
+
+  proc newSelectEvent*(): SelectEvent =
+    ## Creates a new user-defined event.
+
+  proc trigger*(ev: SelectEvent) =
+    ## Trigger event ``ev``.
+
+  proc close*(ev: SelectEvent) =
+    ## Closes user-defined event ``ev``.
+
+  proc unregister*[T](s: Selector[T], ev: SelectEvent) =
+    ## Unregisters user-defined event ``ev`` from selector ``s``.
+
+  proc unregister*[T](s: Selector[T], fd: int|SocketHandle|cint) =
+    ## Unregisters file/socket descriptor ``fd`` from selector ``s``.
+
+  proc selectInto*[T](s: Selector[T], timeout: int,
+                      results: var openarray[ReadyKey]): int =
+    ## Waits for events registered in selector ``s``.
+    ##
+    ## The ``timeout`` argument specifies the maximum number of milliseconds
+    ## the function will be blocked for if no events are ready. Specifying a
+    ## timeout of ``-1`` causes the function to block indefinitely.
+    ## All available events will be stored in ``results`` array.
+    ##
+    ## Returns number of triggered events.
+
+  proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
+    ## Waits for events registered in selector ``s``.
+    ##
+    ## The ``timeout`` argument specifies the maximum number of milliseconds
+    ## the function will be blocked for if no events are ready. Specifying a
+    ## timeout of ``-1`` causes the function to block indefinitely.
+    ##
+    ## Returns a list of triggered events.
+
+  proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
+    ## Retrieves application-defined ``data`` associated with descriptor ``fd``.
+    ## If specified descriptor ``fd`` is not registered, empty/default value
+    ## will be returned.
+
+  proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool =
+    ## Associate application-defined ``data`` with descriptor ``fd``.
+    ##
+    ## Returns ``true``, if data was succesfully updated, ``false`` otherwise.
+
+  template isEmpty*[T](s: Selector[T]): bool = # TODO: Why is this a template?
+    ## Returns ``true``, if there are no registered events or descriptors
+    ## in selector.
+
+  template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
+                        body: untyped) =
+    ## Retrieves the application-data assigned with descriptor ``fd``
+    ## to ``value``. This ``value`` can be modified in the scope of
+    ## the ``withData`` call.
+    ##
+    ## .. code-block:: nim
+    ##
+    ##   s.withData(fd, value) do:
+    ##     # block is executed only if ``fd`` registered in selector ``s``
+    ##     value.uid = 1000
+    ##
+
+  template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
+                        body1, body2: untyped) =
+    ## Retrieves the application-data assigned with descriptor ``fd``
+    ## to ``value``. This ``value`` can be modified in the scope of
+    ## the ``withData`` call.
+    ##
+    ## .. code-block:: nim
+    ##
+    ##   s.withData(fd, value) do:
+    ##     # block is executed only if ``fd`` registered in selector ``s``.
+    ##     value.uid = 1000
+    ##   do:
+    ##     # block is executed if ``fd`` not registered in selector ``s``.
+    ##     raise
+    ##
+
+  proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
     ## Determines whether selector contains a file descriptor.
 
-  proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
-    ## Retrieves the selector key for ``fd``.
-
-elif defined(linux):
-  type
-    Selector* = object
-      epollFD: cint
-      events: array[64, epoll_event]
-      when MultiThreaded:
-        fds: SharedTable[SocketHandle, SelectorKey]
-      else:
-        fds: Table[SocketHandle, SelectorKey]
-
-  proc createEventStruct(events: set[Event], fd: SocketHandle): epoll_event =
-    if EvRead in events:
-      result.events = EPOLLIN
-    if EvWrite in events:
-      result.events = result.events or EPOLLOUT
-    result.events = result.events or EPOLLRDHUP
-    result.data.fd = fd.cint
-
-  proc register*(s: var Selector, fd: SocketHandle, events: set[Event],
-                 data: SelectorData) =
-    var event = createEventStruct(events, fd)
-    if events != {}:
-      if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
-        raiseOSError(osLastError())
-
-    s.fds[fd] = SelectorKey(fd: fd, events: events, data: data)
-
-  proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) =
-    if s.fds[fd].events != events:
-      if events == {}:
-        # This fd is idle -- it should not be registered to epoll.
-        # But it should remain a part of this selector instance.
-        # This is to prevent epoll_wait from returning immediately
-        # because its got fds which are waiting for no events and
-        # are therefore constantly ready. (leading to 100% CPU usage).
-        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
-          raiseOSError(osLastError())
-        s.fds[fd].events = events
-      else:
-        var event = createEventStruct(events, fd)
-        if s.fds[fd].events == {}:
-          # This fd is idle. It's not a member of this epoll instance and must
-          # be re-registered.
-          if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
-            raiseOSError(osLastError())
-        else:
-          if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
-            raiseOSError(osLastError())
-        s.fds[fd].events = events
-
-  proc unregister*(s: var Selector, fd: SocketHandle) =
-    if s.fds[fd].events != {}:
-      if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
-        let err = osLastError()
-        if err.cint notin {ENOENT, EBADF}:
-          # TODO: Why do we sometimes get an EBADF? Is this normal?
-          raiseOSError(err)
-    s.fds.del(fd)
-
-  proc close*(s: var Selector) =
-    when MultiThreaded: deinitSharedTable(s.fds)
-    if s.epollFD.close() != 0: raiseOSError(osLastError())
-
-  proc epollHasFd(s: Selector, fd: SocketHandle): bool =
-    result = true
-    var event = createEventStruct(s.fds[fd].events, fd)
-    if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
-      let err = osLastError()
-      if err.cint in {ENOENT, EBADF}:
-        return false
-      raiseOSError(err)
-
-  proc select*(s: var Selector, timeout: int): seq[ReadyInfo] =
-    result = @[]
-    let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
-    if evNum < 0:
-      let err = osLastError()
-      if err.cint == EINTR:
-        return @[]
-      raiseOSError(err)
-    if evNum == 0: return @[]
-    for i in 0 .. <evNum:
-      let fd = s.events[i].data.fd.SocketHandle
-
-      var evSet: set[Event] = {}
-      if (s.events[i].events and EPOLLERR) != 0 or (s.events[i].events and EPOLLHUP) != 0: evSet = evSet + {EvError}
-      if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
-      if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
-      let selectorKey = s.fds[fd]
-      assert selectorKey.fd != 0.SocketHandle
-      result.add((selectorKey, evSet))
-
-      #echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
-
-  proc newSelector*(): Selector =
-    result.epollFD = epoll_create(64)
-    if result.epollFD < 0:
-      raiseOSError(osLastError())
-    when MultiThreaded:
-      result.fds = initSharedTable[SocketHandle, SelectorKey]()
-    else:
-      result.fds = initTable[SocketHandle, SelectorKey]()
-
-  proc contains*(s: Selector, fd: SocketHandle): bool =
-    ## Determines whether selector contains a file descriptor.
-    if s.fds.hasKey(fd):
-      # Ensure the underlying epoll instance still contains this fd.
-      if s.fds[fd].events != {}:
-        result = epollHasFd(s, fd)
-      else:
-        result = true
-    else:
-      return false
+else:
+  when hasThreadSupport:
+    import locks
 
-  proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
-    ## Retrieves the selector key for ``fd``.
-    return s.fds[fd]
 
-elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
-  type
-    Selector* = object
-      kqFD: cint
-      events: array[64, KEvent]
-      when MultiThreaded:
-        fds: SharedTable[SocketHandle, SelectorKey]
-      else:
-        fds: Table[SocketHandle, SelectorKey]
-
-  template modifyKQueue(kqFD: cint, fd: SocketHandle, event: Event,
-                        op: cushort) =
-    var kev = KEvent(ident:  fd.cuint,
-                     filter: if event == EvRead: EVFILT_READ else: EVFILT_WRITE,
-                     flags:  op)
-    if kevent(kqFD, addr kev, 1, nil, 0, nil) == -1:
-      raiseOSError(osLastError())
-
-  proc register*(s: var Selector, fd: SocketHandle, events: set[Event],
-                 data: SelectorData) =
-    for event in events:
-      modifyKQueue(s.kqFD, fd, event, EV_ADD)
-    s.fds[fd] = SelectorKey(fd: fd, events: events, data: data)
-
-  proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) =
-    let previousEvents = s.fds[fd].events
-    if previousEvents != events:
-      for event in events-previousEvents:
-        modifyKQueue(s.kqFD, fd, event, EV_ADD)
-      for event in previousEvents-events:
-        modifyKQueue(s.kqFD, fd, event, EV_DELETE)
-      s.fds[fd].events = events
-
-  proc unregister*(s: var Selector, fd: SocketHandle) =
-    for event in s.fds[fd].events:
-      modifyKQueue(s.kqFD, fd, event, EV_DELETE)
-    s.fds.del(fd)
-
-  proc close*(s: var Selector) =
-    when MultiThreaded: deinitSharedTable(s.fds)
-    if s.kqFD.close() != 0: raiseOSError(osLastError())
-
-  proc select*(s: var Selector, timeout: int): seq[ReadyInfo] =
-    result = @[]
-    var tv =
-      if timeout >= 1000: Timespec(tv_sec: (timeout div 1000).Time, tv_nsec: 0)
-      else: Timespec(tv_sec: 0.Time, tv_nsec: timeout * 1000000)
-    let evNum = kevent(s.kqFD, nil, 0, addr s.events[0], 64.cint, addr tv)
-    if evNum < 0:
-      let err = osLastError()
-      if err.cint == EINTR:
-        return @[]
-      raiseOSError(err)
-    if evNum == 0: return @[]
-    for i in 0 .. <evNum:
-      let fd = s.events[i].ident.SocketHandle
-
-      var evSet: set[Event] = {}
-      if  (s.events[i].flags and EV_EOF) != 0: evSet = evSet + {EvError}
-      if   s.events[i].filter == EVFILT_READ:  evSet = evSet + {EvRead}
-      elif s.events[i].filter == EVFILT_WRITE: evSet = evSet + {EvWrite}
-      let selectorKey = s.fds[fd]
-      assert selectorKey.fd != 0.SocketHandle
-      result.add((selectorKey, evSet))
-
-  proc newSelector*(): Selector =
-    result.kqFD = kqueue()
-    if result.kqFD < 0:
-      raiseOSError(osLastError())
-    when MultiThreaded:
-      result.fds = initSharedTable[SocketHandle, SelectorKey]()
-    else:
-      result.fds = initTable[SocketHandle, SelectorKey]()
+    type
+      SharedArray[T] = UncheckedArray[T]
 
-  proc contains*(s: Selector, fd: SocketHandle): bool =
-    ## Determines whether selector contains a file descriptor.
-    s.fds.hasKey(fd) # and s.fds[fd].events != {}
+    proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
+      result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
 
-  proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
-    ## Retrieves the selector key for ``fd``.
-    return s.fds[fd]
+    proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
+      deallocShared(cast[pointer](sa))
 
-elif not defined(nimdoc):
-  # TODO: kqueue for bsd/mac os x.
   type
-    Selector* = object
-      when MultiThreaded:
-        fds: SharedTable[SocketHandle, SelectorKey]
-      else:
-        fds: Table[SocketHandle, SelectorKey]
-
-  proc register*(s: var Selector, fd: SocketHandle, events: set[Event],
-                 data: SelectorData) =
-    let result = SelectorKey(fd: fd, events: events, data: data)
-    if s.fds.hasKeyOrPut(fd, result):
-      raise newException(ValueError, "File descriptor already exists.")
-
-  proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) =
-    #if not s.fds.hasKey(fd):
-    #  raise newException(ValueError, "File descriptor not found.")
-    s.fds[fd].events = events
-
-  proc unregister*(s: var Selector, fd: SocketHandle) =
-    s.fds.del(fd)
-
-  proc close*(s: var Selector) =
-    when MultiThreaded: deinitSharedTable(s.fds)
-
-  proc timeValFromMilliseconds(timeout: int): TimeVal =
-    if timeout != -1:
-      var seconds = timeout div 1000
-      result.tv_sec = seconds.int32
-      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
-
-  proc createFdSet(rd, wr: var TFdSet, s: Selector, m: var int) =
-    FD_ZERO(rd); FD_ZERO(wr)
-    for k, v in pairs(s.fds):
-      if EvRead in v.events:
-        m = max(m, int(k))
-        FD_SET(k, rd)
-      if EvWrite in v.events:
-        m = max(m, int(k))
-        FD_SET(k, wr)
-
-  proc getReadyFDs(rd, wr: var TFdSet,
-                   s: var Selector): seq[ReadyInfo] =
-    result = @[]
-    for k, v in pairs(s.fds):
-      var events: set[Event] = {}
-      if FD_ISSET(k, rd) != 0'i32:
-        events = events + {EvRead}
-      if FD_ISSET(k, wr) != 0'i32:
-        events = events + {EvWrite}
-      result.add((v, events))
-
-  proc select*(s: var Selector, timeout: int): seq[ReadyInfo] =
-    var tv {.noInit.}: TimeVal = timeValFromMilliseconds(timeout)
-
-    var rd, wr: TFdSet
-    var m = 0
-    createFdSet(rd, wr, s, m)
-
-    var retCode = 0
-    if timeout != -1:
-      retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
-    else:
-      retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
-
-    if retCode < 0:
-      raiseOSError(osLastError())
-    elif retCode == 0:
-      return @[]
-    else:
-      return getReadyFDs(rd, wr, s)
+    Event* {.pure.} = enum
+      Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot,
+      Finished, VnodeWrite, VnodeDelete, VnodeExtend, VnodeAttrib, VnodeLink,
+      VnodeRename, VnodeRevoke
 
-  proc newSelector*(): Selector =
-    when MultiThreaded:
-      result.fds = initSharedTable[SocketHandle, SelectorKey]()
+  type
+    IOSelectorsException* = object of Exception
+
+    ReadyKey* = object
+      fd* : int
+      events*: set[Event]
+
+    SelectorKey[T] = object
+      ident: int
+      events: set[Event]
+      param: int
+      data: T
+
+  proc raiseIOSelectorsError[T](message: T) =
+    var msg = ""
+    when T is string:
+      msg.add(message)
+    elif T is OSErrorCode:
+      msg.add(osErrorMsg(message) & " (code: " & $int(message) & ")")
     else:
-      result.fds = initTable[SocketHandle, SelectorKey]()
-
-  proc contains*(s: Selector, fd: SocketHandle): bool =
-    return s.fds.hasKey(fd)
-
-  proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey =
-    return s.fds[fd]
-
-proc contains*(s: Selector, key: SelectorKey): bool =
-  ## Determines whether selector contains this selector key. More accurate
-  ## than checking if the file descriptor is in the selector because it
-  ## ensures that the keys are equal. File descriptors may not always be
-  ## unique especially when an fd is closed and then a new one is opened,
-  ## the new one may have the same value.
-  when not defined(nimdoc):
-    return key.fd in s and s.fds[key.fd] == key
-
-proc len*(s: Selector): int =
-  ## Retrieves the number of registered file descriptors in this Selector.
-  when not defined(nimdoc):
-    return s.fds.len
-
-{.deprecated: [TEvent: Event, PSelectorKey: SelectorKey,
-   TReadyInfo: ReadyInfo, PSelector: Selector].}
-
-
-when not defined(testing) and isMainModule and not defined(nimdoc):
-  # Select()
-  import sockets
+      msg.add("Internal Error\n")
+    var err = newException(IOSelectorsException, msg)
+    raise err
+
+  proc setNonBlocking(fd: cint) {.inline.} =
+    setBlocking(fd.SocketHandle, false)
+
+  when not defined(windows):
+    import posix
+
+    template setKey(s, pident, pevents, pparam, pdata: untyped) =
+      var skey = addr(s.fds[pident])
+      skey.ident = pident
+      skey.events = pevents
+      skey.param = pparam
+      skey.data = data
+
+  when ioselSupportedPlatform:
+    template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
+      when hasThreadSupport:
+        if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
+          raiseIOSelectorsError(osLastError())
+      else:
+        if posix.sigprocmask(SIG_BLOCK, newmask, oldmask) == -1:
+          raiseIOSelectorsError(osLastError())
 
-  when MultiThreaded:
-    type
-      SockWrapper = object
-        sock: Socket
-  else:
-    type
-      SockWrapper = ref object of RootObj
-        sock: Socket
-
-  var sock = socket()
-  if sock == sockets.invalidSocket: raiseOSError(osLastError())
-  #sock.setBlocking(false)
-  sock.connect("irc.freenode.net", Port(6667))
-
-  var selector = newSelector()
-  var data = SockWrapper(sock: sock)
-  when MultiThreaded:
-    selector.register(sock.getFD, {EvWrite}, addr data)
+    template unblockSignals(newmask: var Sigset, oldmask: var Sigset) =
+      when hasThreadSupport:
+        if posix.pthread_sigmask(SIG_UNBLOCK, newmask, oldmask) == -1:
+          raiseIOSelectorsError(osLastError())
+      else:
+        if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
+          raiseIOSelectorsError(osLastError())
+
+  when defined(linux):
+    include ioselects/ioselectors_epoll
+  elif bsdPlatform:
+    include ioselects/ioselectors_kqueue
+  elif defined(windows):
+    include ioselects/ioselectors_select
+  elif defined(solaris):
+    include ioselects/ioselectors_poll # need to replace it with event ports
+  elif defined(genode):
+    include ioselects/ioselectors_select # TODO: use the native VFS layer
   else:
-    selector.register(sock.getFD, {EvWrite}, data)
-  var i = 0
-  while true:
-    let ready = selector.select(1000)
-    echo ready.len
-    if ready.len > 0: echo ready[0].events
-    i.inc
-    if i == 6:
-      selector.unregister(sock.getFD)
-      selector.close()
-      break
+    include ioselects/ioselectors_poll
+
+{.deprecated: [setEvent: trigger].}
+{.deprecated: [register: registerHandle].}
+{.deprecated: [update: updateHandle].}
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 69f673990..354e07da3 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -224,6 +224,38 @@ proc peekInt64*(s: Stream): int64 =
   ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
   peek(s, result)
 
+proc readUint8*(s: Stream): uint8 =
+  ## reads an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint8*(s: Stream): uint8 =
+  ## peeks an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint16*(s: Stream): uint16 =
+  ## reads an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint16*(s: Stream): uint16 =
+  ## peeks an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint32*(s: Stream): uint32 =
+  ## reads an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint32*(s: Stream): uint32 =
+  ## peeks an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
+proc readUint64*(s: Stream): uint64 =
+  ## reads an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  read(s, result)
+
+proc peekUint64*(s: Stream): uint64 =
+  ## peeks an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  peek(s, result)
+
 proc readFloat32*(s: Stream): float32 =
   ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
   read(s, result)
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
new file mode 100644
index 000000000..180cbcbec
--- /dev/null
+++ b/lib/pure/strformat.nim
@@ -0,0 +1,619 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+##[
+String `interpolation`:idx: / `format`:idx: inspired by
+Python's ``f``-strings.
+
+Examples:
+
+.. code-block:: nim
+
+    doAssert fmt"""{"abc":>4}""" == " abc"
+    doAssert fmt"""{"abc":<4}""" == "abc "
+
+    doAssert fmt"{-12345:08}" == "-0012345"
+    doAssert fmt"{-1:3}" == " -1"
+    doAssert fmt"{-1:03}" == "-01"
+    doAssert fmt"{16:#X}" == "0x10"
+
+    doAssert fmt"{123.456}" == "123.456"
+    doAssert fmt"{123.456:>9.3f}" == "  123.456"
+    doAssert fmt"{123.456:9.3f}" == "  123.456"
+    doAssert fmt"{123.456:9.4f}" == " 123.4560"
+    doAssert fmt"{123.456:>9.0f}" == "     123."
+    doAssert fmt"{123.456:<9.4f}" == "123.4560 "
+
+    doAssert fmt"{123.456:e}" == "1.234560e+02"
+    doAssert fmt"{123.456:>13e}" == " 1.234560e+02"
+    doAssert fmt"{123.456:13e}" == " 1.234560e+02"
+
+
+An expression like ``fmt"{key} is {value:arg} {{z}}"`` is transformed into:
+
+.. code-block:: nim
+  var temp = newStringOfCap(educatedCapGuess)
+  format(key, temp)
+  format(" is ", temp)
+  format(value, arg, temp)
+  format(" {z}", temp)
+  temp
+
+Parts of the string that are enclosed in the curly braces are interpreted
+as Nim code, to escape an ``{`` or ``}`` double it.
+
+``fmt`` delegates most of the work to an open overloaded set
+of ``format`` procs. The required signature for a type ``T`` that supports
+formatting is usually ``proc format(x: T; result: var string)`` for efficiency
+but can also be ``proc format(x: T): string``. ``add`` and ``$`` procs are
+used as the fallback implementation.
+
+This is the concrete lookup algorithm that ``fmt`` uses:
+
+.. code-block:: nim
+
+  when compiles(format(arg, res)):
+    format(arg, res)
+  elif compiles(format(arg)):
+    res.add format(arg)
+  elif compiles(add(res, arg)):
+    res.add(arg)
+  else:
+    res.add($arg)
+
+
+The subexpression after the colon
+(``arg`` in ``fmt"{key} is {value:arg} {{z}}"``) is an optional argument
+passed to ``format``.
+
+If an optional argument is present the following lookup algorithm is used:
+
+.. code-block:: nim
+
+  when compiles(format(arg, option, res)):
+    format(arg, option, res)
+  else:
+    res.add format(arg, option)
+
+
+For strings and numeric types the optional argument is a so-called
+"standard format specifier".
+
+
+Standard format specifier
+=========================
+
+
+The general form of a standard format specifier is::
+
+  [[fill]align][sign][#][0][minimumwidth][.precision][type]
+
+The square brackets ``[]`` indicate an optional element.
+
+The optional align flag can be one of the following:
+
+'<'
+    Forces the field to be left-aligned within the available
+    space. (This is the default for strings.)
+
+'>'
+    Forces the field to be right-aligned within the available space.
+    (This is the default for numbers.)
+
+'^'
+    Forces the field to be centered within the available space.
+
+Note that unless a minimum field width is defined, the field width
+will always be the same size as the data to fill it, so that the alignment
+option has no meaning in this case.
+
+The optional 'fill' character defines the character to be used to pad
+the field to the minimum width. The fill character, if present, must be
+followed by an alignment flag.
+
+The 'sign' option is only valid for numeric types, and can be one of the following:
+
+=================        ====================================================
+  Sign                   Meaning
+=================        ====================================================
+``+``                    Indicates that a sign should be used for both
+                         positive as well as negative numbers.
+``-``                    Indicates that a sign should be used only for
+                         negative numbers (this is the default behavior).
+(space)                  Indicates that a leading space should be used on
+                         positive numbers.
+=================        ====================================================
+
+If the '#' character is present, integers use the 'alternate form' for formatting.
+This means that binary, octal, and hexadecimal output will be prefixed
+with '0b', '0o', and '0x', respectively.
+
+'width' is a decimal integer defining the minimum field width. If not specified,
+then the field width will be determined by the content.
+
+If the width field is preceded by a zero ('0') character, this enables
+zero-padding.
+
+The 'precision' is a decimal number indicating how many digits should be displayed
+after the decimal point in a floating point conversion. For non-numeric types the
+field indicates the maximum field size - in other words, how many characters will
+be used from the field content. The precision is ignored for integer conversions.
+
+Finally, the 'type' determines how the data should be presented.
+
+The available integer presentation types are:
+
+
+=================        ====================================================
+  Type                   Result
+=================        ====================================================
+``b``                    Binary. Outputs the number in base 2.
+``d``                    Decimal Integer. Outputs the number in base 10.
+``o``                    Octal format. Outputs the number in base 8.
+``x``                    Hex format. Outputs the number in base 16, using
+                         lower-case letters for the digits above 9.
+``X``                    Hex format. Outputs the number in base 16, using
+                         uppercase letters for the digits above 9.
+(None)                   the same as 'd'
+=================        ====================================================
+
+
+The available floating point presentation types are:
+
+=================        ====================================================
+  Type                   Result
+=================        ====================================================
+``e``                    Exponent notation. Prints the number in scientific
+                         notation using the letter 'e' to indicate the
+                         exponent.
+``E``                    Exponent notation. Same as 'e' except it converts
+                         the number to uppercase.
+``f``                    Fixed point. Displays the number as a fixed-point
+                         number.
+``F``                    Fixed point. Same as 'f' except it converts the
+                         number to uppercase.
+``g``                    General format. This prints the number as a
+                         fixed-point number, unless the number is too
+                         large, in which case it switches to 'e'
+                         exponent notation.
+``G``                    General format. Same as 'g' except switches to 'E'
+                         if the number gets to large.
+(None)                   similar to 'g', except that it prints at least one
+                         digit after the decimal point.
+=================        ====================================================
+
+
+Future directions
+=================
+
+A curly expression with commas in it like ``{x, argA, argB}`` could be
+transformed to ``format(x, argA, argB, res)`` in order to support
+formatters that do not need to parse a custom language within a custom
+language but instead prefer to use Nim's existing syntax. This also
+helps in readability since there is only so much you can cram into
+single letter DSLs.
+
+]##
+
+import macros, parseutils, unicode
+import strutils
+
+template callFormat(res, arg) {.dirty.} =
+  when arg is string:
+    # workaround in order to circumvent 'strutils.format' which matches
+    # too but doesn't adhere to our protocol.
+    res.add arg
+  elif compiles(format(arg, res)):
+    format(arg, res)
+  elif compiles(format(arg)):
+    res.add format(arg)
+  elif compiles(add(res, arg)):
+    res.add(arg)
+  else:
+    res.add($arg)
+
+template callFormatOption(res, arg, option) {.dirty.} =
+  when compiles(format(arg, option, res)):
+    format(arg, option, res)
+  else:
+    res.add format(arg, option)
+
+macro fmt*(pattern: string): untyped =
+  ## For a specification of the ``fmt`` macro, see the module level documentation.
+  runnableExamples:
+    template check(actual, expected: string) =
+      doAssert actual == expected
+
+    from strutils import toUpperAscii, repeat
+
+    # Basic tests
+    let s = "string"
+    check fmt"{0} {s}", "0 string"
+    check fmt"{s[0..2].toUpperAscii}", "STR"
+    check fmt"{-10:04}", "-010"
+    check fmt"{-10:<04}", "-010"
+    check fmt"{-10:>04}", "-010"
+    check fmt"0x{10:02X}", "0x0A"
+
+    check fmt"{10:#04X}", "0x0A"
+
+    check fmt"""{"test":#>5}""", "#test"
+    check fmt"""{"test":>5}""", " test"
+
+    check fmt"""{"test":#^7}""", "#test##"
+
+    check fmt"""{"test": <5}""", "test "
+    check fmt"""{"test":<5}""", "test "
+    check fmt"{1f:.3f}", "1.000"
+    check fmt"Hello, {s}!", "Hello, string!"
+
+    # Tests for identifers without parenthesis
+    check fmt"{s} works{s}", "string worksstring"
+    check fmt"{s:>7}", " string"
+    doAssert(not compiles(fmt"{s_works}")) # parsed as identifier `s_works`
+
+    # Misc general tests
+    check fmt"{{}}", "{}"
+    check fmt"{0}%", "0%"
+    check fmt"{0}%asdf", "0%asdf"
+    check fmt("\n{\"\\n\"}\n"), "\n\n\n"
+    check fmt"""{"abc"}s""", "abcs"
+
+    # String tests
+    check fmt"""{"abc"}""", "abc"
+    check fmt"""{"abc":>4}""", " abc"
+    check fmt"""{"abc":<4}""", "abc "
+    check fmt"""{"":>4}""", "    "
+    check fmt"""{"":<4}""", "    "
+
+    # Int tests
+    check fmt"{12345}", "12345"
+    check fmt"{ - 12345}", "-12345"
+    check fmt"{12345:6}", " 12345"
+    check fmt"{12345:>6}", " 12345"
+    check fmt"{12345:4}", "12345"
+    check fmt"{12345:08}", "00012345"
+    check fmt"{-12345:08}", "-0012345"
+    check fmt"{0:0}", "0"
+    check fmt"{0:02}", "00"
+    check fmt"{-1:3}", " -1"
+    check fmt"{-1:03}", "-01"
+    check fmt"{10}", "10"
+    check fmt"{16:#X}", "0x10"
+    check fmt"{16:^#7X}", " 0x10  "
+    check fmt"{16:^+#7X}", " +0x10 "
+
+    # Hex tests
+    check fmt"{0:x}", "0"
+    check fmt"{-0:x}", "0"
+    check fmt"{255:x}", "ff"
+    check fmt"{255:X}", "FF"
+    check fmt"{-255:x}", "-ff"
+    check fmt"{-255:X}", "-FF"
+    check fmt"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe"
+    check fmt"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe"
+    check fmt"{255:4x}", "  ff"
+    check fmt"{255:04x}", "00ff"
+    check fmt"{-255:4x}", " -ff"
+    check fmt"{-255:04x}", "-0ff"
+
+    # Float tests
+    check fmt"{123.456}", "123.456"
+    check fmt"{-123.456}", "-123.456"
+    check fmt"{123.456:.3f}", "123.456"
+    check fmt"{123.456:+.3f}", "+123.456"
+    check fmt"{-123.456:+.3f}", "-123.456"
+    check fmt"{-123.456:.3f}", "-123.456"
+    check fmt"{123.456:1g}", "123.456"
+    check fmt"{123.456:.1f}", "123.5"
+    check fmt"{123.456:.0f}", "123."
+    #check fmt"{123.456:.0f}", "123."
+    check fmt"{123.456:>9.3f}", "  123.456"
+    check fmt"{123.456:9.3f}", "  123.456"
+    check fmt"{123.456:>9.4f}", " 123.4560"
+    check fmt"{123.456:>9.0f}", "     123."
+    check fmt"{123.456:<9.4f}", "123.4560 "
+
+    # Float (scientific) tests
+    check fmt"{123.456:e}", "1.234560e+02"
+    check fmt"{123.456:>13e}", " 1.234560e+02"
+    check fmt"{123.456:<13e}", "1.234560e+02 "
+    check fmt"{123.456:.1e}", "1.2e+02"
+    check fmt"{123.456:.2e}", "1.23e+02"
+    check fmt"{123.456:.3e}", "1.235e+02"
+
+    # Note: times.format adheres to the format protocol. Test that this
+    # works:
+    import times
+
+    var nullTime: TimeInfo
+    check fmt"{nullTime:yyyy-mm-dd}", "0000-00-00"
+
+    # Unicode string tests
+    check fmt"""{"αβγ"}""", "αβγ"
+    check fmt"""{"αβγ":>5}""", "  αβγ"
+    check fmt"""{"αβγ":<5}""", "αβγ  "
+    check fmt"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈"
+    check fmt"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 "
+    # Invalid unicode sequences should be handled as plain strings.
+    # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173
+    let invalidUtf8 = [
+      "\xc3\x28", "\xa0\xa1",
+      "\xe2\x28\xa1", "\xe2\x82\x28",
+      "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28"
+    ]
+    for s in invalidUtf8:
+      check fmt"{s:>5}", repeat(" ", 5-s.len) & s
+
+  if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
+    error "fmt only works with string literals", pattern
+  let f = pattern.strVal
+  var i = 0
+  let res = genSym(nskVar, "fmtRes")
+  result = newNimNode(nnkStmtListExpr, lineInfoFrom=pattern)
+  result.add newVarStmt(res, newCall(bindSym"newStringOfCap", newLit(f.len + count(f, '{')*10)))
+  var strlit = ""
+  while i < f.len:
+    if f[i] == '{':
+      inc i
+      if f[i] == '{':
+        inc i
+        strlit.add '{'
+      else:
+        if strlit.len > 0:
+          result.add newCall(bindSym"add", res, newLit(strlit))
+          strlit = ""
+
+        var subexpr = ""
+        while i < f.len and f[i] != '}' and f[i] != ':':
+          subexpr.add f[i]
+          inc i
+        let x = parseExpr(subexpr)
+
+        if f[i] == ':':
+          inc i
+          var options = ""
+          while i < f.len and f[i] != '}':
+            options.add f[i]
+            inc i
+          result.add getAst(callFormatOption(res, x, newLit(options)))
+        else:
+          result.add getAst(callFormat(res, x))
+        if f[i] == '}':
+          inc i
+        else:
+          doAssert false, "invalid format string: missing '}'"
+    elif f[i] == '}':
+      if f[i+1] == '}':
+        strlit.add '}'
+        inc i, 2
+      else:
+        doAssert false, "invalid format string: '}' instead of '}}'"
+        inc i
+    else:
+      strlit.add f[i]
+      inc i
+  if strlit.len > 0:
+    result.add newCall(bindSym"add", res, newLit(strlit))
+  result.add res
+  when defined(debugFmtDsl):
+    echo repr result
+
+proc mkDigit(v: int, typ: char): string {.inline.} =
+  assert(v < 26)
+  if v < 10:
+    result = $chr(ord('0') + v)
+  else:
+    result = $chr(ord(if typ == 'x': 'a' else: 'A') + v - 10)
+
+proc alignString*(s: string, minimumWidth: int; align = '\0'; fill = ' '): string =
+  ## Aligns ``s`` using ``fill`` char.
+  ## This is only of interest if you want to write a custom ``format`` proc that
+  ## should support the standard format specifiers.
+  if minimumWidth == 0:
+    result = s
+  else:
+    let sRuneLen = if s.validateUtf8 == -1: s.runeLen else: s.len
+    let toFill = minimumWidth - sRuneLen
+    if toFill <= 0:
+      result = s
+    elif align == '<' or align == '\0':
+      result = s & repeat(fill, toFill)
+    elif align == '^':
+      let half = toFill div 2
+      result = repeat(fill, half) & s & repeat(fill, toFill - half)
+    else:
+      result = repeat(fill, toFill) & s
+
+type
+  StandardFormatSpecifier* = object ## Type that describes "standard format specifiers".
+    fill*, align*: char             ## Desired fill and alignment.
+    sign*: char                     ## Desired sign.
+    alternateForm*: bool            ## Whether to prefix binary, octal and hex numbers
+                                    ## with ``0b``, ``0o``, ``0x``.
+    padWithZero*: bool              ## Whether to pad with zeros rather than spaces.
+    minimumWidth*, precision*: int  ## Desired minium width and precision.
+    typ*: char                      ## Type like 'f', 'g' or 'd'.
+    endPosition*: int ## End position in the format specifier after
+                      ## ``parseStandardFormatSpecifier`` returned.
+
+proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string =
+  ## Converts ``n`` to string. If ``n`` is `SomeReal`, it casts to `int64`.
+  ## Conversion is done using ``radix``. If result's length is lesser than
+  ## ``minimumWidth``, it aligns result to the right or left (depending on ``a``)
+  ## with ``fill`` char.
+  when n is SomeUnsignedInt:
+    var v = n.uint64
+    let negative = false
+  else:
+    var v = n.int64
+    let negative = v.int64 < 0
+    if negative:
+      # FIXME: overflow error for low(int64)
+      v = v * -1
+
+  var xx = ""
+  if spec.alternateForm:
+    case spec.typ
+    of 'X': xx = "0x"
+    of 'x': xx = "0x"
+    of 'b': xx = "0b"
+    of 'o': xx = "0o"
+    else: discard
+
+  if v == 0:
+    result = "0"
+  else:
+    result = ""
+    while v > type(v)(0):
+      let d = v mod type(v)(radix)
+      v = v div type(v)(radix)
+      result.add(mkDigit(d.int, spec.typ))
+    for idx in 0..<(result.len div 2):
+      swap result[idx], result[result.len - idx - 1]
+  if spec.padWithZero:
+    let sign = negative or spec.sign != '-'
+    let toFill = spec.minimumWidth - result.len - xx.len - ord(sign)
+    if toFill > 0:
+      result = repeat('0', toFill) & result
+
+  if negative:
+    result = "-" & xx & result
+  elif spec.sign != '-':
+    result = spec.sign & xx & result
+  else:
+    result = xx & result
+
+  if spec.align == '<':
+    for i in result.len..<spec.minimumWidth:
+      result.add(spec.fill)
+  else:
+    let toFill = spec.minimumWidth - result.len
+    if spec.align == '^':
+      let half = toFill div 2
+      result = repeat(spec.fill, half) & result & repeat(spec.fill, toFill - half)
+    else:
+      if toFill > 0:
+        result = repeat(spec.fill, toFill) & result
+
+proc parseStandardFormatSpecifier*(s: string; start = 0;
+                                   ignoreUnknownSuffix = false): StandardFormatSpecifier =
+  ## An exported helper proc that parses the "standard format specifiers",
+  ## as specified by the grammar::
+  ##
+  ##   [[fill]align][sign][#][0][minimumwidth][.precision][type]
+  ##
+  ## This is only of interest if you want to write a custom ``format`` proc that
+  ## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true,
+  ## an unknown suffix after the ``type`` field is not an error.
+  const alignChars = {'<', '>', '^'}
+  result.fill = ' '
+  result.align = '\0'
+  result.sign = '-'
+  var i = start
+  if i + 1 < s.len and s[i+1] in alignChars:
+    result.fill = s[i]
+    result.align = s[i+1]
+    inc i, 2
+  elif i < s.len and s[i] in alignChars:
+    result.align = s[i]
+    inc i
+
+  if i < s.len and s[i] in {'-', '+', ' '}:
+    result.sign = s[i]
+    inc i
+
+  if i < s.len and s[i] == '#':
+    result.alternateForm = true
+    inc i
+
+  if i+1 < s.len and s[i] == '0' and s[i+1] in {'0'..'9'}:
+    result.padWithZero = true
+    inc i
+
+  let parsedLength = parseSaturatedNatural(s, result.minimumWidth, i)
+  inc i, parsedLength
+  if i < s.len and s[i] == '.':
+    inc i
+    let parsedLengthB = parseSaturatedNatural(s, result.precision, i)
+    inc i, parsedLengthB
+  else:
+    result.precision = -1
+
+  if i < s.len and s[i] in {'A'..'Z', 'a'..'z'}:
+    result.typ = s[i]
+    inc i
+  result.endPosition = i
+  if i != s.len and not ignoreUnknownSuffix:
+    raise newException(ValueError,
+      "invalid format string, cannot parse: " & s[i..^1])
+
+
+proc format*(value: SomeInteger; specifier: string; res: var string) =
+  ## Standard format implementation for ``SomeInteger``. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the ``fmt`` macro.
+  let spec = parseStandardFormatSpecifier(specifier)
+  var radix = 10
+  case spec.typ
+  of 'x', 'X': radix = 16
+  of 'd', '\0': discard
+  of 'b': radix = 2
+  of 'o': radix = 8
+  else:
+    raise newException(ValueError,
+      "invalid type in format string for number, expected one " &
+      " of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
+  res.add formatInt(value, radix, spec)
+
+proc format*(value: SomeReal; specifier: string; res: var string) =
+  ## Standard format implementation for ``SomeReal``. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the ``fmt`` macro.
+  let spec = parseStandardFormatSpecifier(specifier)
+
+  var fmode = ffDefault
+  case spec.typ
+  of 'e', 'E':
+    fmode = ffScientific
+  of 'f', 'F':
+    fmode = ffDecimal
+  of 'g', 'G':
+    fmode = ffDefault
+  of '\0': discard
+  else:
+    raise newException(ValueError,
+      "invalid type in format string for number, expected one " &
+      " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ)
+
+  var f = formatBiggestFloat(value, fmode, spec.precision)
+  if value >= 0.0 and spec.sign != '-':
+    f = spec.sign & f
+  # the default for numbers is right-alignment:
+  let align = if spec.align == '\0': '>' else: spec.align
+  let result = alignString(f, spec.minimumWidth,
+                           align, spec.fill)
+  if spec.typ in {'A'..'Z'}:
+    res.add toUpperAscii(result)
+  else:
+    res.add result
+
+proc format*(value: string; specifier: string; res: var string) =
+  ## Standard format implementation for ``string``. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the ``fmt`` macro.
+  let spec = parseStandardFormatSpecifier(specifier)
+  var fmode = ffDefault
+  case spec.typ
+  of 's', '\0': discard
+  else:
+    raise newException(ValueError,
+      "invalid type in format string for string, expected 's', but got " &
+      spec.typ)
+  res.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index a54556915..2bd87837f 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -31,7 +31,10 @@ As can be seen from the examples, strings are matched verbatim except for
 substrings starting with ``$``. These constructions are available:
 
 =================   ========================================================
-``$i``              Matches an integer. This uses ``parseutils.parseInt``.
+``$b``              Matches a binary integer. This uses ``parseutils.parseBin``.
+``$o``              Matches an octal integer. This uses ``parseutils.parseOct``.
+``$i``              Matches a decimal integer. This uses ``parseutils.parseInt``.
+``$h``              Matches a hex integer. This uses ``parseutils.parseHex``.
 ``$f``              Matches a floating pointer number. Uses ``parseFloat``.
 ``$w``              Matches an ASCII identifier: ``[A-Z-a-z_][A-Za-z_0-9]*``.
 ``$s``              Skips optional whitespace.
@@ -330,19 +333,37 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         conds.add resLen.notZero
         conds.add resLen
       of 'w':
-        if i < results.len or getType(results[i]).typeKind != ntyString:
+        if i < results.len and getType(results[i]).typeKind == ntyString:
           matchBind "parseIdent"
         else:
           error("no string var given for $w")
         inc i
+      of 'b':
+        if i < results.len and getType(results[i]).typeKind == ntyInt:
+          matchBind "parseBin"
+        else:
+          error("no int var given for $b")
+        inc i
+      of 'o':
+        if i < results.len and getType(results[i]).typeKind == ntyInt:
+          matchBind "parseOct"
+        else:
+          error("no int var given for $o")
+        inc i
       of 'i':
-        if i < results.len or getType(results[i]).typeKind != ntyInt:
+        if i < results.len and getType(results[i]).typeKind == ntyInt:
           matchBind "parseInt"
         else:
-          error("no int var given for $d")
+          error("no int var given for $i")
+        inc i
+      of 'h':
+        if i < results.len and getType(results[i]).typeKind == ntyInt:
+          matchBind "parseHex"
+        else:
+          error("no int var given for $h")
         inc i
       of 'f':
-        if i < results.len or getType(results[i]).typeKind != ntyFloat:
+        if i < results.len and getType(results[i]).typeKind == ntyFloat:
           matchBind "parseFloat"
         else:
           error("no float var given for $f")
@@ -357,7 +378,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         else:
           error("invalid format string")
       of '*', '+':
-        if i < results.len or getType(results[i]).typeKind != ntyString:
+        if i < results.len and getType(results[i]).typeKind == ntyString:
           var min = ord(pattern[p] == '+')
           var q=p+1
           var token = ""
@@ -441,7 +462,7 @@ template success*(x: int): bool = x != 0
 template nxt*(input: string; idx, step: int = 1) = inc(idx, step)
 
 macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
-  ## See top level documentation of his module of how ``scanp`` works.
+  ## ``scanp`` is currently undocumented.
   type StmtTriple = tuple[init, cond, action: NimNode]
 
   template interf(x): untyped = bindSym(x, brForceOpen)
@@ -563,12 +584,12 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
     of nnkCurlyExpr:
       if it.len == 3 and it[1].kind == nnkIntLit and it[2].kind == nnkIntLit:
         var h = newTree(nnkPar, it[0])
-        for count in 2..it[1].intVal: h.add(it[0])
+        for count in 2i64 .. it[1].intVal: h.add(it[0])
         for count in it[1].intVal .. it[2].intVal-1: h.add(newTree(nnkPrefix, ident"?", it[0]))
         result = atm(h, input, idx, attached)
       elif it.len == 2 and it[1].kind == nnkIntLit:
         var h = newTree(nnkPar, it[0])
-        for count in 2..it[1].intVal: h.add(it[0])
+        for count in 2i64 .. it[1].intVal: h.add(it[0])
         result = atm(h, input, idx, attached)
       else:
         error("invalid pattern")
@@ -645,6 +666,14 @@ when isMainModule:
   doAssert intval == 89
   doAssert floatVal == 33.25
 
+  var binval: int
+  var octval: int
+  var hexval: int
+  doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval)
+  doAssert binval == 0b0101
+  doAssert octval == 0o1234
+  doAssert hexval == 0xabcd
+
   let xx = scanf("$abc", "$$$i", intval)
   doAssert xx == false
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index cc0f474f4..dbb4db781 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -32,10 +32,6 @@ when defined(nimOldSplit):
 else:
   {.pragma: deprecatedSplit.}
 
-type
-  CharSet* {.deprecated.} = set[char] # for compatibility with Nim
-{.deprecated: [TCharSet: CharSet].}
-
 const
   Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
     ## All the characters that count as whitespace.
@@ -78,40 +74,40 @@ proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
   return c in Letters
 
 proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericChar".}=
+  rtl, extern: "nsuIsAlphaNumericChar".} =
   ## Checks whether or not `c` is alphanumeric.
   ##
   ## This checks a-z, A-Z, 0-9 ASCII characters only.
-  return c in Letters or c in Digits
+  return c in Letters+Digits
 
 proc isDigit*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitChar".}=
+  rtl, extern: "nsuIsDigitChar".} =
   ## Checks whether or not `c` is a number.
   ##
   ## This checks 0-9 ASCII characters only.
   return c in Digits
 
 proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiChar".}=
+  rtl, extern: "nsuIsSpaceAsciiChar".} =
   ## Checks whether or not `c` is a whitespace character.
   return c in Whitespace
 
 proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsLowerAsciiChar".}=
+  rtl, extern: "nsuIsLowerAsciiChar".} =
   ## Checks whether or not `c` is a lower case character.
   ##
   ## This checks ASCII characters only.
   return c in {'a'..'z'}
 
 proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsUpperAsciiChar".}=
+  rtl, extern: "nsuIsUpperAsciiChar".} =
   ## Checks whether or not `c` is an upper case character.
   ##
   ## This checks ASCII characters only.
   return c in {'A'..'Z'}
 
 proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaAsciiStr".}=
+  rtl, extern: "nsuIsAlphaAsciiStr".} =
   ## Checks whether or not `s` is alphabetical.
   ##
   ## This checks a-z, A-Z ASCII characters only.
@@ -123,10 +119,10 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isAlphaAscii() and result
+    if not c.isAlphaAscii(): return false
 
 proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericStr".}=
+  rtl, extern: "nsuIsAlphaNumericStr".} =
   ## Checks whether or not `s` is alphanumeric.
   ##
   ## This checks a-z, A-Z, 0-9 ASCII characters only.
@@ -138,10 +134,11 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isAlphaNumeric() and result
+    if not c.isAlphaNumeric():
+      return false
 
 proc isDigit*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitStr".}=
+  rtl, extern: "nsuIsDigitStr".} =
   ## Checks whether or not `s` is a numeric value.
   ##
   ## This checks 0-9 ASCII characters only.
@@ -153,10 +150,11 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isDigit() and result
+    if not c.isDigit():
+      return false
 
 proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiStr".}=
+  rtl, extern: "nsuIsSpaceAsciiStr".} =
   ## Checks whether or not `s` is completely whitespace.
   ##
   ## Returns true if all characters in `s` are whitespace
@@ -170,7 +168,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
       return false
 
 proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsLowerAsciiStr".}=
+  rtl, extern: "nsuIsLowerAsciiStr".} =
   ## Checks whether or not `s` contains all lower case characters.
   ##
   ## This checks ASCII characters only.
@@ -185,7 +183,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
   true
 
 proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsUpperAsciiStr".}=
+  rtl, extern: "nsuIsUpperAsciiStr".} =
   ## Checks whether or not `s` contains all upper case characters.
   ##
   ## This checks ASCII characters only.
@@ -504,16 +502,15 @@ template splitCommon(s, sep, maxsplit, sepLen) =
   var last = 0
   var splits = maxsplit
 
-  if len(s) > 0:
-    while last <= len(s):
-      var first = last
-      while last < len(s) and not stringHasSep(s, last, sep):
-        inc(last)
-      if splits == 0: last = len(s)
-      yield substr(s, first, last-1)
-      if splits == 0: break
-      dec(splits)
-      inc(last, sepLen)
+  while last <= len(s):
+    var first = last
+    while last < len(s) and not stringHasSep(s, last, sep):
+      inc(last)
+    if splits == 0: last = len(s)
+    yield substr(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last, sepLen)
 
 template oldSplit(s, seps, maxsplit) =
   var last = 0
@@ -576,15 +573,46 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   else:
     splitCommon(s, seps, maxsplit, 1)
 
-iterator splitWhitespace*(s: string): string =
-  ## Splits at whitespace.
-  oldSplit(s, Whitespace, -1)
+iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
+  ## Splits the string ``s`` at whitespace stripping leading and trailing
+  ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
+  ## no more than ``maxsplit`` splits is made.
+  ##
+  ## The following code:
+  ##
+  ## .. code-block:: nim
+  ##   let s = "  foo \t bar  baz  "
+  ##   for ms in [-1, 1, 2, 3]:
+  ##     echo "------ maxsplit = ", ms, ":"
+  ##     for item in s.splitWhitespace(maxsplit=ms):
+  ##       echo '"', item, '"'
+  ##
+  ## ...results in:
+  ##
+  ## .. code-block::
+  ##   ------ maxsplit = -1:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##   ------ maxsplit = 1:
+  ##   "foo"
+  ##   "bar  baz  "
+  ##   ------ maxsplit = 2:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz  "
+  ##   ------ maxsplit = 3:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##
+  oldSplit(s, Whitespace, maxsplit)
 
-proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
+proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitWhitespace".} =
-  ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
+  ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
   ## iterator, but is a proc that returns a sequence of substrings.
-  accumulateResult(splitWhitespace(s))
+  accumulateResult(splitWhitespace(s, maxsplit))
 
 iterator split*(s: string, sep: char, maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a single separator.
@@ -640,36 +668,35 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
     splits = maxsplit
     startPos = 0
 
-  if len(s) > 0:
-    # go to -1 in order to get separators at the beginning
-    while first >= -1:
-      while first >= 0 and not stringHasSep(s, first, sep):
-        dec(first)
+  # go to -1 in order to get separators at the beginning
+  while first >= -1:
+    while first >= 0 and not stringHasSep(s, first, sep):
+      dec(first)
 
-      if splits == 0:
-        # No more splits means set first to the beginning
-        first = -1
+    if splits == 0:
+      # No more splits means set first to the beginning
+      first = -1
 
-      if first == -1:
-        startPos = 0
-      else:
-        startPos = first + sepLen
+    if first == -1:
+      startPos = 0
+    else:
+      startPos = first + sepLen
 
-      yield substr(s, startPos, last)
+    yield substr(s, startPos, last)
 
-      if splits == 0:
-        break
+    if splits == 0:
+      break
 
-      dec(splits)
-      dec(first)
+    dec(splits)
+    dec(first)
 
-      last = first
+    last = first
 
 iterator rsplit*(s: string, seps: set[char] = Whitespace,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,char>`_ except in reverse order.
+  ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
   ##   for piece in "foo bar".rsplit(WhiteSpace):
@@ -689,7 +716,7 @@ iterator rsplit*(s: string, sep: char,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,char>`_ except in reverse order.
+  ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
   ##   for piece in "foo:bar".rsplit(':'):
@@ -708,7 +735,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
                  keepSeparators: bool = false): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,string>`_ except in reverse order.
+  ## <#split.i,string,string,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
   ##   for piece in "foothebar".rsplit("the"):
@@ -789,14 +816,20 @@ proc countLines*(s: string): int {.noSideEffect,
 
 proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
   noSideEffect, rtl, extern: "nsuSplitCharSet".} =
-  ## The same as the `split iterator <#split.i,string,set[char]>`_, but is a
+  ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
   ## proc that returns a sequence of substrings.
+  runnableExamples:
+    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
+    doAssert "".split({' '}) == @[""]
   accumulateResult(split(s, seps, maxsplit))
 
 proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitChar".} =
-  ## The same as the `split iterator <#split.i,string,char>`_, but is a proc
+  ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
   ## that returns a sequence of substrings.
+  runnableExamples:
+    doAssert "a,b,c".split(',') == @["a", "b", "c"]
+    doAssert "".split(' ') == @[""]
   accumulateResult(split(s, sep, maxsplit))
 
 proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
@@ -804,7 +837,14 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
   ## Splits the string `s` into substrings using a string separator.
   ##
   ## Substrings are separated by the string `sep`. This is a wrapper around the
-  ## `split iterator <#split.i,string,string>`_.
+  ## `split iterator <#split.i,string,string,int>`_.
+  runnableExamples:
+    doAssert "a,b,c".split(",") == @["a", "b", "c"]
+    doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"]
+    doAssert "".split("Elon Musk") == @[""]
+    doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
+
+    doAssert "a  largely    spaced sentence".split(" ", maxsplit=1) == @["a", " largely    spaced sentence"]
   doAssert(sep.len > 0)
 
   accumulateResult(split(s, sep, maxsplit))
@@ -812,7 +852,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
 proc rsplit*(s: string, seps: set[char] = Whitespace,
              maxsplit: int = -1): seq[string]
              {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,set[char]>`_, but is a
+  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
   ## proc that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
@@ -834,7 +874,7 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
 
 proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
              {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
-  ## The same as the `split iterator <#rsplit.i,string,char>`_, but is a proc
+  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
   ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
@@ -856,7 +896,7 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
 
 proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
              {.noSideEffect, rtl, extern: "nsuRSplitString".} =
-  ## The same as the `split iterator <#rsplit.i,string,string>`_, but is a proc
+  ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc
   ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
@@ -873,6 +913,13 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
+  runnableExamples:
+    doAssert "a  largely    spaced sentence".rsplit(" ", maxsplit=1) == @["a  largely    spaced", "sentence"]
+
+    doAssert "a,b,c".rsplit(",") == @["a", "b", "c"]
+    doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ", "plan ", "canal panama"]
+    doAssert "".rsplit("Elon Musk") == @[""]
+    doAssert "a  largely    spaced sentence".rsplit(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
   accumulateResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
@@ -1062,8 +1109,8 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   ##
   ## `padding` characters (by default spaces) are added before `s` resulting in
   ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
-  ## returned unchanged. If you need to left align a string use the `repeatChar
-  ## proc <#repeatChar>`_. Example:
+  ## returned unchanged. If you need to left align a string use the `alignLeft
+  ## proc <#alignLeft>`_. Example:
   ##
   ## .. code-block:: nim
   ##   assert align("abc", 4) == " abc"
@@ -1078,6 +1125,28 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   else:
     result = s
 
+proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect.} =
+  ## Left-Aligns a string `s` with `padding`, so that it is of length `count`.
+  ##
+  ## `padding` characters (by default spaces) are added after `s` resulting in
+  ## left alignment. If ``s.len >= count``, no spaces are added and `s` is
+  ## returned unchanged. If you need to right align a string use the `align
+  ## proc <#align>`_. Example:
+  ##
+  ## .. code-block:: nim
+  ##   assert alignLeft("abc", 4) == "abc "
+  ##   assert alignLeft("a", 0) == "a"
+  ##   assert alignLeft("1232", 6) == "1232  "
+  ##   assert alignLeft("1232", 6, '#') == "1232##"
+  if s.len < count:
+    result = newString(count)
+    if s.len > 0:
+      result[0 .. (s.len - 1)] = s
+    for i in s.len ..< count:
+      result[i] = padding
+  else:
+    result = s
+
 iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
   token: string, isSep: bool] =
   ## Tokenizes the string `s` into substrings.
@@ -1175,7 +1244,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
     var indentCount = 0
     for j in 0..<count.int:
       indentCount.inc
-      if line[j .. j + <padding.len] != padding:
+      if line[j .. j + padding.len-1] != padding:
         indentCount = j
         break
     result.add(line[indentCount*padding.len .. ^1])
@@ -1250,14 +1319,13 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
   ## This is often useful for generating some code where the items need to
   ## be *separated* by `sep`. `sep` is only added if `dest` is longer than
   ## `startLen`. The following example creates a string describing
-  ## an array of integers:
-  ##
-  ## .. code-block:: nim
-  ##   var arr = "["
-  ##   for x in items([2, 3, 5, 7, 11]):
-  ##     addSep(arr, startLen=len("["))
-  ##     add(arr, $x)
-  ##   add(arr, "]")
+  ## an array of integers.
+  runnableExamples:
+     var arr = "["
+     for x in items([2, 3, 5, 7, 11]):
+       addSep(arr, startLen=len("["))
+       add(arr, $x)
+     add(arr, "]")
   if dest.len > startLen: add(dest, sep)
 
 proc allCharsInSet*(s: string, theSet: set[char]): bool =
@@ -1306,18 +1374,36 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
     add(result, $x)
 
 type
-  SkipTable = array[char, int]
-
-{.push profiler: off.}
-proc preprocessSub(sub: string, a: var SkipTable) =
-  var m = len(sub)
-  for i in 0..0xff: a[chr(i)] = m+1
-  for i in 0..m-1: a[sub[i]] = m-i
-{.pop.}
+  SkipTable* = array[char, int]
 
-proc findAux(s, sub: string, start, last: int, a: SkipTable): int =
-  # Fast "quick search" algorithm:
-  var
+proc initSkipTable*(a: var SkipTable, sub: string)
+  {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
+  ## Preprocess table `a` for `sub`.
+  let m = len(sub)
+  let m1 = m + 1
+  var i = 0
+  while i <= 0xff-7:
+    a[chr(i + 0)] = m1
+    a[chr(i + 1)] = m1
+    a[chr(i + 2)] = m1
+    a[chr(i + 3)] = m1
+    a[chr(i + 4)] = m1
+    a[chr(i + 5)] = m1
+    a[chr(i + 6)] = m1
+    a[chr(i + 7)] = m1
+    i += 8
+
+  for i in 0..m-1:
+    a[sub[i]] = m-i
+
+proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0): int
+  {.noSideEffect, rtl, extern: "nsuFindStrA".} =
+  ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed table `a`.
+  ## If `last` is unspecified, it defaults to `s.high`.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  let
+    last = if last==0: s.high else: last
     m = len(sub)
     n = last + 1
   # search:
@@ -1337,17 +1423,6 @@ when not (defined(js) or defined(nimdoc) or defined(nimscript)):
 else:
   const hasCStringBuiltin = false
 
-proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindStr".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
-  ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  var a {.noinit.}: SkipTable
-  let last = if last==0: s.high else: last
-  preprocessSub(sub, a)
-  result = findAux(s, sub, start, last, a)
-
 proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
   rtl, extern: "nsuFindChar".} =
   ## Searches for `sub` in `s` inside range `start`..`last`.
@@ -1366,9 +1441,24 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
     else:
       for i in start..last:
         if sub == s[i]: return i
-
   return -1
 
+proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
+  rtl, extern: "nsuFindStr".} =
+  ## Searches for `sub` in `s` inside range `start`..`last`.
+  ## If `last` is unspecified, it defaults to `s.high`.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  if sub.len > s.len:
+    return -1
+
+  if sub.len == 1:
+    return find(s, sub[0], start, last)
+
+  var a {.noinit.}: SkipTable
+  initSkipTable(a, sub)
+  result = find(a, s, sub, start, last)
+
 proc find*(s: string, chars: set[char], start: Natural = 0, last: Natural = 0): int {.noSideEffect,
   rtl, extern: "nsuFindCharSet".} =
   ## Searches for `chars` in `s` inside range `start`..`last`.
@@ -1500,11 +1590,11 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   ## Replaces `sub` in `s` by the string `by`.
   var a {.noinit.}: SkipTable
   result = ""
-  preprocessSub(sub, a)
+  initSkipTable(a, sub)
   let last = s.high
   var i = 0
   while true:
-    var j = findAux(s, sub, i, last, a)
+    var j = find(a, s, sub, i, last)
     if j < 0: break
     add result, substr(s, i, j - 1)
     add result, by
@@ -1534,11 +1624,11 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
   var a {.noinit.}: SkipTable
   result = ""
-  preprocessSub(sub, a)
+  initSkipTable(a, sub)
   var i = 0
   let last = s.high
   while true:
-    var j = findAux(s, sub, i, last, a)
+    var j = find(a, s, sub, i, last)
     if j < 0: break
     # word boundary?
     if (j == 0 or s[j-1] notin wordChars) and
@@ -1653,7 +1743,9 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
   ##
   ## Even though the algorithm works with any string `s`, it is only useful
   ## if `s` contains a number.
-  ## Example: ``insertSep("1000000") == "1_000_000"``
+  runnableExamples:
+    doAssert insertSep("1000000") == "1_000_000"
+
   var L = (s.len-1) div digits + s.len
   result = newString(L)
   var j = 0
@@ -1669,29 +1761,15 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
 
 proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   rtl, extern: "nsuEscape".} =
-  ## Escapes a string `s`.
-  ##
-  ## This does these operations (at the same time):
-  ## * replaces any ``\`` by ``\\``
-  ## * replaces any ``'`` by ``\'``
-  ## * replaces any ``"`` by ``\"``
-  ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}``
-  ##   by ``\xHH`` where ``HH`` is its hexadecimal value.
-  ## The procedure has been designed so that its output is usable for many
-  ## different common syntaxes. The resulting string is prefixed with
-  ## `prefix` and suffixed with `suffix`. Both may be empty strings.
-  ## **Note**: This is not correct for producing Ansi C code!
+  ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_
+  ## for the escaping scheme.
+  ##
+  ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
+  ## Both may be empty strings.
   result = newStringOfCap(s.len + s.len shr 2)
   result.add(prefix)
   for c in items(s):
-    case c
-    of '\0'..'\31', '\127'..'\255':
-      add(result, "\\x")
-      add(result, toHex(ord(c), 2))
-    of '\\': add(result, "\\\\")
-    of '\'': add(result, "\\'")
-    of '\"': add(result, "\\\"")
-    else: add(result, c)
+    result.addEscapedChar(c)
   add(result, suffix)
 
 proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
@@ -1741,6 +1819,8 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
   ##
   ## A valid identifier starts with a character of the set `IdentStartChars`
   ## and is followed by any number of characters of the set `IdentChars`.
+  runnableExamples:
+    doAssert "abc_def08".validIdentifier
   if s[0] in IdentStartChars:
     for i in 1..s.len-1:
       if s[i] notin IdentChars: return false
@@ -1751,7 +1831,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
   ## Returns the edit distance between `a` and `b`.
   ##
   ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
-  ## memory overhead.  This implementation is highly optimized!
+  ## memory overhead.
   var len1 = a.len
   var len2 = b.len
   if len1 > len2:
@@ -1850,7 +1930,7 @@ type
 {.deprecated: [TFloatFormat: FloatFormatMode].}
 
 proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
-                         precision: range[0..32] = 16;
+                         precision: range[-1..32] = 16;
                          decimalSep = '.'): string {.
                          noSideEffect, rtl, extern: "nsu$1".} =
   ## Converts a floating point value `f` to a string.
@@ -1862,7 +1942,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
   ## `precision`'s default value is the maximum number of meaningful digits
   ## after the decimal point for Nim's ``biggestFloat`` type.
   ##
-  ## If ``precision == 0``, it tries to format it nicely.
+  ## If ``precision == -1``, it tries to format it nicely.
   when defined(js):
     var res: cstring
     case format
@@ -1884,7 +1964,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
       buf {.noinit.}: array[0..2500, char]
       L: cint
     frmtstr[0] = '%'
-    if precision > 0:
+    if precision >= 0:
       frmtstr[1] = '#'
       frmtstr[2] = '.'
       frmtstr[3] = '*'
@@ -1907,9 +1987,18 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
       # but nothing else is possible:
       if buf[i] in {'.', ','}: result[i] = decimalsep
       else: result[i] = buf[i]
+    when defined(windows):
+      # VS pre 2015 violates the C standard: "The exponent always contains at
+      # least two digits, and only as many more digits as necessary to
+      # represent the exponent." [C11 §7.21.6.1]
+      # The following post-processing fixes this behavior.
+      if result.len > 4 and result[^4] == '+' and result[^3] == '0':
+        result[^3] = result[^2]
+        result[^2] = result[^1]
+        result.setLen(result.len - 1)
 
 proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
-                  precision: range[0..32] = 16; decimalSep = '.'): string {.
+                  precision: range[-1..32] = 16; decimalSep = '.'): string {.
                   noSideEffect, rtl, extern: "nsu$1".} =
   ## Converts a floating point value `f` to a string.
   ##
@@ -1920,7 +2009,12 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
   ## `precision`'s default value is the maximum number of meaningful digits
   ## after the decimal point for Nim's ``float`` type.
   ##
-  ## If ``precision == 0``, it tries to format it nicely.
+  ## If ``precision == -1``, it tries to format it nicely.
+  runnableExamples:
+    let x = 123.456
+    doAssert x.formatFloat() == "123.4560000000000"
+    doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
+    doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
 proc trimZeros*(x: var string) {.noSideEffect.} =
@@ -1955,18 +2049,13 @@ proc formatSize*(bytes: int64,
   ##
   ## `includeSpace` can be set to true to include the (SI preferred) space
   ## between the number and the unit (e.g. 1 KiB).
-  ##
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##    formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
-  ##    formatSize((2.234*1024*1024).int) == "2.234MiB"
-  ##    formatSize(4096, includeSpace=true) == "4 KiB"
-  ##    formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
-  ##    formatSize(4096) == "4KiB"
-  ##    formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
-  ##
+  runnableExamples:
+    doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
+    doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
+    doAssert formatSize(4096, includeSpace=true) == "4 KiB"
+    doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+    doAssert formatSize(4096) == "4KiB"
+    doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
   const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
   const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
   var
@@ -2060,7 +2149,7 @@ proc formatEng*(f: BiggestFloat,
   ##    formatEng(4100, unit="V") == "4.1e3 V"
   ##    formatEng(4100, unit="") == "4.1e3 " # Space with unit=""
   ##
-  ## `decimalSep` is used as the decimal separator
+  ## `decimalSep` is used as the decimal separator.
   var
     absolute: BiggestFloat
     significand: BiggestFloat
@@ -2271,69 +2360,116 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect,
 
 proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
   rtl, extern: "nsuRemoveSuffixCharSet".} =
-  ## Removes the first matching character from the string (in-place) given a
-  ## set of characters. If the set of characters is only equal to `Newlines`
-  ## then it will remove both the newline and return feed.
-  ## .. code-block:: nim
-  ##   var
-  ##     userInput = "Hello World!\r\n"
-  ##     otherInput = "Hello!?!"
-  ##   userInput.removeSuffix
-  ##   userInput == "Hello World!"
-  ##   userInput.removeSuffix({'!', '?'})
-  ##   userInput == "Hello World"
-  ##   otherInput.removeSuffix({'!', '?'})
-  ##   otherInput == "Hello!?"
+  ## Removes all characters from `chars` from the end of the string `s`
+  ## (in-place).
+  runnableExamples:
+     var userInput = "Hello World!*~\r\n"
+     userInput.removeSuffix
+     doAssert userInput == "Hello World!*~"
+     userInput.removeSuffix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "Hello!?!"
+     otherInput.removeSuffix({'!', '?'})
+     doAssert otherInput == "Hello"
   if s.len == 0: return
-  var last = len(s) - 1
-  if chars == Newlines:
-    if s[last] == '\10':
-      last -= 1
-    if s[last] == '\13':
-      last -= 1
-  else:
-    if s[last] in chars:
-      last -= 1
+  var last = s.high
+  while last > -1 and s[last] in chars: last -= 1
   s.setLen(last + 1)
 
 proc removeSuffix*(s: var string, c: char) {.
   rtl, extern: "nsuRemoveSuffixChar".} =
-  ## Removes a single character (in-place) from a string.
-  ## .. code-block:: nim
-  ##   var
-  ##     table = "users"
-  ##   table.removeSuffix('s')
-  ##   table == "user"
+  ## Removes all occurrences of a single character (in-place) from the end
+  ## of a string.
+  ##
+  runnableExamples:
+     var table = "users"
+     table.removeSuffix('s')
+     doAssert table == "user"
+
+     var dots = "Trailing dots......."
+     dots.removeSuffix('.')
+     doAssert dots == "Trailing dots"
   removeSuffix(s, chars = {c})
 
 proc removeSuffix*(s: var string, suffix: string) {.
   rtl, extern: "nsuRemoveSuffixString".} =
   ## Remove the first matching suffix (in-place) from a string.
-  ## .. code-block:: nim
-  ##   var
-  ##     answers = "yeses"
-  ##   answers.removeSuffix("es")
-  ##   answers == "yes"
+  runnableExamples:
+     var answers = "yeses"
+     answers.removeSuffix("es")
+     doAssert answers == "yes"
   var newLen = s.len
   if s.endsWith(suffix):
     newLen -= len(suffix)
     s.setLen(newLen)
 
+proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
+  rtl, extern: "nsuRemovePrefixCharSet".} =
+  ## Removes all characters from `chars` from the start of the string `s`
+  ## (in-place).
+  ##
+  runnableExamples:
+     var userInput = "\r\n*~Hello World!"
+     userInput.removePrefix
+     doAssert userInput == "*~Hello World!"
+     userInput.removePrefix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "?!?Hello!?!"
+     otherInput.removePrefix({'!', '?'})
+     doAssert otherInput == "Hello!?!"
+  var start = 0
+  while start < s.len and s[start] in chars: start += 1
+  if start > 0: s.delete(0, start - 1)
+
+proc removePrefix*(s: var string, c: char) {.
+  rtl, extern: "nsuRemovePrefixChar".} =
+  ## Removes all occurrences of a single character (in-place) from the start
+  ## of a string.
+  ##
+  runnableExamples:
+     var ident = "pControl"
+     ident.removePrefix('p')
+     doAssert ident == "Control"
+  removePrefix(s, chars = {c})
+
+proc removePrefix*(s: var string, prefix: string) {.
+  rtl, extern: "nsuRemovePrefixString".} =
+  ## Remove the first matching prefix (in-place) from a string.
+  ##
+  runnableExamples:
+     var answers = "yesyes"
+     answers.removePrefix("yes")
+     doAssert answers == "yes"
+  if s.startsWith(prefix):
+    s.delete(0, prefix.len - 1)
+
 when isMainModule:
   doAssert align("abc", 4) == " abc"
   doAssert align("a", 0) == "a"
   doAssert align("1232", 6) == "  1232"
   doAssert align("1232", 6, '#') == "##1232"
 
+  doAssert alignLeft("abc", 4) == "abc "
+  doAssert alignLeft("a", 0) == "a"
+  doAssert alignLeft("1232", 6) == "1232  "
+  doAssert alignLeft("1232", 6, '#') == "1232##"
+
   let
     inp = """ this is a long text --  muchlongerthan10chars and here
                it goes"""
     outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
   doAssert wordWrap(inp, 10, false) == outp
 
+  doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
+  doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
+  doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
   doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
   doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
                                                    ["1,0e-11", "1,0e-011"]
+  # bug #6589
+  doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
 
   doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
   doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
@@ -2497,6 +2633,12 @@ bar
   doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
   doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
 
+  doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+  doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
+  doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
+  doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
+  doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
+
   block: # formatEng tests
     doAssert formatEng(0, 2, trim=false) == "0.00"
     doAssert formatEng(0, 2) == "0"
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 871ac5d39..f15cee66a 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -391,8 +391,8 @@ proc eraseLine*(f: File) =
     origin.X = 0'i16
     if setConsoleCursorPosition(h, origin) == 0:
       raiseOSError(osLastError())
-    var ht = scrbuf.dwSize.Y - origin.Y
-    var wt = scrbuf.dwSize.X - origin.X
+    var ht: DWORD = scrbuf.dwSize.Y - origin.Y
+    var wt: DWORD = scrbuf.dwSize.X - origin.X
     if fillConsoleOutputCharacter(h, ' ', ht*wt,
                                   origin, addr(numwrote)) == 0:
       raiseOSError(osLastError())
@@ -634,7 +634,10 @@ proc getch*(): char =
       doAssert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0)
       if numRead == 0 or keyEvent.eventType != 1 or keyEvent.bKeyDown == 0:
         continue
-      return char(keyEvent.uChar)
+      if keyEvent.uChar == 0:
+        return char(keyEvent.wVirtualKeyCode)
+      else:
+        return char(keyEvent.uChar)
   else:
     let fd = getFileHandle(stdin)
     var oldMode: Termios
@@ -650,10 +653,10 @@ template setCursorPos*(x, y: int) = setCursorPos(stdout, x, y)
 template setCursorXPos*(x: int)   = setCursorXPos(stdout, x)
 when defined(windows):
   template setCursorYPos(x: int)  = setCursorYPos(stdout, x)
-template cursorUp*(count=1)       = cursorUp(stdout, f)
-template cursorDown*(count=1)     = cursorDown(stdout, f)
-template cursorForward*(count=1)  = cursorForward(stdout, f)
-template cursorBackward*(count=1) = cursorBackward(stdout, f)
+template cursorUp*(count=1)       = cursorUp(stdout, count)
+template cursorDown*(count=1)     = cursorDown(stdout, count)
+template cursorForward*(count=1)  = cursorForward(stdout, count)
+template cursorBackward*(count=1) = cursorBackward(stdout, count)
 template eraseLine*()             = eraseLine(stdout)
 template eraseScreen*()           = eraseScreen(stdout)
 template setStyle*(style: set[Style]) =
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 96668c4f8..42e89e7ce 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -10,27 +10,25 @@
 
 ## This module contains routines and types for dealing with time.
 ## This module is available for the `JavaScript target
-## <backends.html#the-javascript-target>`_.
+## <backends.html#the-javascript-target>`_. The proleptic Gregorian calendar is the only calendar supported.
 ##
 ## Examples:
 ##
 ## .. code-block:: nim
 ##
 ##  import times, os
-##  var
-##    t = cpuTime()
+##  let time = cpuTime()
 ##
 ##  sleep(100)   # replace this with something to be timed
-##  echo "Time taken: ",cpuTime() - t
+##  echo "Time taken: ",cpuTime() - time
 ##
-##  echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm")
+##  echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
 ##  echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
 ##
 ##  echo "epochTime() float value: ", epochTime()
-##  echo "getTime()   float value: ", toSeconds(getTime())
 ##  echo "cpuTime()   float value: ", cpuTime()
-##  echo "An hour from now      : ", getLocalTime(getTime()) + 1.hours
-##  echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
+##  echo "An hour from now      : ", now() + 1.hours
+##  echo "An hour from (UTC) now: ", getTime().utc + initInterval(0,0,0,1)
 
 {.push debugger:off.} # the user does not want to trace a part
                       # of the standard library!
@@ -40,132 +38,85 @@ import
 
 include "system/inclrtl"
 
-type
-  Month* = enum ## represents a month
-    mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
-  WeekDay* = enum ## represents a weekday
-    dMon, dTue, dWed, dThu, dFri, dSat, dSun
-
-when defined(posix) and not defined(JS):
-  when defined(linux) and defined(amd64):
-    type
-      TimeImpl {.importc: "time_t", header: "<time.h>".} = clong
-      Time* = distinct TimeImpl ## distinct type that represents a time
-                                ## measured as number of seconds since the epoch
-
-      Timeval {.importc: "struct timeval",
-                header: "<sys/select.h>".} = object ## struct timeval
-        tv_sec: clong  ## Seconds.
-        tv_usec: clong ## Microseconds.
-  else:
-    type
-      TimeImpl {.importc: "time_t", header: "<time.h>".} = int
-      Time* = distinct TimeImpl ## distinct type that represents a time
-                                ## measured as number of seconds since the epoch
-
-      Timeval {.importc: "struct timeval",
-                header: "<sys/select.h>".} = object ## struct timeval
-        tv_sec: int  ## Seconds.
-        tv_usec: int ## Microseconds.
+when defined(posix):
+  import posix
 
-  # we cannot import posix.nim here, because posix.nim depends on times.nim.
-  # Ok, we could, but I don't want circular dependencies.
-  # And gettimeofday() is not defined in the posix module anyway. Sigh.
+  type CTime = posix.Time
 
   proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
     importc: "gettimeofday", header: "<sys/time.h>".}
 
   when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
     var timezone {.importc, header: "<time.h>".}: int
-    proc tzset(): void {.importc, header: "<time.h>".}
     tzset()
 
 elif defined(windows):
   import winlean
 
   # newest version of Visual C++ defines time_t to be of 64 bits
-  type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
+  type CTime {.importc: "time_t", header: "<time.h>".} = distinct int64
   # visual c's c runtime exposes these under a different name
-  var
-    timezone {.importc: "_timezone", header: "<time.h>".}: int
-
-  type
-    Time* = distinct TimeImpl
+  var timezone {.importc: "_timezone", header: "<time.h>".}: int
 
+type
+  Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
+                ## the month number in the range ``[1..12]``.
+    mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
 
-elif defined(JS):
-  type
-    TimeBase = float
-    Time* = distinct TimeBase
-
-  proc getDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getTime(t: Time): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
-  proc getTimezoneOffset(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc parse(t: Time; s: cstring): Time {.tags: [], raises: [], benign, importcpp.}
-  proc setDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setTime(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc toGMTString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
-  proc toLocaleString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
+  WeekDay* = enum ## Represents a weekday.
+    dMon, dTue, dWed, dThu, dFri, dSat, dSun
 
-type
-  TimeInfo* = object of RootObj ## represents a time in different parts
-    second*: range[0..61]     ## The number of seconds after the minute,
+  MonthdayRange* = range[1..31]
+  HourRange* = range[0..23]
+  MinuteRange* = range[0..59]
+  SecondRange* = range[0..60]
+  YeardayRange* = range[0..365]
+
+  TimeImpl = int64
+
+  Time* = distinct TimeImpl ## Represents a point in time.
+                            ## This is currently implemented as a ``int64`` representing
+                            ## seconds since ``1970-01-01T00:00:00Z``, but don't
+                            ## rely on this knowledge because it might change
+                            ## in the future to allow for higher precision.
+                            ## Use the procs ``toUnix`` and ``fromUnix`` to
+                            ## work with unix timestamps instead.
+
+  DateTime* = object of RootObj ## Represents a time in different parts.
+                                ## Although this type can represent leap
+                                ## seconds, they are generally not supported
+                                ## in this module. They are not ignored,
+                                ## but the ``DateTime``'s returned by
+                                ## procedures in this module will never have
+                                ## a leap second.
+    second*: SecondRange      ## The number of seconds after the minute,
                               ## normally in the range 0 to 59, but can
-                              ## be up to 61 to allow for leap seconds.
-    minute*: range[0..59]     ## The number of minutes after the hour,
+                              ## be up to 60 to allow for a leap second.
+    minute*: MinuteRange      ## The number of minutes after the hour,
                               ## in the range 0 to 59.
-    hour*: range[0..23]       ## The number of hours past midnight,
+    hour*: HourRange          ## The number of hours past midnight,
                               ## in the range 0 to 23.
-    monthday*: range[1..31]   ## The day of the month, in the range 1 to 31.
+    monthday*: MonthdayRange  ## The day of the month, in the range 1 to 31.
     month*: Month             ## The current month.
-    year*: int                ## The current year.
+    year*: int                ## The current year, using astronomical year numbering
+                              ## (meaning that before year 1 is year 0, then year -1 and so on).
     weekday*: WeekDay         ## The current day of the week.
-    yearday*: range[0..365]   ## The number of days since January 1,
+    yearday*: YeardayRange    ## The number of days since January 1,
                               ## in the range 0 to 365.
-                              ## Always 0 if the target is JS.
-    isDST*: bool              ## Determines whether DST is in effect.
-                              ## Semantically, this adds another negative hour
-                              ## offset to the time in addition to the timezone.
-    timezone*: int            ## The offset of the (non-DST) timezone in seconds
-                              ## west of UTC. Note that the sign of this number
-                              ## is the opposite of the one in a formatted
-                              ## timezone string like ``+01:00`` (which would be
-                              ## parsed into the timezone ``-3600``).
-
-  ## I make some assumptions about the data in here. Either
-  ## everything should be positive or everything negative. Zero is
-  ## fine too. Mixed signs will lead to unexpected results.
-  TimeInterval* = object ## a time interval
+    isDst*: bool              ## Determines whether DST is in effect.
+                              ## Always false for the JavaScript backend.
+    timezone*: Timezone       ## The timezone represented as an implementation of ``Timezone``.
+    utcOffset*: int           ## The offset in seconds west of UTC, including any offset due to DST.
+                              ## Note that the sign of this number is the opposite
+                              ## of the one in a formatted offset string like ``+01:00``
+                              ## (which would be parsed into the UTC offset ``-3600``).
+
+  TimeInterval* = object ## Represents a duration of time. Can be used to add and subtract
+                         ## from a ``DateTime`` or ``Time``.
+                         ## Note that a ``TimeInterval`` doesn't represent a fixed duration of time,
+                         ## since the duration of some units depend on the context (e.g a year
+                         ## can be either 365 or 366 days long). The non-fixed time units are years,
+                         ## months and days.
     milliseconds*: int ## The number of milliseconds
     seconds*: int     ## The number of seconds
     minutes*: int     ## The number of minutes
@@ -174,92 +125,394 @@ type
     months*: int      ## The number of months
     years*: int       ## The number of years
 
+  Timezone* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones.
+                     ## The ``times`` module only supplies implementations for the systems local time and UTC.
+                     ## The members ``zoneInfoFromUtc`` and ``zoneInfoFromTz`` should not be accessed directly
+                     ## and are only exported so that ``Timezone`` can be implemented by other modules.
+    zoneInfoFromUtc*: proc (time: Time): ZonedTime {.nimcall, tags: [], raises: [], benign .}
+    zoneInfoFromTz*:  proc (adjTime: Time): ZonedTime {.nimcall, tags: [], raises: [], benign .}
+    name*: string ## The name of the timezone, f.ex 'Europe/Stockholm' or 'Etc/UTC'. Used for checking equality.
+                  ## Se also: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+  ZonedTime* = object ## Represents a zooned instant in time that is not associated with any calendar.
+                      ## This type is only used for implementing timezones.
+    adjTime*: Time ## Time adjusted to a timezone.
+    utcOffset*: int
+    isDst*: bool
+
 {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
-    TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
+    TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
 
-proc getTime*(): Time {.tags: [TimeEffect], benign.}
-  ## gets the current calendar time as a UNIX epoch value (number of seconds
-  ## elapsed since 1970) with integer precission. Use epochTime for higher
-  ## resolution.
-proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
-  ## converts the calendar time `t` to broken-time representation,
-  ## expressed relative to the user's specified time zone.
-proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
-  ## converts the calendar time `t` to broken-down time representation,
-  ## expressed in Coordinated Universal Time (UTC).
-
-proc timeInfoToTime*(timeInfo: TimeInfo): Time
-    {.tags: [TimeEffect], benign, deprecated.}
-  ## converts a broken-down time structure to
-  ## calendar time representation. The function ignores the specified
-  ## contents of the structure members `weekday` and `yearday` and recomputes
-  ## them from the other information in the broken-down time structure.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``toTime`` instead.
+const
+  secondsInMin = 60
+  secondsInHour = 60*60
+  secondsInDay = 60*60*24
+  minutesInHour = 60
 
-proc toTime*(timeInfo: TimeInfo): Time {.tags: [TimeEffect], benign.}
-  ## converts a broken-down time structure to
-  ## calendar time representation. The function ignores the specified
-  ## contents of the structure members `weekday` and `yearday` and recomputes
-  ## them from the other information in the broken-down time structure.
+proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``.
+  Time(unix)
 
-proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign.}
-  ## Takes a float which contains the number of seconds since the unix epoch and
-  ## returns a time object.
+proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
+  t.int64
 
-proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign.} =
-  ## Takes an int which contains the number of seconds since the unix epoch and
-  ## returns a time object.
-  fromSeconds(float(since1970))
+proc isLeapYear*(year: int): bool =
+  ## Returns true if ``year`` is a leap year.
+  year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
 
-proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.}
-  ## Returns the time in seconds since the unix epoch.
+proc getDaysInMonth*(month: Month, year: int): int =
+  ## Get the number of days in a ``month`` of a ``year``.
+  # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
+  case month
+  of mFeb: result = if isLeapYear(year): 29 else: 28
+  of mApr, mJun, mSep, mNov: result = 30
+  else: result = 31
+
+proc getDaysInYear*(year: int): int =
+  ## Get the number of days in a ``year``
+  result = 365 + (if isLeapYear(year): 1 else: 0)
+
+proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
+  assert monthday <= getDaysInMonth(month, year),
+    $year & "-" & $ord(month) & "-" & $monthday & " is not a valid date"
+
+proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
+  ## Get the epoch day from a year/month/day date.
+  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
+  assertValidDate monthday, month, year
+  # Based on http://howardhinnant.github.io/date_algorithms.html
+  var (y, m, d) = (year, ord(month), monthday.int)
+  if m <= 2:
+    y.dec
+
+  let era = (if y >= 0: y else: y-399) div 400
+  let yoe = y - era * 400
+  let doy = (153 * (m + (if m > 2: -3 else: 9)) + 2) div 5 + d-1
+  let doe = yoe * 365 + yoe div 4 - yoe div 100 + doy
+  return era * 146097 + doe - 719468
+
+proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month, year: int] =
+  ## Get the year/month/day date from a epoch day.
+  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
+  # Based on http://howardhinnant.github.io/date_algorithms.html
+  var z = epochday
+  z.inc 719468
+  let era = (if z >= 0: z else: z - 146096) div 146097
+  let doe = z - era * 146097
+  let yoe = (doe - doe div 1460 + doe div 36524 - doe div 146096) div 365
+  let y = yoe + era * 400;
+  let doy = doe - (365 * yoe + yoe div 4 - yoe div 100)
+  let mp = (5 * doy + 2) div 153
+  let d = doy - (153 * mp + 2) div 5 + 1
+  let m = mp + (if mp < 10: 3 else: -9)
+  return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
+
+proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
+  ## Returns the day of the year.
+  ## Equivalent with ``initDateTime(day, month, year).yearday``.
+  assertValidDate monthday, month, year
+  const daysUntilMonth:     array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+  const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
+
+  if isLeapYear(year):
+    result = daysUntilMonthLeap[month] + monthday - 1
+  else:
+    result = daysUntilMonth[month] + monthday - 1
+
+proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
+  ## Returns the day of the week enum from day, month and year.
+  ## Equivalent with ``initDateTime(day, month, year).weekday``.
+  assertValidDate monthday, month, year
+  # 1970-01-01 is a Thursday, we adjust to the previous Monday
+  let days = toEpochday(monthday, month, year) - 3
+  let weeks = (if days >= 0: days else: days - 6) div 7
+  let wd = days - weeks * 7
+  # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
+  # so we must correct for the WeekDay type.
+  result = if wd == 0: dSun else: WeekDay(wd - 1)
+
+# Forward declarations
+proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
 
 proc `-`*(a, b: Time): int64 {.
-  rtl, extern: "ntDiffTime", tags: [], raises: [], noSideEffect, benign.}
-  ## computes the difference of two calendar times. Result is in seconds.
+    rtl, extern: "ntDiffTime", tags: [], raises: [], noSideEffect, benign, deprecated.} =
+  ## Computes the difference of two calendar times. Result is in seconds.
+  ## This is deprecated because it will need to change when sub second time resolution is implemented.
+  ## Use ``a.toUnix - b.toUnix`` instead.
   ##
   ## .. code-block:: nim
   ##     let a = fromSeconds(1_000_000_000)
   ##     let b = fromSeconds(1_500_000_000)
   ##     echo initInterval(seconds=int(b - a))
-  ##     # (milliseconds: 0, seconds: 20, minutes: 53, hours: 0, days: 5787, months: 0, years: 0) 
+  ##     # (milliseconds: 0, seconds: 20, minutes: 53, hours: 0, days: 5787, months: 0, years: 0)
+  a.toUnix - b.toUnix
 
 proc `<`*(a, b: Time): bool {.
-  rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} =
-  ## returns true iff ``a < b``, that is iff a happened before b.
-  when defined(js):
-    result = TimeBase(a) < TimeBase(b)
-  else:
-    result = a - b < 0
+    rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true iff ``a < b``, that is iff a happened before b.
 
 proc `<=` * (a, b: Time): bool {.
-  rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}=
-  ## returns true iff ``a <= b``.
-  when defined(js):
-    result = TimeBase(a) <= TimeBase(b)
-  else:
-    result = a - b <= 0
+    rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true iff ``a <= b``.
 
 proc `==`*(a, b: Time): bool {.
-  rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} =
-  ## returns true if ``a == b``, that is if both times represent the same value
-  when defined(js):
-    result = TimeBase(a) == TimeBase(b)
+    rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true if ``a == b``, that is if both times represent the same point in time.
+
+proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
+  ## Converts a broken-down time structure to
+  ## calendar time representation.
+  let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
+  result = Time(epochDay * secondsInDay)
+  result.inc dt.hour * secondsInHour
+  result.inc dt.minute * 60
+  result.inc dt.second
+  # The code above ignores the UTC offset of `timeInfo`,
+  # so we need to compensate for that here.
+  result.inc dt.utcOffset
+
+proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
+  let adjTime = zt.adjTime.int64
+  let epochday = (if adjTime >= 0: adjTime else: adjTime - (secondsInDay - 1)) div secondsInDay
+  var rem = zt.adjTime.int64 - epochday * secondsInDay
+  let hour = rem div secondsInHour
+  rem = rem - hour * secondsInHour
+  let minute = rem div secondsInMin
+  rem = rem - minute * secondsInMin
+  let second = rem
+
+  let (d, m, y) = fromEpochday(epochday)
+
+  DateTime(
+    year: y,
+    month: m,
+    monthday: d,
+    hour: hour,
+    minute: minute,
+    second: second,
+    weekday: getDayOfWeek(d, m, y),
+    yearday: getDayOfYear(d, m, y),
+    isDst: zt.isDst,
+    timezone: zone,
+    utcOffset: zt.utcOffset
+  )
+
+proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
+  ## Break down ``time`` into a ``DateTime`` using ``zone`` as the timezone.
+  let zoneInfo = zone.zoneInfoFromUtc(time)
+  result = initDateTime(zoneInfo, zone)
+
+proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
+  ## Convert ``dt`` into a ``DateTime`` using ``zone`` as the timezone.
+  dt.toTime.inZone(zone)
+
+proc `$`*(zone: Timezone): string =
+  ## Returns the name of the timezone.
+  zone.name
+
+proc `==`*(zone1, zone2: Timezone): bool =
+  ## Two ``Timezone``'s are considered equal if their name is equal.
+  zone1.name == zone2.name
+
+proc toAdjTime(dt: DateTime): Time =
+  let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
+  result = Time(epochDay * secondsInDay)
+  result.inc dt.hour * secondsInHour
+  result.inc dt.minute * secondsInMin
+  result.inc dt.second
+
+when defined(JS):
+    type JsDate = object
+    proc newDate(year, month, date, hours, minutes, seconds, milliseconds: int): JsDate {.tags: [], raises: [], importc: "new Date".}
+    proc newDate(): JsDate {.importc: "new Date".}
+    proc newDate(value: float): JsDate {.importc: "new Date".}
+    proc getTimezoneOffset(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getTime(js: JsDate): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
+    proc getDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
+
+    proc localZoneInfoFromUtc(time: Time): ZonedTime =
+      let jsDate = newDate(time.float * 1000)
+      let offset = jsDate.getTimezoneOffset() * secondsInMin
+      result.adjTime = Time(time.int64 - offset)
+      result.utcOffset = offset
+      result.isDst = false
+
+    proc localZoneInfoFromTz(adjTime: Time): ZonedTime =
+      let utcDate = newDate(adjTime.float * 1000)
+      let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
+        utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
+
+      # This is as dumb as it looks - JS doesn't support years in the range 0-99 in the constructor
+      # because they are assumed to be 19xx...
+      # Because JS doesn't support timezone history, it doesn't really matter in practice.
+      if utcDate.getUTCFullYear() in 0 .. 99:
+        localDate.setFullYear(utcDate.getUTCFullYear())
+
+      result.adjTime = adjTime
+      result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
+      result.isDst = false
+
+else:
+  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
+      defined(macosx):
+    type
+      StructTm {.importc: "struct tm".} = object
+        second {.importc: "tm_sec".},
+          minute {.importc: "tm_min".},
+          hour {.importc: "tm_hour".},
+          monthday {.importc: "tm_mday".},
+          month {.importc: "tm_mon".},
+          year {.importc: "tm_year".},
+          weekday {.importc: "tm_wday".},
+          yearday {.importc: "tm_yday".},
+          isdst {.importc: "tm_isdst".}: cint
+        gmtoff {.importc: "tm_gmtoff".}: clong
   else:
-    result = a - b == 0
+    type
+      StructTm {.importc: "struct tm".} = object
+        second {.importc: "tm_sec".},
+          minute {.importc: "tm_min".},
+          hour {.importc: "tm_hour".},
+          monthday {.importc: "tm_mday".},
+          month {.importc: "tm_mon".},
+          year {.importc: "tm_year".},
+          weekday {.importc: "tm_wday".},
+          yearday {.importc: "tm_yday".},
+          isdst {.importc: "tm_isdst".}: cint
+        when defined(linux) and defined(amd64):
+          gmtoff {.importc: "tm_gmtoff".}: clong
+          zone {.importc: "tm_zone".}: cstring
+  type
+    StructTmPtr = ptr StructTm
+
+  proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
+
+  proc toAdjTime(tm: StructTm): Time =
+    let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
+    result = Time(epochDay * secondsInDay)
+    result.inc tm.hour * secondsInHour
+    result.inc tm.minute * 60
+    result.inc tm.second
+
+  proc getStructTm(time: Time | int64): StructTm =
+    let timei64 = time.int64
+    var a =
+      if timei64 < low(CTime):
+        CTime(low(CTime))
+      elif timei64 > high(CTime):
+        CTime(high(CTime))
+      else:
+        CTime(timei64)
+    result = localtime(addr(a))[]
+
+  proc localZoneInfoFromUtc(time: Time): ZonedTime =
+    let tm = getStructTm(time)
+    let adjTime = tm.toAdjTime
+    result.adjTime = adjTime
+    result.utcOffset = (time.toUnix - adjTime.toUnix).int
+    result.isDst = tm.isdst > 0
+
+  proc localZoneInfoFromTz(adjTime: Time): ZonedTime  =
+    var adjTimei64 = adjTime.int64
+    let past = adjTimei64 - secondsInDay
+    var tm = getStructTm(past)
+    let pastOffset = past - tm.toAdjTime.int64
+
+    let future = adjTimei64 + secondsInDay
+    tm = getStructTm(future)
+    let futureOffset = future - tm.toAdjTime.int64
+
+    var utcOffset: int
+    if pastOffset == futureOffset:
+        utcOffset = pastOffset.int
+    else:
+      if pastOffset > futureOffset:
+        adjTimei64 -= secondsInHour
+
+      adjTimei64 += pastOffset
+      utcOffset = (adjTimei64 - getStructTm(adjTimei64).toAdjTime.int64).int
+
+    # This extra roundtrip is needed to normalize any impossible datetimes
+    # as a result of offset changes (normally due to dst)
+    let utcTime = adjTime.int64 + utcOffset
+    tm = getStructTm(utcTime)
+    result.adjTime = tm.toAdjTime
+    result.utcOffset = (utcTime - result.adjTime.int64).int
+    result.isDst = tm.isdst > 0
+
+proc utcZoneInfoFromUtc(time: Time): ZonedTime =
+  result.adjTime = time
+  result.utcOffset = 0
+  result.isDst = false
+
+proc utcZoneInfoFromTz(adjTime: Time): ZonedTime =
+  utcZoneInfoFromUtc(adjTime) # adjTime == time since we are in UTC
+
+proc utc*(): TimeZone =
+  ## Get the ``Timezone`` implementation for the UTC timezone.
+  ##
+  ## .. code-block:: nim
+  ##  doAssert now().utc.timezone == utc()
+  ##  doAssert utc().name == "Etc/UTC"
+  Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC")
+
+proc local*(): TimeZone =
+  ## Get the ``Timezone`` implementation for the local timezone.
+  ##
+  ## .. code-block:: nim
+  ##  doAssert now().timezone == local()
+  ##  doAssert local().name == "LOCAL"
+  Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL")
 
-proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.}
-  ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
+proc utc*(dt: DateTime): DateTime =
+  ## Shorthand for ``dt.inZone(utc())``.
+  dt.inZone(utc())
 
-proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
-  ## get the milliseconds from the start of the program. **Deprecated since
-  ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
+proc local*(dt: DateTime): DateTime =
+  ## Shorthand for ``dt.inZone(local())``.
+  dt.inZone(local())
+
+proc utc*(t: Time): DateTime =
+  ## Shorthand for ``t.inZone(utc())``.
+  t.inZone(utc())
+
+proc local*(t: Time): DateTime =
+  ## Shorthand for ``t.inZone(local())``.
+  t.inZone(local())
+
+proc getTime*(): Time {.tags: [TimeEffect], benign.}
+  ## Gets the current time as a ``Time`` with second resolution. Use epochTime for higher
+  ## resolution.
+
+proc now*(): DateTime {.tags: [TimeEffect], benign.} =
+  ## Get the current time as a  ``DateTime`` in the local timezone.
+  ##
+  ## Shorthand for ``getTime().local``.
+  getTime().local
 
 proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
                    years: int = 0): TimeInterval =
-  ## creates a new ``TimeInterval``.
+  ## Creates a new ``TimeInterval``.
   ##
   ## You can also use the convenience procedures called ``milliseconds``,
   ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
@@ -269,46 +522,33 @@ proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
   ## .. code-block:: nim
   ##
   ##     let day = initInterval(hours=24)
-  ##     let tomorrow = getTime() + day
-  ##     echo(tomorrow)
-  var carryO = 0
-  result.milliseconds = `mod`(milliseconds, 1000)
-  carryO = `div`(milliseconds, 1000)
-  result.seconds = `mod`(carryO + seconds, 60)
-  carryO = `div`(carryO + seconds, 60)
-  result.minutes = `mod`(carryO + minutes, 60)
-  carryO = `div`(carryO + minutes, 60)
-  result.hours = `mod`(carryO + hours, 24)
-  carryO = `div`(carryO + hours, 24)
-  result.days = carryO + days
-
-  result.months = `mod`(months, 12)
-  carryO = `div`(months, 12)
-  result.years = carryO + years
+  ##     let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
+  ##     doAssert $(dt + day) == "2000-01-02T12-00-00+00:00"
+  result.milliseconds = milliseconds
+  result.seconds = seconds
+  result.minutes = minutes
+  result.hours = hours
+  result.days = days
+  result.months = months
+  result.years = years
 
 proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Adds two ``TimeInterval`` objects together.
-  var carryO = 0
-  result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
-  carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
-  result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
-  carryO = `div`(carryO + ti1.seconds + ti2.seconds, 60)
-  result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
-  carryO = `div`(carryO + ti1.minutes + ti2.minutes, 60)
-  result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
-  carryO = `div`(carryO + ti1.hours + ti2.hours, 24)
-  result.days = carryO + ti1.days + ti2.days
-
-  result.months = `mod`(ti1.months + ti2.months, 12)
-  carryO = `div`(ti1.months + ti2.months, 12)
-  result.years = carryO + ti1.years + ti2.years
+  result.milliseconds = ti1.milliseconds + ti2.milliseconds
+  result.seconds = ti1.seconds + ti2.seconds
+  result.minutes = ti1.minutes + ti2.minutes
+  result.hours = ti1.hours + ti2.hours
+  result.days = ti1.days + ti2.days
+  result.months = ti1.months + ti2.months
+  result.years = ti1.years + ti2.years
 
 proc `-`*(ti: TimeInterval): TimeInterval =
   ## Reverses a time interval
+  ##
   ## .. code-block:: nim
   ##
   ##     let day = -initInterval(hours=24)
-  ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: 0, days: -1, months: 0, years: 0)
+  ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: -24, days: 0, months: 0, years: 0)
   result = TimeInterval(
     milliseconds: -ti.milliseconds,
     seconds: -ti.seconds,
@@ -325,123 +565,100 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Time components are compared one-by-one, see output:
   ##
   ## .. code-block:: nim
-  ##     let a = fromSeconds(1_000_000_000)
-  ##     let b = fromSeconds(1_500_000_000)
+  ##     let a = fromUnix(1_000_000_000)
+  ##     let b = fromUnix(1_500_000_000)
   ##     echo b.toTimeInterval - a.toTimeInterval
-  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: -2, months: -2, years: 16)
+  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
   result = ti1 + (-ti2)
 
-proc isLeapYear*(year: int): bool =
-  ## returns true if ``year`` is a leap year
-
-  if year mod 400 == 0:
-    return true
-  elif year mod 100 == 0:
-    return false
-  elif year mod 4 == 0:
-    return true
-  else:
-    return false
-
-proc getDaysInMonth*(month: Month, year: int): int =
-  ## Get the number of days in a ``month`` of a ``year``
+proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDiff, absDiff: int64] =
+  ## Evaluates how many seconds the interval is worth
+  ## in the context of ``dt``.
+  ## The result in split into an adjusted diff and an absolute diff.
 
-  # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
-  case month
-  of mFeb: result = if isLeapYear(year): 29 else: 28
-  of mApr, mJun, mSep, mNov: result = 30
-  else: result = 31
-
-proc getDaysInYear*(year: int): int =
-  ## Get the number of days in a ``year``
-  result = 365 + (if isLeapYear(year): 1 else: 0)
-
-proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
-  ## Calculates how many seconds the interval is worth by adding up
-  ## all the fields
-
-  var anew = a
+  var anew = dt
   var newinterv = interval
-  result = 0
 
   newinterv.months += interval.years * 12
   var curMonth = anew.month
-  if newinterv.months < 0:   # subtracting
+  # Subtracting
+  if newinterv.months < 0:
     for mth in countDown(-1 * newinterv.months, 1):
-      result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
       if curMonth == mJan:
         curMonth = mDec
         anew.year.dec()
       else:
         curMonth.dec()
-  else:  # adding
+      result.adjDiff -= getDaysInMonth(curMonth, anew.year) * secondsInDay
+  # Adding
+  else:
     for mth in 1 .. newinterv.months:
-      result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
+      result.adjDiff += getDaysInMonth(curMonth, anew.year) * secondsInDay
       if curMonth == mDec:
         curMonth = mJan
         anew.year.inc()
       else:
         curMonth.inc()
-  result += float(newinterv.days * 24 * 60 * 60)
-  result += float(newinterv.hours * 60 * 60)
-  result += float(newinterv.minutes * 60)
-  result += float(newinterv.seconds)
-  result += newinterv.milliseconds / 1000
-
-proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## adds ``interval`` time from TimeInfo ``a``.
+  result.adjDiff += newinterv.days * secondsInDay
+  result.absDiff += newinterv.hours * secondsInHour
+  result.absDiff += newinterv.minutes * secondsInMin
+  result.absDiff += newinterv.seconds
+  result.absDiff += newinterv.milliseconds div 1000
+
+proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
+  ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
+  ## in the order of their size, i.e first the ``years`` component, then the ``months``
+  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
   ##
-  ## **Note:** This has been only briefly tested and it may not be
-  ## very accurate.
-  let t = toSeconds(toTime(a))
-  let secs = toSeconds(a, interval)
-  if a.timezone == 0:
-    result = getGMTime(fromSeconds(t + secs))
-  else:
-    result = getLocalTime(fromSeconds(t + secs))
-
-proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## subtracts ``interval`` time from TimeInfo ``a``.
+  ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
+  ## month doesn't have enough days it, the month will be incremented and the monthday will be
+  ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
+  ## which will overflow and result in `1 December`.
   ##
-  ## **Note:** This has been only briefly tested, it is inaccurate especially
-  ## when you subtract so much that you reach the Julian calendar.
-  let
-    t = toSeconds(toTime(a))
-    secs = toSeconds(a, -interval)
-  if a.timezone == 0:
-    result = getGMTime(fromSeconds(t + secs))
+  ## .. code-block:: nim
+  ##  let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
+  ##  doAssert $(dt + 1.months) == "2017-04-30T00:00:00+00:00"
+  ##  # This is correct and happens due to monthday overflow.
+  ##  doAssert $(dt - 1.months) == "2017-03-02T00:00:00+00:00"
+  let (adjDiff, absDiff) = evaluateInterval(dt, interval)
+
+  if adjDiff.int64 != 0:
+    let zInfo = dt.timezone.zoneInfoFromTz(Time(dt.toAdjTime.int64 + adjDiff))
+
+    if absDiff != 0:
+      let time = Time(zInfo.adjTime.int64 + zInfo.utcOffset + absDiff)
+      result = initDateTime(dt.timezone.zoneInfoFromUtc(time), dt.timezone)
+    else:
+      result = initDateTime(zInfo, dt.timezone)
   else:
-    result = getLocalTime(fromSeconds(t + secs))
+    result = initDateTime(dt.timezone.zoneInfoFromUtc(Time(dt.toTime.int64 + absDiff)), dt.timezone)
 
-proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
-
-proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
-  ## An alias for a misspelled field in ``TimeInterval``.
-  ##
-  ## **Warning:** This should not be used! It will be removed in the next
-  ## version.
-  t.milliseconds = milliseconds
+proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
+  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
+  ## in the order of their size, i.e first the ``years`` component, then the ``months``
+  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
+  dt + (-interval)
 
 proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## gets the current date as a string of the format ``YYYY-MM-DD``.
-  var ti = getLocalTime(getTime())
-  result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
+  ## Gets the current date as a string of the format ``YYYY-MM-DD``.
+  var ti = now()
+  result = $ti.year & '-' & intToStr(ord(ti.month), 2) &
     '-' & intToStr(ti.monthday, 2)
 
 proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## gets the current clock time as a string of the format ``HH:MM:SS``.
-  var ti = getLocalTime(getTime())
+  ## Gets the current clock time as a string of the format ``HH:MM:SS``.
+  var ti = now()
   result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
     ':' & intToStr(ti.second, 2)
 
 proc `$`*(day: WeekDay): string =
-  ## stingify operator for ``WeekDay``.
+  ## Stringify operator for ``WeekDay``.
   const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
      "Thursday", "Friday", "Saturday", "Sunday"]
   return lookup[day]
 
 proc `$`*(m: Month): string =
-  ## stingify operator for ``Month``.
+  ## Stringify operator for ``Month``.
   const lookup: array[Month, string] = ["January", "February", "March",
       "April", "May", "June", "July", "August", "September", "October",
       "November", "December"]
@@ -450,74 +667,68 @@ proc `$`*(m: Month): string =
 proc milliseconds*(ms: int): TimeInterval {.inline.} =
   ## TimeInterval of `ms` milliseconds
   ##
-  ## Note: not all time functions have millisecond resolution
-  initInterval(`mod`(ms,1000), `div`(ms,1000))
+  ## Note: not all time procedures have millisecond resolution
+  initInterval(milliseconds = ms)
 
 proc seconds*(s: int): TimeInterval {.inline.} =
   ## TimeInterval of `s` seconds
   ##
   ## ``echo getTime() + 5.second``
-  initInterval(0,`mod`(s,60), `div`(s,60))
+  initInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
   ## TimeInterval of `m` minutes
   ##
   ## ``echo getTime() + 5.minutes``
-  initInterval(0,0,`mod`(m,60), `div`(m,60))
+  initInterval(minutes = m)
 
 proc hours*(h: int): TimeInterval {.inline.} =
   ## TimeInterval of `h` hours
   ##
   ## ``echo getTime() + 2.hours``
-  initInterval(0,0,0,`mod`(h,24),`div`(h,24))
+  initInterval(hours = h)
 
 proc days*(d: int): TimeInterval {.inline.} =
   ## TimeInterval of `d` days
   ##
   ## ``echo getTime() + 2.days``
-  initInterval(0,0,0,0,d)
+  initInterval(days = d)
 
 proc months*(m: int): TimeInterval {.inline.} =
   ## TimeInterval of `m` months
   ##
   ## ``echo getTime() + 2.months``
-  initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
+  initInterval(months = m)
 
 proc years*(y: int): TimeInterval {.inline.} =
   ## TimeInterval of `y` years
   ##
   ## ``echo getTime() + 2.years``
-  initInterval(0,0,0,0,0,0,y)
+  initInterval(years = y)
 
-proc `+=`*(t: var Time, ti: TimeInterval) =
-  ## modifies `t` by adding the interval `ti`
-  t = toTime(getLocalTime(t) + ti)
+proc `+=`*(time: var Time, interval: TimeInterval) =
+  ## Modifies `time` by adding `interval`.
+  time = toTime(time.local + interval)
 
-proc `+`*(t: Time, ti: TimeInterval): Time =
-  ## adds the interval `ti` to Time `t`
-  ## by converting to localTime, adding the interval, and converting back
+proc `+`*(time: Time, interval: TimeInterval): Time =
+  ## Adds `interval` to `time`
+  ## by converting to a ``DateTime`` in the local timezone,
+  ## adding the interval, and converting back to ``Time``.
   ##
   ## ``echo getTime() + 1.day``
-  result = toTime(getLocalTime(t) + ti)
+  result = toTime(time.local + interval)
 
-proc `-=`*(t: var Time, ti: TimeInterval) =
-  ## modifies `t` by subtracting the interval `ti`
-  t = toTime(getLocalTime(t) - ti)
+proc `-=`*(time: var Time, interval: TimeInterval) =
+  ## Modifies `time` by subtracting `interval`.
+  time = toTime(time.local - interval)
 
-proc `-`*(t: Time, ti: TimeInterval): Time =
-  ## subtracts the interval `ti` from Time `t`
+proc `-`*(time: Time, interval: TimeInterval): Time =
+  ## Subtracts `interval` from Time `time`.
   ##
   ## ``echo getTime() - 1.day``
-  result = toTime(getLocalTime(t) - ti)
+  result = toTime(time.local - interval)
 
-const
-  secondsInMin = 60
-  secondsInHour = 60*60
-  secondsInDay = 60*60*24
-  minutesInHour = 60
-  epochStartYear = 1970
-
-proc formatToken(info: TimeInfo, token: string, buf: var string) =
+proc formatToken(dt: DateTime, token: string, buf: var string) =
   ## Helper of the format proc to parse individual tokens.
   ##
   ## Pass the found token in the user input string, and the buffer where the
@@ -525,96 +736,96 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
   ## formatting tokens require modifying the previous characters.
   case token
   of "d":
-    buf.add($info.monthday)
+    buf.add($dt.monthday)
   of "dd":
-    if info.monthday < 10:
+    if dt.monthday < 10:
       buf.add("0")
-    buf.add($info.monthday)
+    buf.add($dt.monthday)
   of "ddd":
-    buf.add(($info.weekday)[0 .. 2])
+    buf.add(($dt.weekday)[0 .. 2])
   of "dddd":
-    buf.add($info.weekday)
+    buf.add($dt.weekday)
   of "h":
-    buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
+    buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour))
   of "hh":
-    let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
+    let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour
     if amerHour < 10:
       buf.add('0')
     buf.add($amerHour)
   of "H":
-    buf.add($info.hour)
+    buf.add($dt.hour)
   of "HH":
-    if info.hour < 10:
+    if dt.hour < 10:
       buf.add('0')
-    buf.add($info.hour)
+    buf.add($dt.hour)
   of "m":
-    buf.add($info.minute)
+    buf.add($dt.minute)
   of "mm":
-    if info.minute < 10:
+    if dt.minute < 10:
       buf.add('0')
-    buf.add($info.minute)
+    buf.add($dt.minute)
   of "M":
-    buf.add($(int(info.month)+1))
+    buf.add($ord(dt.month))
   of "MM":
-    if info.month < mOct:
+    if dt.month < mOct:
       buf.add('0')
-    buf.add($(int(info.month)+1))
+    buf.add($ord(dt.month))
   of "MMM":
-    buf.add(($info.month)[0..2])
+    buf.add(($dt.month)[0..2])
   of "MMMM":
-    buf.add($info.month)
+    buf.add($dt.month)
   of "s":
-    buf.add($info.second)
+    buf.add($dt.second)
   of "ss":
-    if info.second < 10:
+    if dt.second < 10:
       buf.add('0')
-    buf.add($info.second)
+    buf.add($dt.second)
   of "t":
-    if info.hour >= 12:
+    if dt.hour >= 12:
       buf.add('P')
     else: buf.add('A')
   of "tt":
-    if info.hour >= 12:
+    if dt.hour >= 12:
       buf.add("PM")
     else: buf.add("AM")
   of "y":
-    var fr = ($info.year).len()-1
+    var fr = ($dt.year).len()-1
     if fr < 0: fr = 0
-    buf.add(($info.year)[fr .. ($info.year).len()-1])
+    buf.add(($dt.year)[fr .. ($dt.year).len()-1])
   of "yy":
-    var fr = ($info.year).len()-2
+    var fr = ($dt.year).len()-2
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
     buf.add(fyear)
   of "yyy":
-    var fr = ($info.year).len()-3
+    var fr = ($dt.year).len()-3
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
     buf.add(fyear)
   of "yyyy":
-    var fr = ($info.year).len()-4
+    var fr = ($dt.year).len()-4
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
     buf.add(fyear)
   of "yyyyy":
-    var fr = ($info.year).len()-5
+    var fr = ($dt.year).len()-5
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
     buf.add(fyear)
   of "z":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
     if nonDstTz <= 0: buf.add('+')
     else: buf.add('-')
     buf.add($hours)
   of "zz":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
     if nonDstTz <= 0: buf.add('+')
     else: buf.add('-')
@@ -622,7 +833,7 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
     buf.add($hours)
   of "zzz":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
       minutes = (abs(nonDstTz) div secondsInMin) mod minutesInHour
     if nonDstTz <= 0: buf.add('+')
@@ -638,8 +849,8 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
     raise newException(ValueError, "Invalid format string: " & token)
 
 
-proc format*(info: TimeInfo, f: string): string =
-  ## This function formats `info` as specified by `f`. The following format
+proc format*(dt: DateTime, f: string): string {.tags: [].}=
+  ## This procedure formats `dt` as specified by `f`. The following format
   ## specifiers are available:
   ##
   ## ==========  =================================================================================  ================================================
@@ -683,7 +894,7 @@ proc format*(info: TimeInfo, f: string): string =
   while true:
     case f[i]
     of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
-      formatToken(info, currentF, result)
+      formatToken(dt, currentF, result)
 
       currentF = ""
       if f[i] == '\0': break
@@ -700,187 +911,187 @@ proc format*(info: TimeInfo, f: string): string =
       if currentF.len < 1 or currentF[high(currentF)] == f[i]:
         currentF.add(f[i])
       else:
-        formatToken(info, currentF, result)
+        formatToken(dt, currentF, result)
         dec(i) # Move position back to re-process the character separately.
         currentF = ""
 
     inc(i)
 
-proc `$`*(timeInfo: TimeInfo): string {.tags: [], raises: [], benign.} =
-  ## converts a `TimeInfo` object to a string representation.
+proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
+  ## Converts a `DateTime` object to a string representation.
   ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
   try:
-    result = format(timeInfo, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this
+    result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this
   except ValueError: assert false # cannot happen because format string is valid
 
-proc `$`*(time: Time): string {.tags: [TimeEffect], raises: [], benign.} =
+proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
   ## converts a `Time` value to a string representation. It will use the local
   ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
-  $getLocalTime(time)
+  $time.local
 
 {.pop.}
 
-proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
+proc parseToken(dt: var DateTime; token, value: string; j: var int) =
   ## Helper of the parse proc to parse individual tokens.
   var sv: int
   case token
   of "d":
     var pd = parseInt(value[j..j+1], sv)
-    info.monthday = sv
+    dt.monthday = sv
     j += pd
   of "dd":
-    info.monthday = value[j..j+1].parseInt()
+    dt.monthday = value[j..j+1].parseInt()
     j += 2
   of "ddd":
     case value[j..j+2].toLowerAscii()
-    of "sun": info.weekday = dSun
-    of "mon": info.weekday = dMon
-    of "tue": info.weekday = dTue
-    of "wed": info.weekday = dWed
-    of "thu": info.weekday = dThu
-    of "fri": info.weekday = dFri
-    of "sat": info.weekday = dSat
+    of "sun": dt.weekday = dSun
+    of "mon": dt.weekday = dMon
+    of "tue": dt.weekday = dTue
+    of "wed": dt.weekday = dWed
+    of "thu": dt.weekday = dThu
+    of "fri": dt.weekday = dFri
+    of "sat": dt.weekday = dSat
     else:
       raise newException(ValueError,
         "Couldn't parse day of week (ddd), got: " & value[j..j+2])
     j += 3
   of "dddd":
     if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
-      info.weekday = dSun
+      dt.weekday = dSun
       j += 6
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("monday") == 0:
-      info.weekday = dMon
+      dt.weekday = dMon
       j += 6
     elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("tuesday") == 0:
-      info.weekday = dTue
+      dt.weekday = dTue
       j += 7
     elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("wednesday") == 0:
-      info.weekday = dWed
+      dt.weekday = dWed
       j += 9
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("thursday") == 0:
-      info.weekday = dThu
+      dt.weekday = dThu
       j += 8
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("friday") == 0:
-      info.weekday = dFri
+      dt.weekday = dFri
       j += 6
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("saturday") == 0:
-      info.weekday = dSat
+      dt.weekday = dSat
       j += 8
     else:
       raise newException(ValueError,
         "Couldn't parse day of week (dddd), got: " & value)
   of "h", "H":
     var pd = parseInt(value[j..j+1], sv)
-    info.hour = sv
+    dt.hour = sv
     j += pd
   of "hh", "HH":
-    info.hour = value[j..j+1].parseInt()
+    dt.hour = value[j..j+1].parseInt()
     j += 2
   of "m":
     var pd = parseInt(value[j..j+1], sv)
-    info.minute = sv
+    dt.minute = sv
     j += pd
   of "mm":
-    info.minute = value[j..j+1].parseInt()
+    dt.minute = value[j..j+1].parseInt()
     j += 2
   of "M":
     var pd = parseInt(value[j..j+1], sv)
-    info.month = Month(sv-1)
+    dt.month = sv.Month
     j += pd
   of "MM":
     var month = value[j..j+1].parseInt()
     j += 2
-    info.month = Month(month-1)
+    dt.month = month.Month
   of "MMM":
     case value[j..j+2].toLowerAscii():
-    of "jan": info.month =  mJan
-    of "feb": info.month =  mFeb
-    of "mar": info.month =  mMar
-    of "apr": info.month =  mApr
-    of "may": info.month =  mMay
-    of "jun": info.month =  mJun
-    of "jul": info.month =  mJul
-    of "aug": info.month =  mAug
-    of "sep": info.month =  mSep
-    of "oct": info.month =  mOct
-    of "nov": info.month =  mNov
-    of "dec": info.month =  mDec
+    of "jan": dt.month =  mJan
+    of "feb": dt.month =  mFeb
+    of "mar": dt.month =  mMar
+    of "apr": dt.month =  mApr
+    of "may": dt.month =  mMay
+    of "jun": dt.month =  mJun
+    of "jul": dt.month =  mJul
+    of "aug": dt.month =  mAug
+    of "sep": dt.month =  mSep
+    of "oct": dt.month =  mOct
+    of "nov": dt.month =  mNov
+    of "dec": dt.month =  mDec
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMM), got: " & value)
     j += 3
   of "MMMM":
     if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
-      info.month =  mJan
+      dt.month =  mJan
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
-      info.month =  mFeb
+      dt.month =  mFeb
       j += 8
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
-      info.month =  mMar
+      dt.month =  mMar
       j += 5
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
-      info.month =  mApr
+      dt.month =  mApr
       j += 5
     elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
-      info.month =  mMay
+      dt.month =  mMay
       j += 3
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
-      info.month =  mJun
+      dt.month =  mJun
       j += 4
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
-      info.month =  mJul
+      dt.month =  mJul
       j += 4
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
-      info.month =  mAug
+      dt.month =  mAug
       j += 6
     elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
-      info.month =  mSep
+      dt.month =  mSep
       j += 9
     elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
-      info.month =  mOct
+      dt.month =  mOct
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
-      info.month =  mNov
+      dt.month =  mNov
       j += 8
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
-      info.month =  mDec
+      dt.month =  mDec
       j += 8
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMMM), got: " & value)
   of "s":
     var pd = parseInt(value[j..j+1], sv)
-    info.second = sv
+    dt.second = sv
     j += pd
   of "ss":
-    info.second = value[j..j+1].parseInt()
+    dt.second = value[j..j+1].parseInt()
     j += 2
   of "t":
-    if value[j] == 'P' and info.hour > 0 and info.hour < 12:
-      info.hour += 12
+    if value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
+      dt.hour += 12
     j += 1
   of "tt":
-    if value[j..j+1] == "PM" and info.hour > 0 and info.hour < 12:
-      info.hour += 12
+    if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
+      dt.hour += 12
     j += 2
   of "yy":
     # Assumes current century
     var year = value[j..j+1].parseInt()
-    var thisCen = getLocalTime(getTime()).year div 100
-    info.year = thisCen*100 + year
+    var thisCen = now().year div 100
+    dt.year = thisCen*100 + year
     j += 2
   of "yyyy":
-    info.year = value[j..j+3].parseInt()
+    dt.year = value[j..j+3].parseInt()
     j += 4
   of "z":
-    info.isDST = false
+    dt.isDst = false
     if value[j] == '+':
-      info.timezone = 0 - parseInt($value[j+1]) * secondsInHour
+      dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
     elif value[j] == '-':
-      info.timezone = parseInt($value[j+1]) * secondsInHour
+      dt.utcOffset = parseInt($value[j+1]) * secondsInHour
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
@@ -888,13 +1099,13 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
         "Couldn't parse timezone offset (z), got: " & value[j])
     j += 2
   of "zz":
-    info.isDST = false
+    dt.isDst = false
     if value[j] == '+':
-      info.timezone = 0 - value[j+1..j+2].parseInt() * secondsInHour
+      dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
     elif value[j] == '-':
-      info.timezone = value[j+1..j+2].parseInt() * secondsInHour
+      dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
@@ -902,31 +1113,33 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
         "Couldn't parse timezone offset (zz), got: " & value[j])
     j += 3
   of "zzz":
-    info.isDST = false
+    dt.isDst = false
     var factor = 0
     if value[j] == '+': factor = -1
     elif value[j] == '-': factor = 1
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
       raise newException(ValueError,
         "Couldn't parse timezone offset (zzz), got: " & value[j])
-    info.timezone = factor * value[j+1..j+2].parseInt() * secondsInHour
+    dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
     j += 4
-    info.timezone += factor * value[j..j+1].parseInt() * 60
+    dt.utcOffset += factor * value[j..j+1].parseInt() * 60
     j += 2
   else:
     # Ignore the token and move forward in the value string by the same length
     j += token.len
 
-proc parse*(value, layout: string): TimeInfo =
-  ## This function parses a date/time string using the standard format
-  ## identifiers as listed below. The function defaults information not provided
-  ## in the format string from the running program (timezone, month, year, etc).
-  ## Daylight saving time is only set if no timezone is given and the given date
-  ## lies within the DST period of the current locale.
+proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
+  ## This procedure parses a date/time string using the standard format
+  ## identifiers as listed below. The procedure defaults information not provided
+  ## in the format string from the running program (month, year, etc).
+  ##
+  ## The return value will always be in the `zone` timezone. If no UTC offset was
+  ## parsed, then the input will be assumed to be specified in the `zone` timezone
+  ## already, so no timezone conversion will be done in that case.
   ##
   ## ==========  =================================================================================  ================================================
   ## Specifier   Description                                                                        Example
@@ -965,17 +1178,17 @@ proc parse*(value, layout: string): TimeInfo =
   var j = 0 # pointer for value string
   var token = ""
   # Assumes current day of month, month and year, but time is reset to 00:00:00. Weekday will be reset after parsing.
-  var info = getLocalTime(getTime())
-  info.hour = 0
-  info.minute = 0
-  info.second = 0
-  info.isDST = true # using this is flag for checking whether a timezone has \
+  var dt = now()
+  dt.hour = 0
+  dt.minute = 0
+  dt.second = 0
+  dt.isDst = true # using this is flag for checking whether a timezone has \
       # been read (because DST is always false when a tz is parsed)
   while true:
     case layout[i]
     of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
       if token.len > 0:
-        parseToken(info, token, value, j)
+        parseToken(dt, token, value, j)
       # Reset token
       token = ""
       # Break if at end of line
@@ -997,26 +1210,15 @@ proc parse*(value, layout: string): TimeInfo =
         token.add(layout[i])
         inc(i)
       else:
-        parseToken(info, token, value, j)
+        parseToken(dt, token, value, j)
         token = ""
 
-  if info.isDST:
-    # means that no timezone has been parsed. In this case, we need to check
-    # whether the date is within DST of the local time.
-    let tmp = getLocalTime(toTime(info))
-    # correctly set isDST so that the following step works on the correct time
-    info.isDST = tmp.isDST
-
-  # Correct weekday and yearday; transform timestamp to local time.
-  # There currently is no way of returning this with the original (parsed)
-  # timezone while also setting weekday and yearday (we are depending on stdlib
-  # to provide this calculation).
-  return getLocalTime(toTime(info))
-
-# Leap year calculations are adapted from:
-# http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years
-# The dayOfTheWeek procs are adapated from:
-# http://stason.org/TULARC/society/calendars/2-5-What-day-of-the-week-was-2-August-1953.html
+  if dt.isDst:
+    # No timezone parsed - assume timezone is `zone`
+    result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
+  else:
+    # Otherwise convert to `zone`
+    result = dt.toTime.inZone(zone)
 
 proc countLeapYears*(yearSpan: int): int =
   ## Returns the number of leap years spanned by a given number of years.
@@ -1025,7 +1227,7 @@ proc countLeapYears*(yearSpan: int): int =
   ## counts the number of leap years up to January 1st of a given year.
   ## Keep in mind that if specified year is a leap year, the leap day
   ## has not happened before January 1st of that year.
-  (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1) / 400)).int
+  (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400
 
 proc countDays*(yearSpan: int): int =
   ## Returns the number of days spanned by a given number of years.
@@ -1042,75 +1244,7 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
   result.years = days div 365
   result.days = days mod 365
 
-proc getDayOfWeek*(day, month, year: int): WeekDay =
-  ## Returns the day of the week enum from day, month and year.
-  # Day & month start from one.
-  let
-    a = (14 - month) div 12
-    y = year - a
-    m = month + (12*a) - 2
-    d = (day + y + (y div 4) - (y div 100) + (y div 400) + (31*m) div 12) mod 7
-  # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
-  # so we must correct for the WeekDay type.
-  if d == 0: return dSun
-  result = (d-1).WeekDay
-
-proc getDayOfWeekJulian*(day, month, year: int): WeekDay =
-  ## Returns the day of the week enum from day, month and year,
-  ## according to the Julian calendar.
-  # Day & month start from one.
-  let
-    a = (14 - month) div 12
-    y = year - a
-    m = month + (12*a) - 2
-    d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
-  result = d.WeekDay
-
-proc timeToTimeInfo*(t: Time): TimeInfo {.deprecated.} =
-  ## Converts a Time to TimeInfo.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``getLocalTime`` or ``getGMTime`` instead.
-  let
-    secs = t.toSeconds().int
-    daysSinceEpoch = secs div secondsInDay
-    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
-    daySeconds = secs mod secondsInDay
-
-    y = yearsSinceEpoch + epochStartYear
-
-  var
-    mon = mJan
-    days = daysRemaining
-    daysInMonth = getDaysInMonth(mon, y)
-
-  # calculate month and day remainder
-  while days > daysInMonth and mon <= mDec:
-    days -= daysInMonth
-    mon.inc
-    daysInMonth = getDaysInMonth(mon, y)
-
-  let
-    yd = daysRemaining
-    m = mon  # month is zero indexed enum
-    md = days
-    # NB: month is zero indexed but dayOfWeek expects 1 indexed.
-    wd = getDayOfWeek(days, mon.int + 1, y).Weekday
-    h = daySeconds div secondsInHour + 1
-    mi = (daySeconds mod secondsInHour) div secondsInMin
-    s = daySeconds mod secondsInMin
-  result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s)
-
-proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
-  ## Converts a Time to a TimeInterval.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``toTimeInterval`` instead.
-  # Milliseconds not available from Time
-  var tInfo = t.getLocalTime()
-  initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
-
-proc toTimeInterval*(t: Time): TimeInterval =
+proc toTimeInterval*(time: Time): TimeInterval =
   ## Converts a Time to a TimeInterval.
   ##
   ## To be used when diffing times.
@@ -1121,10 +1255,25 @@ proc toTimeInterval*(t: Time): TimeInterval =
   ##     echo a, " ", b  # real dates
   ##     echo a.toTimeInterval  # meaningless value, don't use it by itself
   ##     echo b.toTimeInterval - a.toTimeInterval
-  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: -2, months: -2, years: 16)
+  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
   # Milliseconds not available from Time
-  var tInfo = t.getLocalTime()
-  initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
+  var dt = time.local
+  initInterval(0, dt.second, dt.minute, dt.hour, dt.monthday, dt.month.ord - 1, dt.year)
+
+proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange, zone: Timezone = local()): DateTime =
+  ## Create a new ``DateTime`` in the specified timezone.
+  assertValidDate monthday, month, year
+  doAssert monthday <= getDaysInMonth(month, year), "Invalid date: " & $month & " " & $monthday & ", " & $year
+  let dt = DateTime(
+    monthday:  monthday,
+    year:  year,
+    month:  month,
+    hour:  hour,
+    minute:  minute,
+    second:  second
+  )
+  result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
 
 when not defined(JS):
   proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
@@ -1145,160 +1294,39 @@ when not defined(JS):
     ##   doWork()
     ##   echo "CPU time [s] ", cpuTime() - t0
 
-when not defined(JS):
-  # C wrapper:
-  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
-      defined(macosx):
-    type
-      StructTM {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        gmtoff {.importc: "tm_gmtoff".}: clong
-  else:
-    type
-      StructTM {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        when defined(linux) and defined(amd64):
-          gmtoff {.importc: "tm_gmtoff".}: clong
-          zone {.importc: "tm_zone".}: cstring
+when defined(JS):
+  proc getTime(): Time =
+    (newDate().getTime() div 1000).Time
+
+  proc epochTime*(): float {.tags: [TimeEffect].} =
+    newDate().getTime() / 1000
+
+else:
   type
-    TimeInfoPtr = ptr StructTM
     Clock {.importc: "clock_t".} = distinct int
 
-  when not defined(windows):
-    # This is not ANSI C, but common enough
-    proc timegm(t: StructTM): Time {.
-      importc: "timegm", header: "<time.h>", tags: [].}
-
-  proc localtime(timer: ptr Time): TimeInfoPtr {.
-    importc: "localtime", header: "<time.h>", tags: [].}
-  proc gmtime(timer: ptr Time): TimeInfoPtr {.
-    importc: "gmtime", header: "<time.h>", tags: [].}
-  proc timec(timer: ptr Time): Time {.
+  proc timec(timer: ptr CTime): CTime {.
     importc: "time", header: "<time.h>", tags: [].}
-  proc mktime(t: StructTM): Time {.
-    importc: "mktime", header: "<time.h>", tags: [].}
+
   proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
-  proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
-    tags: [].}
 
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
 
-  # our own procs on top of that:
-  proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
-    const
-      weekDays: array[0..6, WeekDay] = [
-        dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-    TimeInfo(second: int(tm.second),
-      minute: int(tm.minute),
-      hour: int(tm.hour),
-      monthday: int(tm.monthday),
-      month: Month(tm.month),
-      year: tm.year + 1900'i32,
-      weekday: weekDays[int(tm.weekday)],
-      yearday: int(tm.yearday),
-      isDST: tm.isdst > 0,
-      timezone: if local: getTimezone() else: 0
-    )
-
-
-  proc timeInfoToTM(t: TimeInfo): StructTM =
-    const
-      weekDays: array[WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
-    result.second = t.second
-    result.minute = t.minute
-    result.hour = t.hour
-    result.monthday = t.monthday
-    result.month = ord(t.month)
-    result.year = cint(t.year - 1900)
-    result.weekday = weekDays[t.weekday]
-    result.yearday = t.yearday
-    result.isdst = if t.isDST: 1 else: 0
-
-  when not defined(useNimRtl):
-    proc `-` (a, b: Time): int64 =
-      return toBiggestInt(difftime(a, b))
-
-  proc getStartMilsecs(): int =
-    #echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
-    #return getClock() div (clocksPerSec div 1000)
-    when defined(macosx):
-      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
-    else:
-      result = int(getClock()) div (clocksPerSec div 1000)
-    when false:
-      var a: Timeval
-      posix_gettimeofday(a)
-      result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
-      #echo "result: ", result
-
-  proc getTime(): Time = return timec(nil)
-  proc getLocalTime(t: Time): TimeInfo =
-    var a = t
-    let lt = localtime(addr(a))
-    assert(not lt.isNil)
-    result = tmToTimeInfo(lt[], true)
-    # copying is needed anyway to provide reentrancity; thus
-    # the conversion is not expensive
-
-  proc getGMTime(t: Time): TimeInfo =
-    var a = t
-    result = tmToTimeInfo(gmtime(addr(a))[], false)
-    # copying is needed anyway to provide reentrancity; thus
-    # the conversion is not expensive
-
-  proc toTime(timeInfo: TimeInfo): Time =
-    var cTimeInfo = timeInfo # for C++ we have to make a copy
-    # because the header of mktime is broken in my version of libc
-
-    result = mktime(timeInfoToTM(cTimeInfo))
-    # mktime is defined to interpret the input as local time. As timeInfoToTM
-    # does ignore the timezone, we need to adjust this here.
-    result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
-
-  proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
+  proc getTime(): Time =
+    timec(nil).Time
 
   const
     epochDiff = 116444736000000000'i64
     rateDiff = 10000000'i64 # 100 nsecs
 
-  proc unixTimeToWinTime*(t: Time): int64 =
+  proc unixTimeToWinTime*(time: CTime): int64 =
     ## converts a UNIX `Time` (``time_t``) to a Windows file time
-    result = int64(t) * rateDiff + epochDiff
+    result = int64(time) * rateDiff + epochDiff
 
-  proc winTimeToUnixTime*(t: int64): Time =
+  proc winTimeToUnixTime*(time: int64): CTime =
     ## converts a Windows time to a UNIX `Time` (``time_t``)
-    result = Time((t - epochDiff) div rateDiff)
-
-  proc getTimezone(): int =
-    when defined(freebsd) or defined(netbsd) or defined(openbsd):
-      var a = timec(nil)
-      let lt = localtime(addr(a))
-      # BSD stores in `gmtoff` offset east of UTC in seconds,
-      # but posix systems using west of UTC in seconds
-      return -(lt.gmtoff)
-    else:
-      return timezone
-
-  proc fromSeconds(since1970: float): Time = Time(since1970)
-
-  proc toSeconds(time: Time): float = float(time)
+    result = CTime((time - epochDiff) div rateDiff)
 
   when not defined(useNimRtl):
     proc epochTime(): float =
@@ -1319,83 +1347,138 @@ when not defined(JS):
     proc cpuTime(): float =
       result = toFloat(int(getClock())) / toFloat(clocksPerSec)
 
-elif defined(JS):
-  proc newDate(): Time {.importc: "new Date".}
-  proc internGetTime(): Time {.importc: "new Date", tags: [].}
+# Deprecated procs
 
-  proc newDate(value: float): Time {.importc: "new Date".}
-  proc newDate(value: cstring): Time {.importc: "new Date".}
-  proc getTime(): Time =
-    # Warning: This is something different in JS.
-    return newDate()
+proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign, deprecated.} =
+  ## Takes a float which contains the number of seconds since the unix epoch and
+  ## returns a time object.
+  ##
+  ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
+  Time(since1970)
 
-  const
-    weekDays: array[0..6, WeekDay] = [
-      dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-
-  proc getLocalTime(t: Time): TimeInfo =
-    result.second = t.getSeconds()
-    result.minute = t.getMinutes()
-    result.hour = t.getHours()
-    result.monthday = t.getDate()
-    result.month = Month(t.getMonth())
-    result.year = t.getFullYear()
-    result.weekday = weekDays[t.getDay()]
-    result.timezone = getTimezone()
-
-    result.yearday = result.monthday - 1
-    for month in mJan..<result.month:
-      result.yearday += getDaysInMonth(month, result.year)
-
-  proc getGMTime(t: Time): TimeInfo =
-    result.second = t.getUTCSeconds()
-    result.minute = t.getUTCMinutes()
-    result.hour = t.getUTCHours()
-    result.monthday = t.getUTCDate()
-    result.month = Month(t.getUTCMonth())
-    result.year = t.getUTCFullYear()
-    result.weekday = weekDays[t.getUTCDay()]
-
-    result.yearday = result.monthday - 1
-    for month in mJan..<result.month:
-      result.yearday += getDaysInMonth(month, result.year)
-
-  proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
-
-  proc toTime*(timeInfo: TimeInfo): Time = newDate($timeInfo)
-
-  proc `-` (a, b: Time): int64 =
-    return a.getTime() - b.getTime()
+proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprecated.} =
+  ## Takes an int which contains the number of seconds since the unix epoch and
+  ## returns a time object.
+  ##
+  ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
+  Time(since1970)
 
-  var
-    startMilsecs = getTime()
+proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
+  ## Returns the time in seconds since the unix epoch.
+  ##
+  ## **Deprecated since v0.18.0:** use ``toUnix`` instead
+  float(time)
+
+proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+  ## Converts the calendar time `time` to broken-time representation,
+  ## expressed relative to the user's specified time zone.
+  ##
+  ## **Deprecated since v0.18.0:** use ``local`` instead
+  time.local
 
-  proc getStartMilsecs(): int =
-    ## get the milliseconds from the start of the program
-    return int(getTime() - startMilsecs)
+proc getGMTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+  ## Converts the calendar time `time` to broken-down time representation,
+  ## expressed in Coordinated Universal Time (UTC). 
+  ##
+  ## **Deprecated since v0.18.0:** use ``utc`` instead
+  time.utc
 
-  proc fromSeconds(since1970: float): Time = result = newDate(since1970 * 1000)
+proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} =
+  ## Returns the offset of the local (non-DST) timezone in seconds west of UTC.
+  ##
+  ## **Deprecated since v0.18.0:** use ``now().utcOffset`` to get the current
+  ## utc offset (including DST).
+  when defined(JS):
+    return newDate().getTimezoneOffset() * 60
+  elif defined(freebsd) or defined(netbsd) or defined(openbsd):
+    var a = timec(nil)
+    let lt = localtime(addr(a))
+    # BSD stores in `gmtoff` offset east of UTC in seconds,
+    # but posix systems using west of UTC in seconds
+    return -(lt.gmtoff)
+  else:
+    return timezone
 
-  proc toSeconds(time: Time): float = result = time.getTime() / 1000
+proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
+  ## Converts a broken-down time structure to calendar time representation.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``toTime`` instead.
+  dt.toTime
+
+when defined(JS):
+  var startMilsecs = getTime()
+  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
+    ## get the milliseconds from the start of the program. **Deprecated since
+    ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
+    when defined(JS):
+      ## get the milliseconds from the start of the program
+      return int(getTime() - startMilsecs)
+else:
+  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
+    when defined(macosx):
+      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
+    else:
+      result = int(getClock()) div (clocksPerSec div 1000)
+
+proc miliseconds*(t: TimeInterval): int {.deprecated.} =
+  t.milliseconds
+
+proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
+  ## Converts a Time to a TimeInterval.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``toTimeInterval`` instead.
+  # Milliseconds not available from Time
+  t.toTimeInterval()
 
-  proc getTimezone(): int = result = newDate().getTimezoneOffset() * 60
+proc timeToTimeInfo*(t: Time): DateTime {.deprecated.} =
+  ## Converts a Time to DateTime.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``inZone`` instead.
+  const epochStartYear = 1970
 
-  proc epochTime*(): float {.tags: [TimeEffect].} = newDate().toSeconds()
+  let
+    secs = t.toSeconds().int
+    daysSinceEpoch = secs div secondsInDay
+    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
+    daySeconds = secs mod secondsInDay
 
+    y = yearsSinceEpoch + epochStartYear
 
-when isMainModule:
-  # this is testing non-exported function
   var
-    t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
-    t4L = getLocalTime(fromSeconds(876124714))
-  assert toSeconds(t4, initInterval(seconds=0)) == 0.0
-  assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
-  assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
-  assert toSeconds(t4L, initInterval(minutes=1)) == toSeconds(t4, initInterval(minutes=1))
-  assert toSeconds(t4L, initInterval(hours=1)) == toSeconds(t4, initInterval(hours=1))
-  assert toSeconds(t4L, initInterval(days=1)) == toSeconds(t4, initInterval(days=1))
-  assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
-  assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
-
-  # Further tests are in tests/stdlib/ttime.nim
-  # koch test c stdlib
+    mon = mJan
+    days = daysRemaining
+    daysInMonth = getDaysInMonth(mon, y)
+
+  # calculate month and day remainder
+  while days > daysInMonth and mon <= mDec:
+    days -= daysInMonth
+    mon.inc
+    daysInMonth = getDaysInMonth(mon, y)
+
+  let
+    yd = daysRemaining
+    m = mon  # month is zero indexed enum
+    md = days
+    # NB: month is zero indexed but dayOfWeek expects 1 indexed.
+    wd = getDayOfWeek(days, mon, y).Weekday
+    h = daySeconds div secondsInHour + 1
+    mi = (daySeconds mod secondsInHour) div secondsInMin
+    s = daySeconds mod secondsInMin
+  result = DateTime(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s)
+
+proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
+  getDayOfWeek(day, month.Month, year)
+
+proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
+  ## Returns the day of the week enum from day, month and year,
+  ## according to the Julian calendar.
+  # Day & month start from one.
+  let
+    a = (14 - month) div 12
+    y = year - a
+    m = month + (12*a) - 2
+    d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
+  result = d.WeekDay
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 55c4bf038..2047abda4 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -21,7 +21,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
   ##
   ##   proc `$`*(T: typedesc): string = name(T)
   ##
-  ##   template test(x): stmt =
+  ##   template test(x): typed =
   ##     echo "type: ", type(x), ", value: ", x
   ##
   ##   test 42
@@ -31,6 +31,10 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".}
   ##   test(@['A','B'])
   ##   # --> type: seq[char], value: @[A, B]
 
+proc `$`*(t: typedesc): string =
+  ## An alias for `name`.
+  name(t)
+
 proc arity*(t: typedesc): int {.magic: "TypeTrait".}
   ## Returns the arity of the given type
 
@@ -49,3 +53,15 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
   ## This trait is similar to `genericHead`, but instead of producing
   ## error for non-generic types, it will just return them unmodified
 
+proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
+  ## This trait returns true iff the type ``t`` is safe to use for
+  ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:.
+
+
+when isMainModule:
+  # echo type(42)
+  import streams
+  var ss = newStringStream()
+  ss.write($type(42)) # needs `$`
+  ss.setPosition(0)
+  doAssert ss.readAll() == "int"
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 7d9c3108b..257c620f7 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -293,33 +293,33 @@ proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
   if pos < 0:
     let (o, rl) = runeReverseOffset(s, -pos)
     if len >= rl:
-      result = s[o.. s.len-1]
+      result = s.substr(o, s.len-1)
     elif len < 0:
       let e = rl + len
       if e < 0:
         result = ""
       else:
-        result = s[o.. runeOffset(s, e-(rl+pos) , o)-1]
+        result = s.substr(o, runeOffset(s, e-(rl+pos) , o)-1)
     else:
-      result = s[o.. runeOffset(s, len, o)-1]
+      result = s.substr(o, runeOffset(s, len, o)-1)
   else:
     let o = runeOffset(s, pos)
     if o < 0:
       result = ""
     elif len == int.high:
-      result = s[o.. s.len-1]
+      result = s.substr(o, s.len-1)
     elif len < 0:
       let (e, rl) = runeReverseOffset(s, -len)
       discard rl
       if e <= 0:
         result = ""
       else:
-        result = s[o.. e-1]
+        result = s.substr(o, e-1)
     else:
       var e = runeOffset(s, len, o)
       if e < 0:
         e = s.len
-      result = s[o.. e-1]
+      result = s.substr(o, e-1)
 
 const
   alphaRanges = [
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 3772a213a..fbce087ff 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -21,13 +21,41 @@
 ## ``nim c -r <testfile.nim>`` exits with 0 or 1
 ##
 ## Running a single test
-## ---------------------
+## =====================
 ##
-## Simply specify the test name as a command line argument.
+## Specify the test name as a command line argument.
 ##
 ## .. code::
 ##
-##   nim c -r test "my super awesome test name"
+##   nim c -r test "my test name" "another test"
+##
+## Multiple arguments can be used.
+##
+## Running a single test suite
+## ===========================
+##
+## Specify the suite name delimited by ``"::"``.
+##
+## .. code::
+##
+##   nim c -r test "my test name::"
+##
+## Selecting tests by pattern
+## ==========================
+##
+## A single ``"*"`` can be used for globbing.
+##
+## Delimit the end of a suite name with ``"::"``.
+##
+## Tests matching **any** of the arguments are executed.
+##
+## .. code::
+##
+##   nim c -r test fast_suite::mytest1 fast_suite::mytest2
+##   nim c -r test "fast_suite::mytest*"
+##   nim c -r test "auth*::" "crypto::hashing*"
+##   # Run suites starting with 'bug #' and standalone tests starting with '#'
+##   nim c -r test 'bug #*::' '::#*'
 ##
 ## Example
 ## -------
@@ -121,7 +149,7 @@ var
 
   checkpoints {.threadvar.}: seq[string]
   formatters {.threadvar.}: seq[OutputFormatter]
-  testsToRun {.threadvar.}: HashSet[string]
+  testsFilters {.threadvar.}: HashSet[string]
 
 when declared(stdout):
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
@@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
 method suiteEnded*(formatter: JUnitOutputFormatter) =
   formatter.stream.writeLine("\t</testsuite>")
 
-proc shouldRun(testName: string): bool =
-  if testsToRun.len == 0:
+proc glob(matcher, filter: string): bool =
+  ## Globbing using a single `*`. Empty `filter` matches everything.
+  if filter.len == 0:
     return true
 
-  result = testName in testsToRun
+  if not filter.contains('*'):
+    return matcher == filter
+
+  let beforeAndAfter = filter.split('*', maxsplit=1)
+  if beforeAndAfter.len == 1:
+    # "foo*"
+    return matcher.startswith(beforeAndAfter[0])
+
+  if matcher.len < filter.len - 1:
+    return false  # "12345" should not match "123*345"
+
+  return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
+
+proc matchFilter(suiteName, testName, filter: string): bool =
+  if filter == "":
+    return true
+  if testName == filter:
+    # corner case for tests containing "::" in their name
+    return true
+  let suiteAndTestFilters = filter.split("::", maxsplit=1)
+
+  if suiteAndTestFilters.len == 1:
+    # no suite specified
+    let test_f = suiteAndTestFilters[0]
+    return glob(testName, test_f)
+
+  return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
+
+when defined(testing): export matchFilter
+
+proc shouldRun(currentSuiteName, testName: string): bool =
+  ## Check if a test should be run by matching suiteName and testName against
+  ## test filters.
+  if testsFilters.len == 0:
+    return true
+
+  for f in testsFilters:
+    if matchFilter(currentSuiteName, testName, f):
+      return true
+
+  return false
 
 proc ensureInitialized() =
   if formatters == nil:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
-  if not testsToRun.isValid:
-    testsToRun.init()
-    when declared(os):
+  if not testsFilters.isValid:
+    testsFilters.init()
+    when declared(paramCount):
       # Read tests to run from the command line.
       for i in 1 .. paramCount():
-        testsToRun.incl(paramStr(i))
+        testsFilters.incl(paramStr(i))
 
 # These two procs are added as workarounds for
 # https://github.com/nim-lang/Nim/issues/5549
@@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
 
   ensureInitialized()
 
-  if shouldRun(name):
+  if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
     checkpoints = @[]
     var testStatusIMPL {.inject.} = OK
 
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index 4b2e4e052..a651530c3 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -47,6 +47,49 @@ proc add*(url: var Url, a: Url) {.deprecated.} =
   url = url / a
 {.pop.}
 
+proc encodeUrl*(s: string): string =
+  ## Encodes a value to be HTTP safe: This means that characters in the set
+  ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
+  ## a space is converted to ``'+'`` and every other character is encoded as
+  ## ``'%xx'`` where ``xx`` denotes its hexadecimal value.
+  result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
+  for i in 0..s.len-1:
+    case s[i]
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
+    of ' ': add(result, '+')
+    else:
+      add(result, '%')
+      add(result, toHex(ord(s[i]), 2))
+      
+proc decodeUrl*(s: string): string =
+  ## Decodes a value from its HTTP representation: This means that a ``'+'``
+  ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
+  ## value) is converted to the character with ordinal number ``xx``, and
+  ## and every other character is carried over.
+  proc handleHexChar(c: char, x: var int) {.inline.} =
+    case c
+    of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
+    of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
+    of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
+    else: assert(false)
+    
+  result = newString(s.len)
+  var i = 0
+  var j = 0
+  while i < s.len:
+    case s[i]
+    of '%':
+      var x = 0
+      handleHexChar(s[i+1], x)
+      handleHexChar(s[i+2], x)
+      inc(i, 2)
+      result[j] = chr(x)
+    of '+': result[j] = ' '
+    else: result[j] = s[i]
+    inc(i)
+    inc(j)
+  setLen(result, j)
+
 proc parseAuthority(authority: string, result: var Uri) =
   var i = 0
   var inPort = false
@@ -250,7 +293,7 @@ proc combine*(base: Uri, reference: Uri): Uri =
 proc combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
   result = uris[0]
-  for i in 1 .. <uris.len:
+  for i in 1 ..< uris.len:
     result = combine(result, uris[i])
 
 proc isAbsolute*(uri: Uri): bool =
@@ -308,11 +351,16 @@ proc `$`*(u: Uri): string =
       result.add(":")
       result.add(u.password)
     result.add("@")
-  result.add(u.hostname)
+  if u.hostname.endswith('/'):
+    result.add(u.hostname[0..^2])
+  else:
+    result.add(u.hostname)
   if u.port.len > 0:
     result.add(":")
     result.add(u.port)
   if u.path.len > 0:
+    if u.hostname.len > 0 and u.path[0] != '/':
+      result.add('/')
     result.add(u.path)
   if u.query.len > 0:
     result.add("?")
@@ -323,6 +371,11 @@ proc `$`*(u: Uri): string =
 
 when isMainModule:
   block:
+    const test1 = "abc\L+def xyz"
+    doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
+    doAssert decodeUrl(encodeUrl(test1)) == test1
+    
+  block:
     let str = "http://localhost"
     let test = parseUri(str)
     doAssert test.path == ""
@@ -483,6 +536,34 @@ when isMainModule:
     let foo = parseUri("http://localhost:9515") / "status"
     doAssert $foo == "http://localhost:9515/status"
 
+  # bug #6649 #6652
+  block:
+    var foo = parseUri("http://example.com")
+    foo.hostname = "example.com"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com"
+    foo.path = "/baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.path = "/baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.port = "8000"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com:8000/baz"
+
+    foo = parseUri("file:/dir/file")
+    foo.path = "relative"
+    doAssert $foo == "file:relative"
+
   # isAbsolute tests
   block:
     doAssert "www.google.com".parseUri().isAbsolute() == false
@@ -524,4 +605,4 @@ when isMainModule:
     doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
 
-  echo("All good!")
\ No newline at end of file
+  echo("All good!")
diff --git a/lib/system.nim b/lib/system.nim
index d19a406cb..4d8610737 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -285,6 +285,13 @@ proc low*(x: string): int {.magic: "Low", noSideEffect.}
   ##  low(2) #=> -9223372036854775808
   ##  low(int) #=> -9223372036854775808
 
+proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
+  ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
+  ## only changes the semantics for sequences and strings (and types which
+  ## contain those). Be careful with the changed semantics though! There
+  ## is a reason why the default assignment does a deep copy of sequences
+  ## and strings.
+
 when defined(nimArrIdx):
   # :array|openarray|string|seq|cstring|tuple
   proc `[]`*[I: Ordinal;T](a: T; i: I): T {.
@@ -292,15 +299,21 @@ when defined(nimArrIdx):
   proc `[]=`*[I: Ordinal;T,S](a: T; i: I;
     x: S) {.noSideEffect, magic: "ArrPut".}
   proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
+  when defined(nimNewRuntime):
+    proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} =
+      ## generic `destructor`:idx: implementation that can be overriden.
+      discard
+    proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} =
+      ## generic `sink`:idx: implementation that can be overriden.
+      shallowCopy(x, y)
 
 type
-  Slice*[T] = object ## builtin slice type
-    a*, b*: T        ## the bounds
+  HSlice*[T, U] = object ## "heterogenous" slice type
+    a*: T        ## the lower bound (inclusive)
+    b*: U        ## the upper bound (inclusive)
+  Slice*[T] = HSlice[T, T] ## an alias for ``HSlice[T, T]``
 
-when defined(nimalias):
-  {.deprecated: [TSlice: Slice].}
-
-proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} =
+proc `..`*[T, U](a: T, b: U): HSlice[T, U] {.noSideEffect, inline, magic: "DotDot".} =
   ## `slice`:idx: operator that constructs an interval ``[a, b]``, both `a`
   ## and `b` are inclusive. Slices can also be used in the set constructor
   ## and in ordinal case statements, but then they are special-cased by the
@@ -308,8 +321,8 @@ proc `..`*[T](a, b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} =
   result.a = a
   result.b = b
 
-proc `..`*[T](b: T): Slice[T] {.noSideEffect, inline, magic: "DotDot".} =
-  ## `slice`:idx: operator that constructs an interval ``[default(T), b]``
+proc `..`*[T](b: T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot".} =
+  ## `slice`:idx: operator that constructs an interval ``[default(int), b]``
   result.b = b
 
 when not defined(niminheritable):
@@ -443,7 +456,14 @@ type
   WriteIOEffect* = object of IOEffect  ## Effect describing a write IO operation.
   ExecIOEffect* = object of IOEffect   ## Effect describing an executing IO operation.
 
-  Exception* {.compilerproc.} = object of RootObj ## \
+  StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
+                            ## to them. A StackTraceEntry is a single entry of the
+                            ## stack trace.
+    procname*: cstring  ## name of the proc that is currently executing
+    line*: int          ## line number of the proc that is currently executing
+    filename*: cstring  ## filename of the proc that is currently executing
+
+  Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \
     ## Base exception class.
     ##
     ## Each exception has to inherit from `Exception`. See the full `exception
@@ -455,7 +475,10 @@ type
     msg* {.exportc: "message".}: string ## the exception's message. Not
                                         ## providing an exception message
                                         ## is bad style.
-    trace: string
+    when defined(js):
+      trace: string
+    else:
+      trace: seq[StackTraceEntry]
     up: ref Exception # used for stacking exceptions. Not exported!
 
   SystemError* = object of Exception ## \
@@ -638,13 +661,17 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
 when defined(nimtypedescfixed):
   proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.}
 
-proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.}
+proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect, deprecated.}
   ## unary ``<`` that can be used for nice looking excluding ranges:
   ##
   ## .. code-block:: nim
   ##   for i in 0 .. <10: echo i #=> 0 1 2 3 4 5 6 7 8 9
   ##
   ## Semantically this is the same as ``pred``.
+  ##
+  ## **Deprecated since version 0.18.0**. For the common excluding range
+  ## write ``0 ..< 10`` instead of ``0 .. < 10`` (look at the spacing).
+  ## For ``<x`` write ``pred(x)``.
 
 proc succ*[T](x: Ordinal[T], y = 1): T {.magic: "Succ", noSideEffect.}
   ## returns the ``y``-th successor of the value ``x``. ``T`` has to be
@@ -716,6 +743,18 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {.
   ## ``cap``.
   discard
 
+when not defined(JS):
+  proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] =
+    ## creates a new sequence of type ``seq[T]`` with length ``len``.
+    ##
+    ## Only available for numbers types. Note that the sequence will be
+    ## uninitialized. After the creation of the sequence you should assign
+    ## entries to the sequence instead of adding them.
+
+    result = newSeqOfCap[T](len)
+    var s = cast[PGenericSeq](result)
+    s.len = len
+
 proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
   magic: "LengthOpenArray", noSideEffect.}
 proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
@@ -878,7 +917,7 @@ proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
 proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
 proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
   ## computes the integer division. This is roughly the same as
-  ## ``floor(x/y)``.
+  ## ``trunc(x/y)``.
   ##
   ## .. code-block:: Nim
   ##   1 div 2 == 0
@@ -1080,7 +1119,7 @@ proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
 
 proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
   ## computes the integer division. This is roughly the same as
-  ## ``floor(x/y)``.
+  ## ``trunc(x/y)``.
   ##
   ## .. code-block:: Nim
   ##  (7 div 5) == 1
@@ -1150,7 +1189,7 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
   ## is achieved by reversing the parameters for ``contains``; ``in`` then
   ## passes its arguments in reverse order.
 
-proc contains*[T](s: Slice[T], value: T): bool {.noSideEffect, inline.} =
+proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} =
   ## Checks if `value` is within the range of `s`; returns true iff
   ## `value >= s.a and value <= s.b`
   ##
@@ -1298,6 +1337,7 @@ proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
   ##   tmp.add("cd")
   ##   assert(tmp == "abcd")
 
+
 type
   Endianness* = enum ## is a type describing the endianness of a processor.
     littleEndian, bigEndian
@@ -1412,7 +1452,11 @@ const
     ## is the value that should be passed to `quit <#quit>`_ to indicate
     ## failure.
 
-var programResult* {.exportc: "nim_program_result".}: int
+when defined(nodejs):
+  var programResult* {.importc: "process.exitCode".}: int
+  programResult = 0
+else:
+  var programResult* {.exportc: "nim_program_result".}: int
   ## modify this variable to specify the exit code of the program
   ## under normal circumstances. When the program is terminated
   ## prematurely using ``quit``, this value is ignored.
@@ -1429,7 +1473,8 @@ when defined(nimdoc):
     ## <#GC_fullCollect>`_.
     ##
     ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
-    ## program finishes without incident. A raised unhandled exception is
+    ## program finishes without incident for platforms where this is the
+    ## expected behavior. A raised unhandled exception is
     ## equivalent to calling ``quit(QuitFailure)``.
     ##
     ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
@@ -1480,13 +1525,6 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
   setLen(x, xl + y.len)
   for i in 0..high(y): x[xl+i] = y[i]
 
-proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
-  ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
-  ## only changes the semantics for sequences and strings (and types which
-  ## contain those). Be careful with the changed semantics though! There
-  ## is a reason why the default assignment does a deep copy of sequences
-  ## and strings.
-
 proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
   ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
   ## This is an O(1) operation.
@@ -1507,7 +1545,7 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
   ##  i.delete(2) #=> @[1, 2, 4, 5]
   template defaultImpl =
     let xl = x.len
-    for j in i..xl-2: shallowCopy(x[j], x[j+1])
+    for j in i.int..xl-2: shallowCopy(x[j], x[j+1])
     setLen(x, xl-1)
 
   when nimvm:
@@ -1974,34 +2012,80 @@ iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
       yield res
       dec(res, step)
 
-iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
-  ## Counts from ordinal value `a` up to `b` (inclusive) with the given
-  ## step count. `S`, `T` may be any ordinal type, `step` may only
-  ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
-  ## efficiency reasons.
-  when T is IntLikeForCount:
-    var res = int(a)
-    while res <= int(b):
-      yield T(res)
-      inc(res, step)
-  else:
-    var res: T = T(a)
-    while res <= b:
-      yield res
-      inc(res, step)
+when defined(nimNewRoof):
+  iterator countup*[T](a, b: T, step = 1): T {.inline.} =
+    ## Counts from ordinal value `a` up to `b` (inclusive) with the given
+    ## step count. `S`, `T` may be any ordinal type, `step` may only
+    ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
+    ## efficiency reasons.
+    when T is IntLikeForCount:
+      var res = int(a)
+      while res <= int(b):
+        yield T(res)
+        inc(res, step)
+    else:
+      var res: T = T(a)
+      while res <= b:
+        yield res
+        inc(res, step)
+
+  iterator `..`*[T](a, b: T): T {.inline.} =
+    ## An alias for `countup`.
+    when T is IntLikeForCount:
+      var res = int(a)
+      while res <= int(b):
+        yield T(res)
+        inc(res)
+    else:
+      var res: T = T(a)
+      while res <= b:
+        yield res
+        inc(res)
+
+  template dotdotImpl(t) {.dirty.} =
+    iterator `..`*(a, b: t): t {.inline.} =
+      ## A type specialized version of ``..`` for convenience so that
+      ## mixing integer types work better.
+      var res = a
+      while res <= b:
+        yield res
+        inc(res)
+
+  dotdotImpl(int64)
+  dotdotImpl(int32)
+  dotdotImpl(uint64)
+  dotdotImpl(uint32)
+
+else:
+  iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
+    ## Counts from ordinal value `a` up to `b` (inclusive) with the given
+    ## step count. `S`, `T` may be any ordinal type, `step` may only
+    ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
+    ## efficiency reasons.
+    when T is IntLikeForCount:
+      var res = int(a)
+      while res <= int(b):
+        yield T(res)
+        inc(res, step)
+    else:
+      var res: T = T(a)
+      while res <= b:
+        yield res
+        inc(res, step)
+
+  iterator `..`*[S, T](a: S, b: T): T {.inline.} =
+    ## An alias for `countup`.
+    when T is IntLikeForCount:
+      var res = int(a)
+      while res <= int(b):
+        yield T(res)
+        inc(res)
+    else:
+      var res: T = T(a)
+      while res <= b:
+        yield res
+        inc(res)
 
-iterator `..`*[S, T](a: S, b: T): T {.inline.} =
-  ## An alias for `countup`.
-  when T is IntLikeForCount:
-    var res = int(a)
-    while res <= int(b):
-      yield T(res)
-      inc(res)
-  else:
-    var res: T = T(a)
-    while res <= b:
-      yield res
-      inc(res)
 
 iterator `||`*[S, T](a: S, b: T, annotation=""): T {.
   inline, magic: "OmpParFor", sideEffect.} =
@@ -2065,6 +2149,9 @@ proc max*[T](x, y: T): T =
   if y <= x: x else: y
 {.pop.}
 
+proc high*(T: typedesc[SomeReal]): T = Inf
+proc low*(T: typedesc[SomeReal]): T = NegInf
+
 proc clamp*[T](x, a, b: T): T =
   ## limits the value ``x`` within the interval [a, b]
   ##
@@ -2075,7 +2162,7 @@ proc clamp*[T](x, a, b: T): T =
   if x > b: return b
   return x
 
-proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} =
+proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} =
   ## length of ordinal slice, when x.b < x.a returns zero length
   ##
   ## .. code-block:: Nim
@@ -2143,7 +2230,7 @@ iterator items*(E: typedesc[enum]): E =
   for v in low(E)..high(E):
     yield v
 
-iterator items*[T](s: Slice[T]): T =
+iterator items*[T](s: HSlice[T, T]): T =
   ## iterates over the slice `s`, yielding each value between `s.a` and `s.b`
   ## (inclusively).
   for x in s.a..s.b:
@@ -2429,9 +2516,9 @@ proc `$`*[T: tuple|object](x: T): string =
     when compiles($value):
       when compiles(value.isNil):
         if value.isNil: result.add "nil"
-        else: result.add($value)
+        else: result.addQuoted(value)
       else:
-        result.add($value)
+        result.addQuoted(value)
       firstElement = false
     else:
       result.add("...")
@@ -2451,12 +2538,9 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
       if value.isNil:
         result.add "nil"
       else:
-        result.add($value)
-    # prevent temporary string allocation
-    elif compiles(result.add(value)):
-      result.add(value)
+        result.addQuoted(value)
     else:
-      result.add($value)
+      result.addQuoted(value)
 
   result.add(suffix)
 
@@ -2703,9 +2787,9 @@ when defined(nimvarargstyped):
     ## pretends to be free of side effects, so that it can be used for debugging
     ## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
 else:
-  proc echo*(x: varargs[expr, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
+  proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
     benign, sideEffect.}
-  proc debugEcho*(x: varargs[expr, `$`]) {.magic: "Echo", noSideEffect,
+  proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
                                              tags: [], raises: [].}
 
 template newException*(exceptn: typedesc, message: string;
@@ -2821,7 +2905,7 @@ when not defined(JS): #and not defined(nimscript):
       fmRead,                   ## Open the file for read access only.
       fmWrite,                  ## Open the file for write access only.
                                 ## If the file does not exist, it will be
-                                ## created.
+                                ## created. Existing files will be cleared!
       fmReadWrite,              ## Open the file for read and write access.
                                 ## If the file does not exist, it will be
                                 ## created. Existing files will be cleared!
@@ -2844,7 +2928,10 @@ when not defined(JS): #and not defined(nimscript):
       elif x > y: result = 1
       else: result = 0
     else:
-      result = int(c_strcmp(x, y))
+      let minlen = min(x.len, y.len)
+      result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
+      if result == 0:
+        result = x.len - y.len
 
   when defined(nimscript):
     proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
@@ -3048,9 +3135,6 @@ when not defined(JS): #and not defined(nimscript):
       ## returns the OS file handle of the file ``f``. This is only useful for
       ## platform specific programming.
 
-    when not defined(nimfix):
-      {.deprecated: [fileHandle: getFileHandle].}
-
   when declared(newSeq):
     proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
       ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
@@ -3402,6 +3486,36 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} =
   ## integer division that results in a float.
   result = toFloat(x) / toFloat(y)
 
+type
+  BackwardsIndex* = distinct int ## type that is constructed by ``^`` for
+                                 ## reversed array accesses.
+
+template `^`*(x: int): BackwardsIndex = BackwardsIndex(x)
+  ## builtin `roof`:idx: operator that can be used for convenient array access.
+  ## ``a[^x]`` is a shortcut for ``a[a.len-x]``.
+
+template `..^`*(a, b: untyped): untyped =
+  ## a shortcut for '.. ^' to avoid the common gotcha that a space between
+  ## '..' and '^' is required.
+  a .. ^b
+
+template `..<`*(a, b: untyped): untyped =
+  ## a shortcut for 'a..pred(b)'.
+  a .. pred(b)
+
+when defined(nimNewRoof):
+  iterator `..<`*[T](a, b: T): T =
+    var i = T(a)
+    while i < b:
+      yield i
+      inc i
+else:
+  iterator `..<`*[S, T](a: S, b: T): T =
+    var i = T(a)
+    while i < b:
+      yield i
+      inc i
+
 template spliceImpl(s, a, L, b: untyped): untyped =
   # make room for additional elements or cut:
   var shift = b.len - max(0,L)  # ignore negative slice size
@@ -3415,19 +3529,25 @@ template spliceImpl(s, a, L, b: untyped): untyped =
     # cut down:
     setLen(s, newLen)
   # fill the hole:
-  for i in 0 .. <b.len: s[a+i] = b[i]
+  for i in 0 ..< b.len: s[a+i] = b[i]
+
+template `^^`(s, i: untyped): untyped =
+  (when i is BackwardsIndex: s.len - int(i) else: int(i))
 
 when hasAlloc or defined(nimscript):
-  proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
+  proc `[]`*[T, U](s: string, x: HSlice[T, U]): string {.inline.} =
     ## slice operation for strings.
     ## returns the inclusive range [s[x.a], s[x.b]]:
     ##
     ## .. code-block:: nim
     ##    var s = "abcdef"
     ##    assert s[1..3] == "bcd"
-    result = s.substr(x.a, x.b)
+    let a = s ^^ x.a
+    let L = (s ^^ x.b) - a + 1
+    result = newString(L)
+    for i in 0 ..< L: result[i] = s[i + a]
 
-  proc `[]=`*(s: var string, x: Slice[int], b: string) =
+  proc `[]=`*[T, U](s: var string, x: HSlice[T, U], b: string) =
     ## slice assignment for strings. If
     ## ``b.len`` is not exactly the number of elements that are referred to
     ## by `x`, a `splice`:idx: is performed:
@@ -3436,75 +3556,76 @@ when hasAlloc or defined(nimscript):
     ##   var s = "abcdef"
     ##   s[1 .. ^2] = "xyz"
     ##   assert s == "axyzf"
-    var a = x.a
-    var L = x.b - a + 1
+    var a = s ^^ x.a
+    var L = (s ^^ x.b) - a + 1
     if L == b.len:
-      for i in 0 .. <L: s[i+a] = b[i]
+      for i in 0..<L: s[i+a] = b[i]
     else:
       spliceImpl(s, a, L, b)
 
-proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
+proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: HSlice[U, V]): seq[T] =
   ## slice operation for arrays.
   ## returns the inclusive range [a[x.a], a[x.b]]:
   ##
   ## .. code-block:: nim
   ##    var a = [1,2,3,4]
   ##    assert a[0..2] == @[1,2,3]
-  when low(a) < 0:
-    {.error: "Slicing for arrays with negative indices is unsupported.".}
-  var L = x.b - x.a + 1
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
   result = newSeq[T](L)
-  for i in 0.. <L: result[i] = a[i + x.a]
+  for i in 0..<L: result[i] = a[Idx(i + xa)]
 
-proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[int], b: openArray[T]) =
+proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) =
   ## slice assignment for arrays.
-  when low(a) < 0:
-    {.error: "Slicing for arrays with negative indices is unsupported.".}
-  var L = x.b - x.a + 1
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
   if L == b.len:
-    for i in 0 .. <L: a[i+x.a] = b[i]
+    for i in 0..<L: a[Idx(i + xa)] = b[i]
   else:
     sysFatal(RangeError, "different lengths for slice assignment")
 
-proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[Idx]): seq[T] =
-  ## slice operation for arrays.
-  var L = ord(x.b) - ord(x.a) + 1
-  newSeq(result, L)
-  for i in 0.. <L:
-    result[i] = a[Idx(ord(x.a) + i)]
-
-proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
-  ## slice assignment for arrays.
-  var L = ord(x.b) - ord(x.a) + 1
-  if L == b.len:
-    for i in 0 .. <L:
-      a[Idx(ord(x.a) + i)] = b[i]
-  else:
-    sysFatal(RangeError, "different lengths for slice assignment")
-
-proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
+proc `[]`*[T, U, V](s: openArray[T], x: HSlice[U, V]): seq[T] =
   ## slice operation for sequences.
   ## returns the inclusive range [s[x.a], s[x.b]]:
   ##
   ## .. code-block:: nim
   ##    var s = @[1,2,3,4]
   ##    assert s[0..2] == @[1,2,3]
-  var a = x.a
-  var L = x.b - a + 1
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
   newSeq(result, L)
-  for i in 0.. <L: result[i] = s[i + a]
+  for i in 0 ..< L: result[i] = s[i + a]
 
-proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
+proc `[]=`*[T, U, V](s: var seq[T], x: HSlice[U, V], b: openArray[T]) =
   ## slice assignment for sequences. If
   ## ``b.len`` is not exactly the number of elements that are referred to
   ## by `x`, a `splice`:idx: is performed.
-  var a = x.a
-  var L = x.b - a + 1
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
   if L == b.len:
-    for i in 0 .. <L: s[i+a] = b[i]
+    for i in 0 ..< L: s[i+a] = b[i]
   else:
     spliceImpl(s, a, L, b)
 
+proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} =
+  system.`[]`(s, s.len - int(i))
+
+proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} =
+  a[Idx(a.len - int(i) + int low(a))]
+proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)]
+
+proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} =
+  system.`[]`(s, s.len - int(i))
+proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} =
+  a[Idx(a.len - int(i) + int low(a))]
+
+proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} =
+  system.`[]=`(s, s.len - int(i), x)
+proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} =
+  a[Idx(a.len - int(i) + int low(a))] = x
+proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} =
+  s[s.len - int(i)] = x
+
 proc slurp*(filename: string): string {.magic: "Slurp".}
   ## This is an alias for `staticRead <#staticRead>`_.
 
@@ -3612,7 +3733,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
   ## .. code-block:: nim
   ##   import strutils
   ##
-  ##   template testException(exception, code: expr): stmt =
+  ##   template testException(exception, code: untyped): typed =
   ##     try:
   ##       let pos = instantiationInfo()
   ##       discard(code)
@@ -3651,6 +3772,7 @@ template assert*(cond: bool, msg = "") =
   ## that ``AssertionError`` is hidden from the effect system, so it doesn't
   ## produce ``{.raises: [AssertionError].}``. This exception is only supposed
   ## to be caught by unit testing frameworks.
+  ##
   ## The compiler may not generate any code at all for ``assert`` if it is
   ## advised to do so through the ``-d:release`` or ``--assertions:off``
   ## `command line switches <nimc.html#command-line-switches>`_.
@@ -3750,11 +3872,11 @@ type
 {.deprecated: [PNimrodNode: NimNode].}
 
 when false:
-  template eval*(blk: stmt): stmt =
+  template eval*(blk: typed): typed =
     ## executes a block of code at compile time just as if it was a macro
     ## optionally, the block can return an AST tree that will replace the
     ## eval expression
-    macro payload: stmt {.gensym.} = blk
+    macro payload: typed {.gensym.} = blk
     payload()
 
 when hasAlloc:
@@ -3784,6 +3906,65 @@ proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.
 when declared(initDebugger):
   initDebugger()
 
+proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} =
+  ## Adds a char to string `s` and applies the following escaping:
+  ##
+  ## * replaces any ``\`` by ``\\``
+  ## * replaces any ``'`` by ``\'``
+  ## * replaces any ``"`` by ``\"``
+  ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}``
+  ##   by ``\xHH`` where ``HH`` is its hexadecimal value.
+  ##
+  ## The procedure has been designed so that its output is usable for many
+  ## different common syntaxes.
+  ## **Note**: This is not correct for producing Ansi C code!
+  case c
+  of '\0'..'\31', '\127'..'\255':
+    add(s, "\\x")
+    const HexChars = "0123456789ABCDEF"
+    let n = ord(c)
+    s.add(HexChars[int((n and 0xF0) shr 4)])
+    s.add(HexChars[int(n and 0xF)])
+  of '\\': add(s, "\\\\")
+  of '\'': add(s, "\\'")
+  of '\"': add(s, "\\\"")
+  else: add(s, c)
+
+proc addQuoted*[T](s: var string, x: T) =
+  ## Appends `x` to string `s` in place, applying quoting and escaping
+  ## if `x` is a string or char. See
+  ## `addEscapedChar <system.html#addEscapedChar>`_
+  ## for the escaping scheme.
+  ##
+  ## The Nim standard library uses this function on the elements of
+  ## collections when producing a string representation of a collection.
+  ## It is recommended to use this function as well for user-side collections.
+  ## Users may overload `addQuoted` for custom (string-like) types if
+  ## they want to implement a customized element representation.
+  ##
+  ## .. code-block:: Nim
+  ##   var tmp = ""
+  ##   tmp.addQuoted(1)
+  ##   tmp.add(", ")
+  ##   tmp.addQuoted("string")
+  ##   tmp.add(", ")
+  ##   tmp.addQuoted('c')
+  ##   assert(tmp == """1, "string", 'c'""")
+  when T is string:
+    s.add("\"")
+    for c in x:
+      s.addEscapedChar(c)
+    s.add("\"")
+  elif T is char:
+    s.add("'")
+    s.addEscapedChar(x)
+    s.add("'")
+  # prevent temporary string allocation
+  elif compiles(s.add(x)):
+    s.add(x)
+  else:
+    s.add($x)
+
 when hasAlloc:
   # XXX: make these the default (or implement the NilObject optimization)
   proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
@@ -3845,31 +4026,6 @@ proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} =
   ##   procCall someMethod(a, b)
   discard
 
-proc `^`*[T](x: int; y: openArray[T]): int {.noSideEffect, magic: "Roof".}
-proc `^`*(x: int): int {.noSideEffect, magic: "Roof".} =
-  ## builtin `roof`:idx: operator that can be used for convenient array access.
-  ## ``a[^x]`` is rewritten to ``a[a.len-x]``. However currently the ``a``
-  ## expression must not have side effects for this to compile. Note that since
-  ## this is a builtin, it automatically works for all kinds of
-  ## overloaded ``[]`` or ``[]=`` accessors.
-  discard
-
-template `..^`*(a, b: untyped): untyped =
-  ## a shortcut for '.. ^' to avoid the common gotcha that a space between
-  ## '..' and '^' is required.
-  a .. ^b
-
-template `..<`*(a, b: untyped): untyped {.dirty.} =
-  ## a shortcut for '.. <' to avoid the common gotcha that a space between
-  ## '..' and '<' is required.
-  a .. <b
-
-iterator `..<`*[S,T](a: S, b: T): T =
-  var i = T(a)
-  while i < b:
-    yield i
-    inc i
-
 proc xlen*(x: string): int {.magic: "XLenStr", noSideEffect.} = discard
 proc xlen*[T](x: seq[T]): int {.magic: "XLenSeq", noSideEffect.} =
   ## returns the length of a sequence or a string without testing for 'nil'.
@@ -3916,3 +4072,38 @@ when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage):
   proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32",
     importc: "SetConsoleOutputCP".}
   discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
+
+
+when defined(nimHasRunnableExamples):
+  proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".}
+    ## A section you should use to mark `runnable example`:idx: code with.
+    ##
+    ## - In normal debug and release builds code within
+    ##   a ``runnableExamples`` section is ignored.
+    ## - The documentation generator is aware of these examples and considers them
+    ##   part of the ``##`` doc comment. As the last step of documentation
+    ##   generation the examples are put into an ``$file_example.nim`` file,
+    ##   compiled and tested. The collected examples are
+    ##   put into their own module to ensure the examples do not refer to
+    ##   non-exported symbols.
+else:
+  template runnableExamples*(body: untyped) =
+    discard
+
+template doAssertRaises*(exception, code: untyped): typed =
+  ## Raises ``AssertionError`` if specified ``code`` does not raise the
+  ## specified exception.
+  runnableExamples:
+    doAssertRaises(ValueError):
+      raise newException(ValueError, "Hello World")
+
+  try:
+    block:
+      code
+    raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
+  except exception:
+    discard
+  except Exception as exc:
+    raiseAssert(astToStr(exception) &
+                " wasn't raised, another error was raised instead by:\n"&
+                astToStr(code))
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 78db96e77..e274e8e0c 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -8,8 +8,6 @@
 #
 
 # Low level allocator for Nim. Has been designed to support the GC.
-# TODO:
-# - make searching for block O(1)
 {.push profiler:off.}
 
 include osalloc
@@ -19,14 +17,17 @@ template track(op, address, size) =
     memTrackerOp(op, address, size)
 
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
-# Each chunk starts at an address that is divisible by the page size. Chunks
-# that are bigger than ``ChunkOsReturn`` are returned back to the operating
-# system immediately.
+# Each chunk starts at an address that is divisible by the page size.
 
 const
-  ChunkOsReturn = 256 * PageSize # 1 MB
-  InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
+  InitialMemoryRequest = 128 * PageSize # 0.5 MB
   SmallChunkSize = PageSize
+  MaxFli = 30
+  MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
+                 # everywhere!
+  MaxSli = 1 shl MaxLog2Sli
+  FliOffset = 6
+  RealFli = MaxFli - FliOffset
 
 type
   PTrunk = ptr Trunk
@@ -99,10 +100,12 @@ type
   MemRegion = object
     minLargeObj, maxLargeObj: int
     freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
+    flBitmap: uint32
+    slBitmap: array[RealFli, uint32]
+    matrix: array[RealFli, array[MaxSli, PBigChunk]]
     llmem: PLLChunk
     currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
     lastSize: int # needed for the case that OS gives us pages linearly
-    freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
     chunkStarts: IntSet
     root, deleted, last, freeAvlNodes: PAvlNode
     locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
@@ -110,7 +113,109 @@ type
     bottomData: AvlNode
     heapLinks: HeapLinks
 
-{.deprecated: [TMemRegion: MemRegion].}
+const
+  fsLookupTable: array[byte, int8] = [
+    -1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+    5, 5, 5, 5, 5, 5, 5, 5,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7
+  ]
+
+proc msbit(x: uint32): int {.inline.} =
+  let a = if x <= 0xff_ff:
+            (if x <= 0xff: 0 else: 8)
+          else:
+            (if x <= 0xff_ff_ff: 16 else: 24)
+  result = int(fsLookupTable[byte(x shr a)]) + a
+
+proc lsbit(x: uint32): int {.inline.} =
+  msbit(x and ((not x) + 1))
+
+proc setBit(nr: int; dest: var uint32) {.inline.} =
+  dest = dest or (1u32 shl (nr and 0x1f))
+
+proc clearBit(nr: int; dest: var uint32) {.inline.} =
+  dest = dest and not (1u32 shl (nr and 0x1f))
+
+proc mappingSearch(r, fl, sl: var int) {.inline.} =
+  #let t = (1 shl (msbit(uint32 r) - MaxLog2Sli)) - 1
+  # This diverges from the standard TLSF algorithm because we need to ensure
+  # PageSize alignment:
+  let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
+  r = r + t
+  fl = msbit(uint32 r)
+  sl = (r shr (fl - MaxLog2Sli)) - MaxSli
+  dec fl, FliOffset
+  r = r and not t
+  sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned")
+
+# See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of
+# this algorithm.
+
+proc mappingInsert(r: int): tuple[fl, sl: int] {.inline.} =
+  sysAssert((r and PageMask) == 0, "mappingInsert: still not aligned")
+  result.fl = msbit(uint32 r)
+  result.sl = (r shr (result.fl - MaxLog2Sli)) - MaxSli
+  dec result.fl, FliOffset
+
+template mat(): untyped = a.matrix[fl][sl]
+
+proc findSuitableBlock(a: MemRegion; fl, sl: var int): PBigChunk {.inline.} =
+  let tmp = a.slBitmap[fl] and (not 0u32 shl sl)
+  result = nil
+  if tmp != 0:
+    sl = lsbit(tmp)
+    result = mat()
+  else:
+    fl = lsbit(a.flBitmap and (not 0u32 shl (fl + 1)))
+    if fl > 0:
+      sl = lsbit(a.slBitmap[fl])
+      result = mat()
+
+template clearBits(sl, fl) =
+  clearBit(sl, a.slBitmap[fl])
+  if a.slBitmap[fl] == 0u32:
+    # do not forget to cascade:
+    clearBit(fl, a.flBitmap)
+
+proc removeChunkFromMatrix(a: var MemRegion; b: PBigChunk) =
+  let (fl, sl) = mappingInsert(b.size)
+  if b.next != nil: b.next.prev = b.prev
+  if b.prev != nil: b.prev.next = b.next
+  if mat() == b:
+    mat() = b.next
+    if mat() == nil:
+      clearBits(sl, fl)
+  b.prev = nil
+  b.next = nil
+
+proc removeChunkFromMatrix2(a: var MemRegion; b: PBigChunk; fl, sl: int) =
+  mat() = b.next
+  if mat() != nil:
+    mat().prev = nil
+  else:
+    clearBits(sl, fl)
+  b.prev = nil
+  b.next = nil
+
+proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
+  let (fl, sl) = mappingInsert(b.size)
+  b.prev = nil
+  b.next = mat()
+  if mat() != nil:
+    mat().prev = b
+  mat() = b
+  setBit(sl, a.slBitmap[fl])
+  setBit(fl, a.flBitmap)
 
 {.push stack_trace: off.}
 proc initAllocator() = discard "nothing to do anymore"
@@ -203,6 +308,7 @@ proc llDeallocAll(a: var MemRegion) =
     var next = it.next
     osDeallocPages(it, PageSize)
     it = next
+  a.llmem = nil
 
 proc intSetGet(t: IntSet, key: int): PTrunk =
   var it = t.data[key and high(t.data)]
@@ -301,13 +407,14 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
   result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
   #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
 
-proc writeFreeList(a: MemRegion) =
-  var it = a.freeChunksList
-  c_fprintf(stdout, "freeChunksList: %p\n", it)
-  while it != nil:
-    c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
-              it, it.next, it.prev, it.size)
-    it = it.next
+when false:
+  proc writeFreeList(a: MemRegion) =
+    var it = a.freeChunksList
+    c_fprintf(stdout, "freeChunksList: %p\n", it)
+    while it != nil:
+      c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
+                it, it.next, it.prev, it.size)
+      it = it.next
 
 const nimMaxHeap {.intdefine.} = 0
 
@@ -368,6 +475,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
     result.prevSize = 0 or (result.prevSize and 1) # unknown
     # but do not overwrite 'used' field
   a.lastSize = size # for next request
+  sysAssert((cast[int](result) and PageMask) == 0, "requestOschunks: unaligned chunk")
 
 proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
   result = contains(a.chunkStarts, pageIndex(p))
@@ -418,7 +526,7 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
     if isAccessible(a, ri) and chunkUnused(ri):
       sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
       if not isSmallChunk(ri):
-        listRemove(a.freeChunksList, cast[PBigChunk](ri))
+        removeChunkFromMatrix(a, cast[PBigChunk](ri))
         inc(c.size, ri.size)
         excl(a.chunkStarts, pageIndex(ri))
   when coalescLeft:
@@ -429,49 +537,44 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
       if isAccessible(a, le) and chunkUnused(le):
         sysAssert(not isSmallChunk(le), "freeBigChunk 5")
         if not isSmallChunk(le):
-          listRemove(a.freeChunksList, cast[PBigChunk](le))
+          removeChunkFromMatrix(a, cast[PBigChunk](le))
           inc(le.size, c.size)
           excl(a.chunkStarts, pageIndex(c))
           c = cast[PBigChunk](le)
 
   incl(a, a.chunkStarts, pageIndex(c))
   updatePrevSize(a, c, c.size)
-  listAdd(a.freeChunksList, c)
+  addChunkToMatrix(a, c)
   # set 'used' to false:
   c.prevSize = c.prevSize and not 1
 
 proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
   var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
-  sysAssert(rest notin a.freeChunksList, "splitChunk")
   rest.size = c.size - size
   track("rest.origSize", addr rest.origSize, sizeof(int))
+  # XXX check if these two nil assignments are dead code given
+  # addChunkToMatrix's implementation:
   rest.next = nil
   rest.prev = nil
-  # size and not used
+  # size and not used:
   rest.prevSize = size
   sysAssert((size and 1) == 0, "splitChunk 2")
+  sysAssert((size and PageMask) == 0,
+      "splitChunk: size is not a multiple of the PageSize")
   updatePrevSize(a, c, rest.size)
   c.size = size
   incl(a, a.chunkStarts, pageIndex(rest))
-  listAdd(a.freeChunksList, rest)
+  addChunkToMatrix(a, rest)
 
 proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   # use first fit for now:
-  sysAssert((size and PageMask) == 0, "getBigChunk 1")
   sysAssert(size > 0, "getBigChunk 2")
-  result = a.freeChunksList
-  block search:
-    while result != nil:
-      sysAssert chunkUnused(result), "getBigChunk 3"
-      if result.size == size:
-        listRemove(a.freeChunksList, result)
-        break search
-      elif result.size > size:
-        listRemove(a.freeChunksList, result)
-        splitChunk(a, result, size)
-        break search
-      result = result.next
-      sysAssert result != a.freeChunksList, "getBigChunk 4"
+  var size = size # roundup(size, PageSize)
+  var fl, sl: int
+  mappingSearch(size, fl, sl)
+  sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
+  result = findSuitableBlock(a, fl, sl)
+  if result == nil:
     if size < InitialMemoryRequest:
       result = requestOsChunks(a, InitialMemoryRequest)
       splitChunk(a, result, size)
@@ -480,7 +583,10 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
       # if we over allocated split the chunk:
       if result.size > size:
         splitChunk(a, result, size)
-
+  else:
+    removeChunkFromMatrix2(a, result, fl, sl)
+    if result.size >= size + PageSize:
+      splitChunk(a, result, size)
   # set 'used' to to true:
   result.prevSize = 1
   track("setUsedToFalse", addr result.origSize, sizeof(int))
@@ -571,14 +677,14 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
                size == 0, "rawAlloc 21")
     sysAssert(allocInv(a), "rawAlloc: end small size")
   else:
-    size = roundup(requestedSize+bigChunkOverhead(), PageSize)
+    size = requestedSize + bigChunkOverhead() #  roundup(requestedSize+bigChunkOverhead(), PageSize)
     # allocate a large block
     var c = getBigChunk(a, size)
     sysAssert c.prev == nil, "rawAlloc 10"
     sysAssert c.next == nil, "rawAlloc 11"
-    sysAssert c.size == size, "rawAlloc 12"
     result = addr(c.data)
-    sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
     if a.root == nil: a.root = getBottom(a)
     add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
   sysAssert(isAccessible(a, result), "rawAlloc 14")
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index 8c3801687..afc435638 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -213,12 +213,20 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
     result = memLoc
 
 when defined(vcc):
-  proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-    {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
-  proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-    {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
-  proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-    {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+  when defined(cpp):
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
+  else:
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
 
   proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
     when sizeof(T) == 8:
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 1520f231e..69b680dbd 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
   while x != subclass:
     if x == nil:
       sysFatal(ObjectConversionError, "invalid object conversion")
-      break
     x = x.base
 
 proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim
index cc6919d36..937c0d6f0 100644
--- a/lib/system/debugger.nim
+++ b/lib/system/debugger.nim
@@ -127,7 +127,7 @@ proc fileMatches(c, bp: cstring): bool =
 
 proc canonFilename*(filename: cstring): cstring =
   ## returns 'nil' if the filename cannot be found.
-  for i in 0 .. <dbgFilenameLen:
+  for i in 0 .. dbgFilenameLen-1:
     result = dbgFilenames[i]
     if fileMatches(result, filename): return result
   result = nil
@@ -261,7 +261,7 @@ proc genericHash(dest: pointer, mt: PNimType): int =
 proc dbgRegisterWatchpoint(address: pointer, name: cstring,
                            typ: PNimType) {.compilerproc.} =
   let L = watchPointsLen
-  for i in 0.. <L:
+  for i in 0 .. pred(L):
     if watchPoints[i].name == name:
       # address may have changed:
       watchPoints[i].address = address
@@ -288,7 +288,7 @@ var
 
 proc checkWatchpoints =
   let L = watchPointsLen
-  for i in 0.. <L:
+  for i in 0 .. pred(L):
     let newHash = genericHash(watchPoints[i].address, watchPoints[i].typ)
     if newHash != watchPoints[i].oldValue:
       dbgWatchpointHook(watchPoints[i].name)
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
index 35d8f25c4..d51ae29df 100644
--- a/lib/system/endb.nim
+++ b/lib/system/endb.nim
@@ -370,7 +370,7 @@ proc commandPrompt() =
     if dbgUser.len == 0: dbgUser.len = oldLen
     # now look what we have to do:
     var i = scanWord(addr dbgUser.data, dbgTemp, 0)
-    template `?`(x: expr): expr = dbgTemp == cstring(x)
+    template `?`(x: untyped): untyped = dbgTemp == cstring(x)
     if ?"s" or ?"step":
       dbgState = dbStepInto
       again = false
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 950981227..8e42ea468 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -38,20 +38,29 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
 proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
 proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
 
+type
+  GcFrame = ptr GcFrameHeader
+  GcFrameHeader {.compilerproc.} = object
+    len: int
+    prev: ptr GcFrameHeader
+
 var
   framePtr {.threadvar.}: PFrame
   excHandler {.threadvar.}: PSafePoint
     # list of exception handlers
     # a global variable for the root of all try blocks
   currException {.threadvar.}: ref Exception
+  gcFramePtr {.threadvar.}: GcFrame
 
 type
-  FrameState = tuple[framePtr: PFrame, excHandler: PSafePoint, currException: ref Exception]
+  FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
+                     excHandler: PSafePoint, currException: ref Exception]
 
 proc getFrameState*(): FrameState {.compilerRtl, inl.} =
-  return (framePtr, excHandler, currException)
+  return (gcFramePtr, framePtr, excHandler, currException)
 
 proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
+  gcFramePtr = state.gcFramePtr
   framePtr = state.framePtr
   excHandler = state.excHandler
   currException = state.currException
@@ -61,9 +70,29 @@ proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
 proc popFrame {.compilerRtl, inl.} =
   framePtr = framePtr.prev
 
+when false:
+  proc popFrameOfAddr(s: PFrame) {.compilerRtl.} =
+    var it = framePtr
+    if it == s:
+      framePtr = framePtr.prev
+    else:
+      while it != nil:
+        if it == s:
+          framePtr = it.prev
+          break
+        it = it.prev
+
 proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
   framePtr = s
 
+proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
+proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
+proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
+proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
+  s.prev = gcFramePtr
+  zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
+  gcFramePtr = s
+
 proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
   s.hasRaiseAction = false
   s.prev = excHandler
@@ -138,6 +167,52 @@ when not hasThreadSupport:
   var
     tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
 
+const
+  reraisedFromBegin = -10
+  reraisedFromEnd = -100
+
+template reraisedFrom(z): untyped =
+  StackTraceEntry(procname: nil, line: z, filename: nil)
+
+proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
+  var
+    it = f
+    i = 0
+  while it != nil:
+    inc(i)
+    it = it.prev
+  var last = i-1
+  if s.isNil:
+    s = newSeq[StackTraceEntry](i)
+  else:
+    last = s.len + i - 1
+    s.setLen(last+1)
+  it = f
+  while it != nil:
+    s[last] = StackTraceEntry(procname: it.procname,
+                              line: it.line,
+                              filename: it.filename)
+    it = it.prev
+    dec last
+
+template addFrameEntry(s, f: untyped) =
+  var oldLen = s.len
+  add(s, f.filename)
+  if f.line > 0:
+    add(s, '(')
+    add(s, $f.line)
+    add(s, ')')
+  for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
+  add(s, f.procname)
+  add(s, "\n")
+
+proc `$`(s: seq[StackTraceEntry]): string =
+  result = newStringOfCap(2000)
+  for i in 0 .. s.len-1:
+    if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
+    elif s[i].line == reraisedFromEnd: result.add "]]\n"
+    else: addFrameEntry(result, s[i])
+
 proc auxWriteStackTrace(f: PFrame, s: var string) =
   when hasThreadSupport:
     var
@@ -177,17 +252,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
     if tempFrames[j] == nil:
       add(s, "(")
       add(s, $skipped)
-      add(s, " calls omitted) ...")
+      add(s, " calls omitted) ...\n")
     else:
-      var oldLen = s.len
-      add(s, tempFrames[j].filename)
-      if tempFrames[j].line > 0:
-        add(s, '(')
-        add(s, $tempFrames[j].line)
-        add(s, ')')
-      for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
-      add(s, tempFrames[j].procname)
-    add(s, "\n")
+      addFrameEntry(s, tempFrames[j])
 
 proc stackTraceAvailable*(): bool
 
@@ -204,6 +271,13 @@ when hasSomeStackTrace:
       auxWriteStackTraceWithBacktrace(s)
     else:
       add(s, "No stack traceback available\n")
+
+  proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
+    when NimStackTrace:
+      auxWriteStackTrace(framePtr, s)
+    else:
+      s = nil
+
   proc stackTraceAvailable(): bool =
     when NimStackTrace:
       if framePtr == nil:
@@ -223,12 +297,6 @@ proc quitOrDebug() {.inline.} =
   else:
     endbStep() # call the debugger
 
-when false:
-  proc rawRaise*(e: ref Exception) =
-    ## undocumented. Do not use.
-    pushCurrentException(e)
-    c_longjmp(excHandler.context, 1)
-
 var onUnhandledException*: (proc (errorMsg: string) {.
   nimcall.}) ## set this error \
   ## handler to override the existing behaviour on an unhandled exception.
@@ -265,7 +333,7 @@ proc raiseExceptionAux(e: ref Exception) =
       when hasSomeStackTrace:
         var buf = newStringOfCap(2000)
         if isNil(e.trace): rawWriteStackTrace(buf)
-        else: add(buf, e.trace)
+        else: add(buf, $e.trace)
         add(buf, "Error: unhandled exception: ")
         if not isNil(e.msg): add(buf, e.msg)
         add(buf, " [")
@@ -301,12 +369,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
   if e.name.isNil: e.name = ename
   when hasSomeStackTrace:
     if e.trace.isNil:
-      e.trace = ""
       rawWriteStackTrace(e.trace)
     elif framePtr != nil:
-      e.trace.add "[[reraised from:\n"
+      e.trace.add reraisedFrom(reraisedFromBegin)
       auxWriteStackTrace(framePtr, e.trace)
-      e.trace.add "]]\n"
+      e.trace.add reraisedFrom(reraisedFromEnd)
   raiseExceptionAux(e)
 
 proc reraiseException() {.compilerRtl.} =
@@ -332,10 +399,15 @@ proc getStackTrace(): string =
 
 proc getStackTrace(e: ref Exception): string =
   if not isNil(e) and not isNil(e.trace):
-    result = e.trace
+    result = $e.trace
   else:
     result = ""
 
+proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
+  ## Returns the attached stack trace to the exception ``e`` as
+  ## a ``seq``. This is not yet available for the JS backend.
+  shallowCopy(result, e.trace)
+
 when defined(nimRequiresNimFrame):
   proc stackOverflow() {.noinline.} =
     writeStackTrace()
@@ -357,7 +429,7 @@ when defined(endb):
   var
     dbgAborting: bool # whether the debugger wants to abort
 
-when not defined(noSignalHandler):
+when not defined(noSignalHandler) and not defined(useNimRtl):
   proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} =
     template processSignal(s, action: untyped) {.dirty.} =
       if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index a2ff72a30..dac06119d 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -155,14 +155,15 @@ template setColor(c, col) =
   else:
     c.refcount = c.refcount and not colorMask or col
 
-proc writeCell(msg: cstring, c: PCell) =
-  var kind = -1
-  var typName: cstring = "nil"
-  if c.typ != nil:
-    kind = ord(c.typ.kind)
-    when defined(nimTypeNames):
-      if not c.typ.name.isNil:
-        typName = c.typ.name
+when defined(logGC):
+  proc writeCell(msg: cstring, c: PCell) =
+    var kind = -1
+    var typName: cstring = "nil"
+    if c.typ != nil:
+      kind = ord(c.typ.kind)
+      when defined(nimTypeNames):
+        if not c.typ.name.isNil:
+          typName = c.typ.name
 
   when leakDetector:
     c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
@@ -317,7 +318,7 @@ proc initGC() =
       init(gch.marked)
       init(gch.additionalRoots)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 when useMarkForDebug or useBackupGc:
   type
@@ -642,9 +643,9 @@ when useMarkForDebug or useBackupGc:
         forAllChildren(d, waMarkPrecise)
 
   proc markGlobals(gch: var GcHeap) =
-    for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
     let d = gch.additionalRoots.d
-    for i in 0 .. < gch.additionalRoots.len: markS(gch, d[i])
+    for i in 0 .. gch.additionalRoots.len-1: markS(gch, d[i])
 
 when logGC:
   var
@@ -652,7 +653,7 @@ when logGC:
     cycleCheckALen = 0
 
   proc alreadySeen(c: PCell): bool =
-    for i in 0 .. <cycleCheckALen:
+    for i in 0 .. cycleCheckALen-1:
       if cycleCheckA[i] == c: return true
     if cycleCheckALen == len(cycleCheckA):
       gcAssert(false, "cycle detection overflow")
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 6dffc323e..d57a01dc7 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -133,7 +133,7 @@ proc initGC() =
     init(gch.additionalRoots)
     init(gch.greyStack)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 # Which color to use for new objects is tricky: When we're marking,
 # they have to be *white* so that everything is marked that is only
@@ -173,7 +173,7 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
 when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
-template color(c): expr = c.refCount and colorMask
+template color(c): untyped = c.refCount and colorMask
 template setColor(c, col) =
   c.refcount = c.refcount and not colorMask or col
 
@@ -487,12 +487,12 @@ proc GC_dumpHeap*(file: File) =
   var spaceIter: ObjectSpaceIter
   when false:
     var d = gch.decStack.d
-    for i in 0 .. < gch.decStack.len:
+    for i in 0 .. gch.decStack.len-1:
       if isAllocatedPtr(gch.region, d[i]):
         c_fprintf(file, "onstack %p\n", d[i])
       else:
         c_fprintf(file, "onstack_invalid %p\n", d[i])
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
   while true:
     let x = allObjectsAsProc(gch.region, addr spaceIter)
     if spaceIter.state < 0: break
@@ -579,7 +579,7 @@ proc markIncremental(gch: var GcHeap): bool =
   result = true
 
 proc markGlobals(gch: var GcHeap) =
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
 
 proc doOperation(p: pointer, op: WalkOp) =
   if p == nil: return
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index cfc0dfa8a..5fc48d848 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -233,7 +233,7 @@ proc initGC() =
       init(gch.allocated)
       init(gch.marked)
     when hasThreadSupport:
-      gch.toDispose = initSharedList[pointer]()
+      init(gch.toDispose)
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   var d = cast[ByteAddress](dest)
@@ -450,9 +450,9 @@ when false:
           quit 1
 
 proc markGlobals(gch: var GcHeap) =
-  for i in 0 .. < globalMarkersLen: globalMarkers[i]()
+  for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
   let d = gch.additionalRoots.d
-  for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i])
+  for i in 0 .. gch.additionalRoots.len-1: mark(gch, d[i])
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
diff --git a/lib/system/genodealloc.nim b/lib/system/genodealloc.nim
new file mode 100644
index 000000000..3646a842d
--- /dev/null
+++ b/lib/system/genodealloc.nim
@@ -0,0 +1,114 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Low level dataspace allocator for Genode.
+
+when not defined(genode):
+  {.error: "Genode only module".}
+
+type DataspaceCapability {.
+  importcpp: "Genode::Dataspace_capability", pure.} = object
+
+type
+  Map = object
+    attachment: pointer
+    size: int
+    ds: DataspaceCapability
+
+  SlabMeta = object
+    next: ptr MapSlab
+    ds: DataspaceCapability
+
+  MapSlab = object
+    meta: SlabMeta
+    maps: array[1,Map]
+
+const SlabBackendSize = 4096
+
+proc ramAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_ram().value".}
+  ## Return number of bytes available for allocation.
+
+proc capsAvail(): int {.
+  importcpp: "genodeEnv->pd().avail_caps().value".}
+  ## Return the number of available capabilities.
+  ## Each dataspace allocation consumes a capability.
+
+proc allocDataspace(size: int): DataspaceCapability {.
+  importcpp: "genodeEnv->pd().alloc(@)".}
+  ## Allocate a dataspace and its capability.
+
+proc attachDataspace(ds: DataspaceCapability): pointer {.
+  importcpp: "genodeEnv->rm().attach(@)".}
+  ## Attach a dataspace into the component address-space.
+
+proc detachAddress(p: pointer) {.
+  importcpp: "genodeEnv->rm().detach(@)".}
+  ## Detach a dataspace from the component address-space.
+
+proc freeDataspace(ds: DataspaceCapability) {.
+  importcpp: "genodeEnv->pd().free(@)".}
+  ## Free a dataspace.
+
+proc newMapSlab(): ptr MapSlab =
+  let
+    ds = allocDataspace SlabBackendSize
+    p = attachDataspace ds
+  result = cast[ptr MapSlab](p)
+  result.meta.ds = ds
+
+iterator items(s: ptr MapSlab): ptr Map =
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  for i in 0 .. <mapCount:
+    yield s.maps[i].addr
+
+var slabs: ptr MapSlab
+
+proc osAllocPages(size: int): pointer =
+  if slabs.isNil:
+    slabs = newMapSlab()
+  var
+    slab = slabs
+    map: ptr Map
+  let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
+  block findFreeMap:
+    while true:
+      # lookup first free spot in slabs
+      for m in slab.items:
+        if m.attachment.isNil:
+          map = m
+          break findFreeMap
+      if slab.meta.next.isNil:
+        slab.meta.next = newMapSlab()
+          # tack a new slab on the tail
+      slab = slab.meta.next
+        # move to next slab in linked list
+  map.ds = allocDataspace size
+  map.size = size
+  map.attachment = attachDataspace map.ds
+  result = map.attachment
+
+proc osTryAllocPages(size: int): pointer =
+  if ramAvail() >= size and capsAvail() > 1:
+    result = osAllocPages size
+
+proc osDeallocPages(p: pointer; size: int) =
+  var slab = slabs
+  while not slab.isNil:
+    # lookup first free spot in slabs
+    for m in slab.items:
+      if m.attachment == p:
+        if m.size != size:
+          echo "cannot partially detach dataspace"
+          quit -1
+        detachAddress m.attachment
+        freeDataspace m.ds
+        m[] = Map()
+        return
+    slab = slab.meta.next
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 24093a310..8065f2255 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -531,13 +531,13 @@ proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
 proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   when defined(nimphp):
     asm """
-      return floor(`a` / `b`);
+      return trunc(`a` / `b`);
     """
   else:
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.floor(`a` / `b`);
+      return Math.trunc(`a` / `b`);
     """
 
 proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -549,7 +549,7 @@ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
-      return Math.floor(`a` % `b`);
+      return Math.trunc(`a` % `b`);
     """
 
 proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -594,13 +594,13 @@ proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
 proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
   when defined(nimphp):
     asm """
-      return floor(`a` / `b`);
+      return trunc(`a` / `b`);
     """
   else:
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.floor(`a` / `b`);
+      return Math.trunc(`a` / `b`);
     """
 
 proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
@@ -612,7 +612,7 @@ proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
     asm """
       if (`b` == 0) `raiseDivByZero`();
       if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-      return Math.floor(`a` % `b`);
+      return Math.trunc(`a` % `b`);
     """
 
 proc negInt(a: int): int {.compilerproc.} =
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 824934966..45e0c74c0 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -42,7 +42,8 @@ type
 # Page size of the system; in most cases 4096 bytes. For exotic OS or
 # CPU this needs to be changed:
 const
-  PageShift = when defined(cpu16): 8 else: 12
+  PageShift = when defined(cpu16): 8 else: 12 # \
+    # my tests showed no improvments for using larger page sizes.
   PageSize = 1 shl PageShift
   PageMask = PageSize-1
 
@@ -108,7 +109,6 @@ when defined(boehmgc):
       if result == nil: raiseOutOfMem()
     proc alloc0(size: Natural): pointer =
       result = alloc(size)
-      zeroMem(result, size)
     proc realloc(p: pointer, newsize: Natural): pointer =
       result = boehmRealloc(p, newsize)
       if result == nil: raiseOutOfMem()
@@ -118,8 +118,7 @@ when defined(boehmgc):
       result = boehmAlloc(size)
       if result == nil: raiseOutOfMem()
     proc allocShared0(size: Natural): pointer =
-      result = alloc(size)
-      zeroMem(result, size)
+      result = allocShared(size)
     proc reallocShared(p: pointer, newsize: Natural): pointer =
       result = boehmRealloc(p, newsize)
       if result == nil: raiseOutOfMem()
@@ -343,7 +342,6 @@ elif defined(gogc):
 
   const goFlagNoZero: uint32 = 1 shl 3
   proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
-  proc goFree(v: pointer) {.importc: "__go_free", dynlib: goLib.}
 
   proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
 
@@ -376,7 +374,6 @@ elif defined(gogc):
     result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
     copyMem(result, old, oldsize)
     zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
-    goFree(old)
 
   proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
   proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
@@ -573,3 +570,11 @@ when not declared(nimNewSeqOfCap):
     cast[PGenericSeq](result).reserved = cap
 
 {.pop.}
+
+when not declared(ForeignCell):
+  type ForeignCell* = object
+    data*: pointer
+
+  proc protect*(x: pointer): ForeignCell = ForeignCell(data: x)
+  proc dispose*(x: ForeignCell) = discard
+  proc isNotForeign*(x: ForeignCell): bool = false
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index f5b9cf3ed..f91dae41e 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -106,7 +106,7 @@ proc cmpic*(a, b: string): int =
   ## Compares `a` and `b` ignoring case.
   cmpIgnoreCase(a, b)
 
-proc getEnv*(key: string): string {.tags: [ReadIOEffect].} =
+proc getEnv*(key: string; default = ""): string {.tags: [ReadIOEffect].} =
   ## Retrieves the environment variable of name `key`.
   builtin
 
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 65a057772..1ad4cf695 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -78,17 +78,7 @@ when defined(emscripten):
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
 
 elif defined(genode):
-
-  proc osAllocPages(size: int): pointer {.
-   importcpp: "genodeEnv->rm().attach(genodeEnv->ram().alloc(@))".}
-
-  proc osTryAllocPages(size: int): pointer =
-    {.emit: """try {""".}
-    result = osAllocPages size
-    {.emit: """} catch (...) { }""".}
-
-  proc osDeallocPages(p: pointer, size: int) {.
-    importcpp: "genodeEnv->rm().detach(#)".}
+  include genodealloc # osAllocPages, osTryAllocPages, osDeallocPages
 
 elif defined(posix):
   const
@@ -166,7 +156,7 @@ elif defined(windows):
     # space heavily, so we now treat Windows as a strange unmap target.
     when reallyOsDealloc:
       if virtualFree(p, 0, MEM_RELEASE) == 0:
-        cprintf "yes, failing!"
+        cprintf "virtualFree failing!"
         quit 1
     #VirtualFree(p, size, MEM_DECOMMIT)
 
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 19fa564fb..982b07467 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -41,14 +41,14 @@ proc `$`(x: uint64): string =
 
     let half = i div 2
     # Reverse
-    for t in 0 .. < half: swap(result[t], result[i-t-1])
+    for t in 0 .. half-1: swap(result[t], result[i-t-1])
 
 proc reprStrAux(result: var string, s: cstring; len: int) =
   if cast[pointer](s) == nil:
     add result, "nil"
     return
   add result, reprPointer(cast[pointer](s)) & "\""
-  for i in 0.. <len:
+  for i in 0 .. pred(len):
     let c = s[i]
     case c
     of '"': add result, "\\\""
diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim
index 5c265a891..658220c11 100644
--- a/lib/system/reprjs.nim
+++ b/lib/system/reprjs.nim
@@ -9,13 +9,13 @@
 # The generic ``repr`` procedure for the javascript backend.
 
 proc reprInt(x: int64): string {.compilerproc.} = return $x
-proc reprFloat(x: float): string {.compilerproc.} = 
+proc reprFloat(x: float): string {.compilerproc.} =
   # Js toString doesn't differentiate between 1.0 and 1,
   # but we do.
   if $x == $(x.int): $x & ".0"
   else: $x
 
-proc reprPointer(p: pointer): string {.compilerproc.} = 
+proc reprPointer(p: pointer): string {.compilerproc.} =
   # Do we need to generate the full 8bytes ? In js a pointer is an int anyway
   var tmp: int
   {. emit: """
@@ -38,7 +38,7 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
     result = $typ.node.sons[e].name
   else:
     result = $e & " (invalid data!)"
-  
+
 proc reprChar(x: char): string {.compilerRtl.} =
   result = "\'"
   case x
@@ -50,7 +50,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
 
 proc reprStrAux(result: var string, s: cstring, len: int) =
   add(result, "\"")
-  for i in 0 .. <len:
+  for i in 0 .. len-1:
     let c = s[i]
     case c
     of '"': add(result, "\\\"")
@@ -67,7 +67,7 @@ proc reprStr(s: string): string {.compilerRtl.} =
   if cast[pointer](s).isNil:
     # Handle nil strings here because they don't have a length field in js
     # TODO: check for null/undefined before generating call to length in js?
-    # Also: c backend repr of a nil string is <pointer>"", but repr of an 
+    # Also: c backend repr of a nil string is <pointer>"", but repr of an
     # array of string that is not initialized is [nil, nil, ...] ??
     add(result, "nil")
   else:
@@ -86,7 +86,7 @@ proc addSetElem(result: var string, elem: int, typ: PNimType) =
 
 iterator setKeys(s: int): int {.inline.} =
   # The type of s is a lie, but it's expected to be a set.
-  # Iterate over the JS object representing a set 
+  # Iterate over the JS object representing a set
   # and returns the keys as int.
   var len: int
   var yieldRes: int
@@ -124,16 +124,16 @@ proc initReprClosure(cl: var ReprClosure) =
   cl.recDepth = -1 # default is to display everything!
   cl.indent = 0
 
-proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure) 
+proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
 
-proc reprArray(a: pointer, typ: PNimType, 
+proc reprArray(a: pointer, typ: PNimType,
               cl: var ReprClosure): string {.compilerRtl.} =
   var isNilArrayOrSeq: bool
   # isnil is not enough here as it would try to deref `a` without knowing what's inside
   {. emit: """
-    if (`a` == null) { 
+    if (`a` == null) {
       `isNilArrayOrSeq` = true;
-    } else if (`a`[0] == null) { 
+    } else if (`a`[0] == null) {
       `isNilArrayOrSeq` = true;
     } else {
       `isNilArrayOrSeq` = false;
@@ -146,19 +146,19 @@ proc reprArray(a: pointer, typ: PNimType,
   result = if typ.kind == tySequence: "@[" else: "["
   var len: int = 0
   var i: int = 0
-    
+
   {. emit: "`len` = `a`.length;\n" .}
   var dereffed: pointer = a
-  for i in 0 .. < len:
+  for i in 0 .. len-1:
     if i > 0 :
       add(result, ", ")
     # advance pointer and point to element at index
     {. emit: """
-    `dereffed`_Idx = `i`; 
+    `dereffed`_Idx = `i`;
     `dereffed` = `a`[`dereffed`_Idx];
     """ .}
     reprAux(result, dereffed, typ.base, cl)
-  
+
   add(result, "]")
 
 proc isPointedToNil(p: pointer): bool {.inline.}=
@@ -181,7 +181,7 @@ proc reprRef(result: var string, p: pointer, typ: PNimType,
 
 proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
   add(result, "[")
-  
+
   var first: bool = true
   var val: pointer = o
   if typ.node.len == 0:
@@ -192,7 +192,7 @@ proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprCl
     reprAux(result, val, typ.node.typ, cl)
   else:
     # if the object has more than one field, sons is not nil and contains the fields.
-    for i in 0 .. <typ.node.len:
+    for i in 0 .. typ.node.len-1:
       if first: first = false
       else: add(result, ",\n")
 
@@ -214,11 +214,11 @@ proc reprJSONStringify(p: int): string {.compilerRtl.} =
   {. emit: "`tmp` = JSON.stringify(`p`);\n" .}
   result = $tmp
 
-proc reprAux(result: var string, p: pointer, typ: PNimType, 
+proc reprAux(result: var string, p: pointer, typ: PNimType,
             cl: var ReprClosure) =
   if cl.recDepth == 0:
     add(result, "...")
-    return 
+    return
   dec(cl.recDepth)
   case typ.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 4f266e0ae..4348ffbb5 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -192,7 +192,7 @@ proc write(f: File, r: float32) =
 proc write(f: File, r: BiggestFloat) =
   if c_fprintf(f, "%g", r) < 0: checkErr(f)
 
-proc write(f: File, c: char) = discard c_putc(ord(c), f)
+proc write(f: File, c: char) = discard c_putc(cint(c), f)
 proc write(f: File, a: varargs[string, `$`]) =
   for x in items(a): write(f, x)
 
@@ -404,4 +404,18 @@ proc setStdIoUnbuffered() =
   when declared(stdin):
     discard c_setvbuf(stdin, nil, IONBF, 0)
 
+when declared(stdout):
+  proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
+    when not defined(windows):
+      proc flockfile(f: File) {.importc, noDecl.}
+      proc funlockfile(f: File) {.importc, noDecl.}
+      flockfile(stdout)
+    for s in args:
+      discard c_fwrite(s.cstring, s.len, 1, stdout)
+    const linefeed = "\n" # can be 1 or more chars
+    discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
+    discard c_fflush(stdout)
+    when not defined(windows):
+      funlockfile(stdout)
+
 {.pop.}
diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim
index 7da45b4dd..dc2d13578 100644
--- a/lib/system/sysspawn.nim
+++ b/lib/system/sysspawn.nim
@@ -142,7 +142,7 @@ var
   workersData: array[NumThreads, Worker]
 
 proc setup() =
-  for i in 0.. <NumThreads:
+  for i in 0 ..< NumThreads:
     workersData[i].taskArrived = createCondVar()
     workersData[i].taskStarted = createFastCondVar()
     createThread(workers[i], slave, addr(workersData[i]))
@@ -153,12 +153,12 @@ proc preferSpawn*(): bool =
   ## it is not necessary to call this directly; use 'spawnX' instead.
   result = gSomeReady.event
 
-proc spawn*(call: stmt) {.magic: "Spawn".}
+proc spawn*(call: typed) {.magic: "Spawn".}
   ## always spawns a new task, so that the 'call' is never executed on
   ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has 'void' as the return type.
 
-template spawnX*(call: stmt) =
+template spawnX*(call: typed) =
   ## spawns a new task if a CPU core is ready, otherwise executes the
   ## call in the calling thread. Usually it is advised to
   ## use 'spawn' in order to not block the producer for an unknown
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 43b5a0292..4c5f3d9a1 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
   if a == nil: return -1
   if b == nil: return 1
-  when defined(nimNoArrayToCstringConversion):
-    return c_strcmp(addr a.data, addr b.data)
-  else:
-    return c_strcmp(a.data, b.data)
+  let minlen = min(a.len, b.len)
+  result = c_memcmp(addr a.data, addr b.data, minlen.csize)
+  if result == 0:
+    result = a.len - b.len
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true
@@ -259,7 +259,7 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
     result.reserved = r
 
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
-    compilerRtl.} =
+    compilerRtl, inl.} =
   result = seq
   if result.space < newLen:
     let r = max(resize(result.space), newLen)
@@ -278,14 +278,15 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
                             GenericSeqSize +% (i*%elemSize)),
                             extGetCellType(result).base, waPush)
           let len1 = gch.tempStack.len
-          for i in len0 .. <len1:
+          for i in len0 ..< len1:
             doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
           gch.tempStack.len = len0
       else:
-        for i in newLen..result.len-1:
-          forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
-                            GenericSeqSize +% (i*%elemSize)),
-                            extGetCellType(result).base, waZctDecRef)
+        if ntfNoRefs notin extGetCellType(result).base.flags:
+          for i in newLen..result.len-1:
+            forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
+                              GenericSeqSize +% (i*%elemSize)),
+                              extGetCellType(result).base, waZctDecRef)
 
     # XXX: zeroing out the memory can still result in crashes if a wiped-out
     # cell is aliased by another pointer (ie proc parameter or a let variable).
@@ -322,36 +323,40 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
   result.add x
 
 proc add*(result: var string; x: float) =
-  var buf: array[0..64, char]
-  when defined(nimNoArrayToCstringConversion):
-    var n: int = c_sprintf(addr buf, "%.16g", x)
+  when nimvm:
+    result.add $x
   else:
-    var n: int = c_sprintf(buf, "%.16g", x)
-  var hasDot = false
-  for i in 0..n-1:
-    if buf[i] == ',':
-      buf[i] = '.'
-      hasDot = true
-    elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
-      hasDot = true
-  if not hasDot:
-    buf[n] = '.'
-    buf[n+1] = '0'
-    buf[n+2] = '\0'
-  # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
-  # We want to get rid of these here:
-  if buf[n-1] in {'n', 'N'}:
-    result.add "nan"
-  elif buf[n-1] == 'F':
-    if buf[0] == '-':
-      result.add "-inf"
+    var buf: array[0..64, char]
+    when defined(nimNoArrayToCstringConversion):
+      var n: int = c_sprintf(addr buf, "%.16g", x)
     else:
-      result.add "inf"
-  else:
-    var i = 0
-    while buf[i] != '\0':
-      result.add buf[i]
-      inc i
+      var n: int = c_sprintf(buf, "%.16g", x)
+    var hasDot = false
+    for i in 0..n-1:
+      if buf[i] == ',':
+        buf[i] = '.'
+        hasDot = true
+      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+        hasDot = true
+    if not hasDot:
+      buf[n] = '.'
+      buf[n+1] = '0'
+      buf[n+2] = '\0'
+    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
+    # of '-1.#IND' are produced.
+    # We want to get rid of these here:
+    if buf[n-1] in {'n', 'N', 'D', 'd'}:
+      result.add "nan"
+    elif buf[n-1] == 'F':
+      if buf[0] == '-':
+        result.add "-inf"
+      else:
+        result.add "inf"
+    else:
+      var i = 0
+      while buf[i] != '\0':
+        result.add buf[i]
+        inc i
 
 proc nimFloatToStr(f: float): string {.compilerproc.} =
   result = newStringOfCap(8)
@@ -362,9 +367,9 @@ proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
 
 const
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
-  powtens =   [ 1e0,   1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,
-                1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-                1e20, 1e21, 1e22]
+  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+              1e20, 1e21, 1e22]
 
 proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
                           start = 0): int {.compilerProc.} =
@@ -508,7 +513,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # insert exponent
   t[ti] = 'E'; inc(ti)
-  t[ti] = if exp_negative: '-' else: '+'; inc(ti)
+  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
   inc(ti, 3)
 
   # insert adjusted exponent
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 96c045e6b..f61cc4280 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -255,9 +255,9 @@ when emulatedThreadVars:
   proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
 
 # we preallocate a fixed size for thread local storage, so that no heap
-# allocations are needed. Currently less than 7K are used on a 64bit machine.
+# allocations are needed. Currently less than 16K are used on a 64bit machine.
 # We use ``float`` for proper alignment:
-const nimTlsSize {.intdefine.} = 8000
+const nimTlsSize {.intdefine.} = 16000
 type
   ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
 
@@ -398,7 +398,7 @@ template afterThreadRuns() =
     threadDestructionHandlers[i]()
 
 when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
-  proc deallocOsPages()
+  proc deallocOsPages() {.rtl.}
 
 when defined(boehmgc):
   type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index c3229cc7b..a833377e5 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -111,6 +111,7 @@ const
   WAIT_TIMEOUT* = 0x00000102'i32
   WAIT_FAILED* = 0xFFFFFFFF'i32
   INFINITE* = -1'i32
+  STILL_ACTIVE* = 0x00000103'i32
 
   STD_INPUT_HANDLE* = -10'i32
   STD_OUTPUT_HANDLE* = -11'i32
@@ -541,6 +542,7 @@ var
   SO_DONTLINGER* {.importc, header: "winsock2.h".}: cint
   SO_EXCLUSIVEADDRUSE* {.importc, header: "winsock2.h".}: cint # disallow local address reuse
   SO_ERROR* {.importc, header: "winsock2.h".}: cint
+  TCP_NODELAY* {.importc, header: "winsock2.h".}: cint
 
 proc `==`*(x, y: SocketHandle): bool {.borrow.}
 
diff --git a/lib/wrappers/libuv.nim b/lib/wrappers/libuv.nim
deleted file mode 100644
index 3e28815ad..000000000
--- a/lib/wrappers/libuv.nim
+++ /dev/null
@@ -1,732 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## libuv is still fast moving target
-## This file was last updated against a development HEAD revision of https://github.com/joyent/libuv/
-
-## Use the following link to see changes (in uv.h) since then and don't forget to update the information here.
-## https://github.com/joyent/libuv/compare/9f6024a6fa9d254527b4b59af724257df870288b...master
-
-when defined(Windows):
-  import winlean
-else:
-  import posix
-
-type
-  Port* = distinct int16  ## port type
-
-  cssize = int
-  coff = int
-  csize = int
-
-  AllocProc* = proc (handle: PHandle, suggested_size: csize): Buf {.cdecl.}
-  ReadProc* = proc (stream: PStream, nread: cssize, buf: Buf) {.cdecl.}
-  ReadProc2* = proc (stream: PPipe, nread: cssize, buf: Buf, pending: HandleType) {.cdecl.}
-  WriteProc* = proc (req: PWrite, status: cint) {.cdecl.}
-  ConnectProc* = proc (req: PConnect, status: cint) {.cdecl.}
-  ShutdownProc* = proc (req: PShutdown, status: cint) {.cdecl.}
-  ConnectionProc* = proc (server: PStream, status: cint) {.cdecl.}
-  CloseProc* = proc (handle: PHandle) {.cdecl.}
-  TimerProc* = proc (handle: PTimer, status: cint) {.cdecl.}
-  AsyncProc* = proc (handle: PAsync, status: cint) {.cdecl.}
-  PrepareProc* = proc (handle: PPrepare, status: cint) {.cdecl.}
-  CheckProc* = proc (handle: PCheck, status: cint) {.cdecl.}
-  IdleProc* = proc (handle: PIdle, status: cint) {.cdecl.}
-
-  PSockAddr* = ptr SockAddr
-
-  GetAddrInfoProc* = proc (handle: PGetAddrInfo, status: cint, res: ptr AddrInfo)
-
-  ExitProc* = proc (a2: PProcess, exit_status: cint, term_signal: cint)
-  FsProc* = proc (req: PFS)
-  WorkProc* = proc (req: PWork)
-  AfterWorkProc* = proc (req: PWork)
-
-  FsEventProc* = proc (handle: PFsEvent, filename: cstring, events: cint, status: cint)
-
-  ErrorCode* {.size: sizeof(cint).} = enum
-    UNKNOWN = - 1, OK = 0, EOF, EACCESS, EAGAIN, EADDRINUSE, EADDRNOTAVAIL,
-    EAFNOSUPPORT, EALREADY, EBADF, EBUSY, ECONNABORTED, ECONNREFUSED,
-    ECONNRESET, EDESTADDRREQ, EFAULT, EHOSTUNREACH, EINTR, EINVAL, EISCONN,
-    EMFILE, EMSGSIZE, ENETDOWN, ENETUNREACH, ENFILE, ENOBUFS, ENOMEM, ENONET,
-    ENOPROTOOPT, ENOTCONN, ENOTSOCK, ENOTSUP, ENOENT, EPIPE, EPROTO,
-    EPROTONOSUPPORT, EPROTOTYPE, ETIMEDOUT, ECHARSET, EAIFAMNOSUPPORT,
-    EAINONAME, EAISERVICE, EAISOCKTYPE, ESHUTDOWN, EEXIST
-
-  HandleType* {.size: sizeof(cint).} = enum
-    UNKNOWN_HANDLE = 0, TCP, UDP, NAMED_PIPE, TTY, FILE, TIMER, PREPARE, CHECK,
-    IDLE, ASYNC, ARES_TASK, ARES_EVENT, PROCESS, FS_EVENT
-
-  ReqType* {.size: sizeof(cint).} = enum
-    rUNKNOWN_REQ = 0,
-    rCONNECT,
-    rACCEPT,
-    rREAD,
-    rWRITE,
-    rSHUTDOWN,
-    rWAKEUP,
-    rUDP_SEND,
-    rFS,
-    rWORK,
-    rGETADDRINFO,
-    rREQ_TYPE_PRIVATE
-
-  Err* {.pure, final, importc: "uv_err_t", header: "uv.h".} = object
-    code* {.importc: "code".}: ErrorCode
-    sys_errno* {.importc: "sys_errno_".}: cint
-
-  FsEventType* = enum
-    evRENAME = 1,
-    evCHANGE = 2
-
-  TFsEvent* {.pure, final, importc: "uv_fs_event_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    filename {.importc: "filename".}: cstring
-
-  PFsEvent* = ptr TFsEvent
-
-  FsEvents* {.pure, final, importc: "uv_fs_event_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    filename* {.importc: "filename".}: cstring
-
-  Buf* {.pure, final, importc: "uv_buf_t", header: "uv.h"} = object
-    base* {.importc: "base".}: cstring
-    len* {.importc: "len".}: csize
-
-  AnyHandle* {.pure, final, importc: "uv_any_handle", header: "uv.h".} = object
-    tcp* {.importc: "tcp".}: TTcp
-    pipe* {.importc: "pipe".}: Pipe
-    prepare* {.importc: "prepare".}: TPrepare
-    check* {.importc: "check".}: TCheck
-    idle* {.importc: "idle".}: TIdle
-    async* {.importc: "async".}: TAsync
-    timer* {.importc: "timer".}: TTimer
-    getaddrinfo* {.importc: "getaddrinfo".}: Getaddrinfo
-    fs_event* {.importc: "fs_event".}: FsEvents
-
-  AnyReq* {.pure, final, importc: "uv_any_req", header: "uv.h".} = object
-    req* {.importc: "req".}: Req
-    write* {.importc: "write".}: Write
-    connect* {.importc: "connect".}: Connect
-    shutdown* {.importc: "shutdown".}: Shutdown
-    fs_req* {.importc: "fs_req".}: Fs
-    work_req* {.importc: "work_req".}: Work
-
-  ## better import this
-  uint64 = int64
-
-  Counters* {.pure, final, importc: "uv_counters_t", header: "uv.h".} = object
-    eio_init* {.importc: "eio_init".}: uint64
-    req_init* {.importc: "req_init".}: uint64
-    handle_init* {.importc: "handle_init".}: uint64
-    stream_init* {.importc: "stream_init".}: uint64
-    tcp_init* {.importc: "tcp_init".}: uint64
-    udp_init* {.importc: "udp_init".}: uint64
-    pipe_init* {.importc: "pipe_init".}: uint64
-    tty_init* {.importc: "tty_init".}: uint64
-    prepare_init* {.importc: "prepare_init".}: uint64
-    check_init* {.importc: "check_init".}: uint64
-    idle_init* {.importc: "idle_init".}: uint64
-    async_init* {.importc: "async_init".}: uint64
-    timer_init* {.importc: "timer_init".}: uint64
-    process_init* {.importc: "process_init".}: uint64
-    fs_event_init* {.importc: "fs_event_init".}: uint64
-
-  Loop* {.pure, final, importc: "uv_loop_t", header: "uv.h".} = object
-    # ares_handles_* {.importc: "uv_ares_handles_".}: pointer # XXX: This seems to be a private field?
-    eio_want_poll_notifier* {.importc: "uv_eio_want_poll_notifier".}: TAsync
-    eio_done_poll_notifier* {.importc: "uv_eio_done_poll_notifier".}: TAsync
-    eio_poller* {.importc: "uv_eio_poller".}: TIdle
-    counters* {.importc: "counters".}: Counters
-    last_err* {.importc: "last_err".}: Err
-    data* {.importc: "data".}: pointer
-
-  PLoop* = ptr Loop
-
-  Shutdown* {.pure, final, importc: "uv_shutdown_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    handle* {.importc: "handle".}: PStream
-    cb* {.importc: "cb".}: ShutdownProc
-
-  PShutdown* = ptr Shutdown
-
-  Handle* {.pure, final, importc: "uv_handle_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PHandle* = ptr Handle
-
-  Stream* {.pure, final, importc: "uv_stream_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    alloc_cb* {.importc: "alloc_cb".}: AllocProc
-    read_cb* {.importc: "read_cb".}: ReadProc
-    read2_cb* {.importc: "read2_cb".}: ReadProc2
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    write_queue_size* {.importc: "write_queue_size".}: csize
-
-  PStream* = ptr Stream
-
-  Write* {.pure, final, importc: "uv_write_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    cb* {.importc: "cb".}: WriteProc
-    send_handle* {.importc: "send_handle".}: PStream
-    handle* {.importc: "handle".}: PStream
-
-  PWrite* = ptr Write
-
-  TTcp* {.pure, final, importc: "uv_tcp_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    alloc_cb* {.importc: "alloc_cb".}: AllocProc
-    read_cb* {.importc: "read_cb".}: ReadProc
-    read2_cb* {.importc: "read2_cb".}: ReadProc2
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    write_queue_size* {.importc: "write_queue_size".}: csize
-
-  PTcp* = ptr TTcp
-
-  Connect* {.pure, final, importc: "uv_connect_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    cb* {.importc: "cb".}: ConnectProc
-    handle* {.importc: "handle".}: PStream
-
-  PConnect* = ptr Connect
-
-  UdpFlags* = enum
-    UDP_IPV6ONLY = 1, UDP_PARTIAL = 2
-
-  ## XXX: better import this
-  cunsigned = int
-
-  UdpSendProc* = proc (req: PUdpSend, status: cint)
-  UdpRecvProc* = proc (handle: PUdp, nread: cssize, buf: Buf, adr: ptr SockAddr, flags: cunsigned)
-
-  TUdp* {.pure, final, importc: "uv_udp_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PUdp* = ptr TUdp
-
-  UdpSend* {.pure, final, importc: "uv_udp_send_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    handle* {.importc: "handle".}: PUdp
-    cb* {.importc: "cb".}: UdpSendProc
-
-  PUdpSend* = ptr UdpSend
-
-  tTTy* {.pure, final, importc: "uv_tty_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    alloc_cb* {.importc: "alloc_cb".}: AllocProc
-    read_cb* {.importc: "read_cb".}: ReadProc
-    read2_cb* {.importc: "read2_cb".}: ReadProc2
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    write_queue_size* {.importc: "write_queue_size".}: csize
-
-  pTTy* = ptr tTTy
-
-  Pipe* {.pure, final, importc: "uv_pipe_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    alloc_cb* {.importc: "alloc_cb".}: AllocProc
-    read_cb* {.importc: "read_cb".}: ReadProc
-    read2_cb* {.importc: "read2_cb".}: ReadProc2
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    write_queue_size* {.importc: "write_queue_size".}: csize
-    ipc {.importc: "ipc".}: int
-
-  PPipe* = ptr Pipe
-
-  TPrepare* {.pure, final, importc: "uv_prepare_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PPrepare* = ptr TPrepare
-
-  TCheck* {.pure, final, importc: "uv_check_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PCheck* = ptr TCheck
-
-  TIdle* {.pure, final, importc: "uv_idle_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PIdle* = ptr TIdle
-
-  TAsync* {.pure, final, importc: "uv_async_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PAsync* = ptr TAsync
-
-  TTimer* {.pure, final, importc: "uv_timer_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-
-  PTimer* = ptr TTimer
-
-  GetAddrInfo* {.pure, final, importc: "uv_getaddrinfo_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    loop* {.importc: "loop".}: PLoop
-
-  PGetAddrInfo* = ptr GetAddrInfo
-
-  ProcessOptions* {.pure, final, importc: "uv_process_options_t", header: "uv.h".} = object
-    exit_cb* {.importc: "exit_cb".}: ExitProc
-    file* {.importc: "file".}: cstring
-    args* {.importc: "args".}: cstringArray
-    env* {.importc: "env".}: cstringArray
-    cwd* {.importc: "cwd".}: cstring
-    windows_verbatim_arguments* {.importc: "windows_verbatim_arguments".}: cint
-    stdin_stream* {.importc: "stdin_stream".}: PPipe
-    stdout_stream* {.importc: "stdout_stream".}: PPipe
-    stderr_stream* {.importc: "stderr_stream".}: PPipe
-
-  PProcessOptions* = ptr ProcessOptions
-
-  TProcess* {.pure, final, importc: "uv_process_t", header: "uv.h".} = object
-    loop* {.importc: "loop".}: PLoop
-    typ* {.importc: "type".}: HandleType
-    close_cb* {.importc: "close_cb".}: CloseProc
-    data* {.importc: "data".}: pointer
-    exit_cb* {.importc: "exit_cb".}: ExitProc
-    pid* {.importc: "pid".}: cint
-
-  PProcess* = ptr TProcess
-
-  Work* {.pure, final, importc: "uv_work_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    loop* {.importc: "loop".}: PLoop
-    work_cb* {.importc: "work_cb".}: WorkProc
-    after_work_cb* {.importc: "after_work_cb".}: AfterWorkProc
-
-  PWork* = ptr Work
-
-  FsType* {.size: sizeof(cint).} = enum
-    FS_UNKNOWN = - 1, FS_CUSTOM, FS_OPEN, FS_CLOSE, FS_READ, FS_WRITE,
-    FS_SENDFILE, FS_STAT, FS_LSTAT, FS_FSTAT, FS_FTRUNCATE, FS_UTIME, FS_FUTIME,
-    FS_CHMOD, FS_FCHMOD, FS_FSYNC, FS_FDATASYNC, FS_UNLINK, FS_RMDIR, FS_MKDIR,
-    FS_RENAME, FS_READDIR, FS_LINK, FS_SYMLINK, FS_READLINK, FS_CHOWN, FS_FCHOWN
-
-  FS* {.pure, final, importc: "uv_fs_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-    loop* {.importc: "loop".}: PLoop
-    fs_type* {.importc: "fs_type".}: FsType
-    cb* {.importc: "cb".}: FsProc
-    result* {.importc: "result".}: cssize
-    fsPtr* {.importc: "ptr".}: pointer
-    path* {.importc: "path".}: cstring
-    errorno* {.importc: "errorno".}: cint
-
-  PFS* = ptr FS
-
-  Req* {.pure, final, importc: "uv_req_t", header: "uv.h".} = object
-    typ* {.importc: "type".}: ReqType
-    data* {.importc: "data".}: pointer
-
-  PReq* = ptr Req
-
-  AresOptions* {.pure, final, importc: "ares_options", header: "uv.h".} = object
-    flags* {.importc: "flags".}: int
-    timeout* {.importc: "timeout".}: int
-    tries* {.importc: "tries".}: int
-    ndots* {.importc: "ndots".}: int
-    udp_port* {.importc: "udp_port".}: Port
-    tcp_port* {.importc: "tcp_port".}: Port
-    socket_send_buffer_size* {.importc: "socket_send_buffer_size".}: int
-    socket_recv_buffer_size* {.importc: "socket_receive_buffer_size".}: int
-    servers* {.importc: "servers".}: ptr InAddr
-    nservers* {.importc: "nservers".}: int
-    domains* {.importc: "domains".}: ptr cstring
-    ndomains* {.importc: "ndomains".}: int
-    lookups* {.importc: "lookups".}: cstring
-
-  #XXX: not yet exported
-  #ares_sock_state_cb sock_state_cb;
-  #void *sock_state_cb_data;
-  #struct apattern *sortlist;
-  #int nsort;
-
-  PAresOptions* = ptr AresOptions
-  PAresChannel* = pointer
-{.deprecated: [THandle: Handle, THandleType: HandleType, TAnyHandle: AnyHandle,
-              TAnyReq: AnyReq, TPort: Port, TErrorCode: ErrorCode, TReqType: ReqType,
-              TErr: Err, TFsEventType: FsEventType,
-              # TFsEvent: FsEvent, # Name conflict if we drop `T`
-              TFsEvents: FsEvents, TBuf: Buf, TCounters: Counters, TLoop: Loop,
-              TShutdown: Shutdown, TStream: Stream, TWrite: Write,
-              # TTcp: Tcp, # Name conflict if we drop `T`
-              TConnect: Connect,
-              TUdpFlags: UdpFlags,
-              # TUdp: Udp, # Name conflict if we drop `T`
-              TUdpSend: UdpSend,
-              # tTTy: TTy, # Name conflict if we drop `T`
-              TPipe: Pipe,
-              # TPrepare: Prepare, # Name conflict if we drop `T`
-              # TCheck: Check, # Name conflict if we drop `T`
-              # TIdle: Idle, # Name conflict if we drop `T`
-              # TAsync: Async, # Name conflict if we drop `T`
-              # TTimer: Timer, # Name conflict if we drop `T`
-              TGetAddrInfo: GetAddrInfo, TProcessOptions: ProcessOptions,
-              # TProcess: Process, # Name conflict if we drop `T`
-              TWork: Work,
-              TFsType: FsType, TFS: FS, TReq: Req, TAresOptions: AresOptions].}
-
-proc loop_new*(): PLoop{.
-    importc: "uv_loop_new", header: "uv.h".}
-
-proc loop_delete*(a2: PLoop){.
-    importc: "uv_loop_delete", header: "uv.h".}
-
-proc default_loop*(): PLoop{.
-    importc: "uv_default_loop", header: "uv.h".}
-
-proc run*(a2: PLoop): cint{.
-    importc: "uv_run", header: "uv.h".}
-
-proc addref*(a2: PLoop){.
-    importc: "uv_ref", header: "uv.h".}
-
-proc unref*(a2: PLoop){.
-    importc: "uv_unref", header: "uv.h".}
-
-proc update_time*(a2: PLoop){.
-    importc: "uv_update_time", header: "uv.h".}
-
-proc now*(a2: PLoop): int64{.
-    importc: "uv_now", header: "uv.h".}
-
-proc last_error*(a2: PLoop): Err{.
-    importc: "uv_last_error", header: "uv.h".}
-
-proc strerror*(err: Err): cstring{.
-    importc: "uv_strerror", header: "uv.h".}
-
-proc err_name*(err: Err): cstring{.
-    importc: "uv_err_name", header: "uv.h".}
-
-proc shutdown*(req: PShutdown, handle: PStream, cb: ShutdownProc): cint{.
-    importc: "uv_shutdown", header: "uv.h".}
-
-proc is_active*(handle: PHandle): cint{.
-    importc: "uv_is_active", header: "uv.h".}
-
-proc close*(handle: PHandle, close_cb: CloseProc){.
-    importc: "uv_close", header: "uv.h".}
-
-proc buf_init*(base: cstring, len: csize): Buf{.
-    importc: "uv_buf_init", header: "uv.h".}
-
-proc listen*(stream: PStream, backlog: cint, cb: ConnectionProc): cint{.
-    importc: "uv_listen", header: "uv.h".}
-
-proc accept*(server: PStream, client: PStream): cint{.
-    importc: "uv_accept", header: "uv.h".}
-
-proc read_start*(a2: PStream, alloc_cb: AllocProc, read_cb: ReadProc): cint{.
-    importc: "uv_read_start", header: "uv.h".}
-
-proc read_start*(a2: PStream, alloc_cb: AllocProc, read_cb: ReadProc2): cint{.
-    importc: "uv_read2_start", header: "uv.h".}
-
-proc read_stop*(a2: PStream): cint{.
-    importc: "uv_read_stop", header: "uv.h".}
-
-proc write*(req: PWrite, handle: PStream, bufs: ptr Buf, bufcnt: cint, cb: WriteProc): cint{.
-    importc: "uv_write", header: "uv.h".}
-
-proc write*(req: PWrite, handle: PStream, bufs: ptr Buf, bufcnt: cint, send_handle: PStream, cb: WriteProc): cint{.
-    importc: "uv_write2", header: "uv.h".}
-
-proc tcp_init*(a2: PLoop, handle: PTcp): cint{.
-    importc: "uv_tcp_init", header: "uv.h".}
-
-proc tcp_bind*(handle: PTcp, a3: SockAddrIn): cint{.
-    importc: "uv_tcp_bind", header: "uv.h".}
-
-proc tcp_bind6*(handle: PTcp, a3: TSockAddrIn6): cint{.
-    importc: "uv_tcp_bind6", header: "uv.h".}
-
-proc tcp_getsockname*(handle: PTcp, name: ptr SockAddr, namelen: var cint): cint{.
-    importc: "uv_tcp_getsockname", header: "uv.h".}
-
-proc tcp_getpeername*(handle: PTcp, name: ptr SockAddr, namelen: var cint): cint{.
-    importc: "uv_tcp_getpeername", header: "uv.h".}
-
-proc tcp_connect*(req: PConnect, handle: PTcp, address: SockAddrIn, cb: ConnectProc): cint{.
-    importc: "uv_tcp_connect", header: "uv.h".}
-
-proc tcp_connect6*(req: PConnect, handle: PTcp, address: TSockAddrIn6, cb: ConnectProc): cint{.
-    importc: "uv_tcp_connect6", header: "uv.h".}
-
-proc udp_init*(a2: PLoop, handle: PUdp): cint{.
-    importc: "uv_udp_init", header: "uv.h".}
-
-proc udp_bind*(handle: PUdp, adr: SockAddrIn, flags: cunsigned): cint{.
-    importc: "uv_udp_bind", header: "uv.h".}
-
-proc udp_bind6*(handle: PUdp, adr: TSockAddrIn6, flags: cunsigned): cint{.
-    importc: "uv_udp_bind6", header: "uv.h".}
-
-proc udp_getsockname*(handle: PUdp, name: ptr SockAddr, namelen: var cint): cint{.
-    importc: "uv_udp_getsockname", header: "uv.h".}
-
-proc udp_send*(req: PUdpSend, handle: PUdp, bufs: ptr Buf, bufcnt: cint, adr: SockAddrIn, send_cb: UdpSendProc): cint{.
-    importc: "uv_udp_send", header: "uv.h".}
-
-proc udp_send6*(req: PUdpSend, handle: PUdp, bufs: ptr Buf, bufcnt: cint, adr: TSockAddrIn6, send_cb: UdpSendProc): cint{.
-    importc: "uv_udp_send6", header: "uv.h".}
-
-proc udp_recv_start*(handle: PUdp, alloc_cb: AllocProc, recv_cb: UdpRecvProc): cint{.
-    importc: "uv_udp_recv_start", header: "uv.h".}
-
-proc udp_recv_stop*(handle: PUdp): cint{.
-    importc: "uv_udp_recv_stop", header: "uv.h".}
-
-proc tty_init*(a2: PLoop, a3: pTTy, fd: File): cint{.
-    importc: "uv_tty_init", header: "uv.h".}
-
-proc tty_set_mode*(a2: pTTy, mode: cint): cint{.
-    importc: "uv_tty_set_mode", header: "uv.h".}
-
-proc tty_get_winsize*(a2: pTTy, width: var cint, height: var cint): cint{.
-    importc: "uv_tty_get_winsize", header: "uv.h".}
-
-proc tty_reset_mode*() {.
-    importc: "uv_tty_reset_mode", header: "uv.h".}
-
-proc guess_handle*(file: File): HandleType{.
-    importc: "uv_guess_handle", header: "uv.h".}
-
-proc pipe_init*(a2: PLoop, handle: PPipe, ipc: int): cint{.
-    importc: "uv_pipe_init", header: "uv.h".}
-
-proc pipe_open*(a2: PPipe, file: File){.
-    importc: "uv_pipe_open", header: "uv.h".}
-
-proc pipe_bind*(handle: PPipe, name: cstring): cint{.
-    importc: "uv_pipe_bind", header: "uv.h".}
-
-proc pipe_connect*(req: PConnect, handle: PPipe, name: cstring, cb: ConnectProc): cint{.
-    importc: "uv_pipe_connect", header: "uv.h".}
-
-proc prepare_init*(a2: PLoop, prepare: PPrepare): cint{.
-    importc: "uv_prepare_init", header: "uv.h".}
-
-proc prepare_start*(prepare: PPrepare, cb: PrepareProc): cint{.
-    importc: "uv_prepare_start", header: "uv.h".}
-
-proc prepare_stop*(prepare: PPrepare): cint{.
-    importc: "uv_prepare_stop", header: "uv.h".}
-
-proc check_init*(a2: PLoop, check: PCheck): cint{.
-    importc: "uv_check_init", header: "uv.h".}
-
-proc check_start*(check: PCheck, cb: CheckProc): cint{.
-    importc: "uv_check_start", header: "uv.h".}
-
-proc check_stop*(check: PCheck): cint{.
-    importc: "uv_check_stop", header: "uv.h".}
-
-proc idle_init*(a2: PLoop, idle: PIdle): cint{.
-    importc: "uv_idle_init", header: "uv.h".}
-
-proc idle_start*(idle: PIdle, cb: IdleProc): cint{.
-    importc: "uv_idle_start", header: "uv.h".}
-
-proc idle_stop*(idle: PIdle): cint{.
-    importc: "uv_idle_stop", header: "uv.h".}
-
-proc async_init*(a2: PLoop, async: PAsync, async_cb: AsyncProc): cint{.
-    importc: "uv_async_init", header: "uv.h".}
-
-proc async_send*(async: PAsync): cint{.
-    importc: "uv_async_send", header: "uv.h".}
-
-proc timer_init*(a2: PLoop, timer: PTimer): cint{.
-    importc: "uv_timer_init", header: "uv.h".}
-
-proc timer_start*(timer: PTimer, cb: TimerProc, timeout: int64, repeat: int64): cint{.
-    importc: "uv_timer_start", header: "uv.h".}
-
-proc timer_stop*(timer: PTimer): cint{.
-    importc: "uv_timer_stop", header: "uv.h".}
-
-proc timer_again*(timer: PTimer): cint{.
-    importc: "uv_timer_again", header: "uv.h".}
-
-proc timer_set_repeat*(timer: PTimer, repeat: int64){.
-    importc: "uv_timer_set_repeat", header: "uv.h".}
-
-proc timer_get_repeat*(timer: PTimer): int64{.
-    importc: "uv_timer_get_repeat", header: "uv.h".}
-
-proc ares_init_options*(a2: PLoop, channel: PAresChannel, options: PAresOptions, optmask: cint): cint{.
-    importc: "uv_ares_init_options", header: "uv.h".}
-
-proc ares_destroy*(a2: PLoop, channel: PAresChannel){.
-    importc: "uv_ares_destroy", header: "uv.h".}
-
-proc getaddrinfo*(a2: PLoop, handle: PGetAddrInfo,getaddrinfo_cb: GetAddrInfoProc, node: cstring, service: cstring, hints: ptr AddrInfo): cint{.
-    importc: "uv_getaddrinfo", header: "uv.h".}
-
-proc freeaddrinfo*(ai: ptr AddrInfo){.
-    importc: "uv_freeaddrinfo", header: "uv.h".}
-
-proc spawn*(a2: PLoop, a3: PProcess, options: ProcessOptions): cint{.
-    importc: "uv_spawn", header: "uv.h".}
-
-proc process_kill*(a2: PProcess, signum: cint): cint{.
-    importc: "uv_process_kill", header: "uv.h".}
-
-proc queue_work*(loop: PLoop, req: PWork, work_cb: WorkProc, after_work_cb: AfterWorkProc): cint{.
-    importc: "uv_queue_work", header: "uv.h".}
-
-proc req_cleanup*(req: PFS){.
-    importc: "uv_fs_req_cleanup", header: "uv.h".}
-
-proc close*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{.
-    importc: "uv_fs_close", header: "uv.h".}
-
-proc open*(loop: PLoop, req: PFS, path: cstring, flags: cint, mode: cint, cb: FsProc): cint{.
-    importc: "uv_fs_open", header: "uv.h".}
-
-proc read*(loop: PLoop, req: PFS, file: File, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{.
-    importc: "uv_fs_read", header: "uv.h".}
-
-proc unlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_unlink", header: "uv.h".}
-
-proc write*(loop: PLoop, req: PFS, file: File, buf: pointer, length: csize, offset: coff, cb: FsProc): cint{.
-    importc: "uv_fs_write", header: "uv.h".}
-
-proc mkdir*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{.
-    importc: "uv_fs_mkdir", header: "uv.h".}
-
-proc rmdir*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_rmdir", header: "uv.h".}
-
-proc readdir*(loop: PLoop, req: PFS, path: cstring, flags: cint, cb: FsProc): cint{.
-    importc: "uv_fs_readdir", header: "uv.h".}
-
-proc stat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_stat", header: "uv.h".}
-
-proc fstat*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{.
-    importc: "uv_fs_fstat", header: "uv.h".}
-
-proc rename*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_rename", header: "uv.h".}
-
-proc fsync*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{.
-    importc: "uv_fs_fsync", header: "uv.h".}
-
-proc fdatasync*(loop: PLoop, req: PFS, file: File, cb: FsProc): cint{.
-    importc: "uv_fs_fdatasync", header: "uv.h".}
-
-proc ftruncate*(loop: PLoop, req: PFS, file: File, offset: coff, cb: FsProc): cint{.
-    importc: "uv_fs_ftruncate", header: "uv.h".}
-
-proc sendfile*(loop: PLoop, req: PFS, out_fd: File, in_fd: File, in_offset: coff, length: csize, cb: FsProc): cint{.
-    importc: "uv_fs_sendfile", header: "uv.h".}
-
-proc chmod*(loop: PLoop, req: PFS, path: cstring, mode: cint, cb: FsProc): cint{.
-    importc: "uv_fs_chmod", header: "uv.h".}
-
-proc utime*(loop: PLoop, req: PFS, path: cstring, atime: cdouble, mtime: cdouble, cb: FsProc): cint{.
-    importc: "uv_fs_utime", header: "uv.h".}
-
-proc futime*(loop: PLoop, req: PFS, file: File, atime: cdouble, mtime: cdouble, cb: FsProc): cint{.
-    importc: "uv_fs_futime", header: "uv.h".}
-
-proc lstat*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_lstat", header: "uv.h".}
-
-proc link*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_link", header: "uv.h".}
-
-proc symlink*(loop: PLoop, req: PFS, path: cstring, new_path: cstring, flags: cint, cb: FsProc): cint{.
-    importc: "uv_fs_symlink", header: "uv.h".}
-
-proc readlink*(loop: PLoop, req: PFS, path: cstring, cb: FsProc): cint{.
-    importc: "uv_fs_readlink", header: "uv.h".}
-
-proc fchmod*(loop: PLoop, req: PFS, file: File, mode: cint, cb: FsProc): cint{.
-    importc: "uv_fs_fchmod", header: "uv.h".}
-
-proc chown*(loop: PLoop, req: PFS, path: cstring, uid: cint, gid: cint, cb: FsProc): cint{.
-    importc: "uv_fs_chown", header: "uv.h".}
-
-proc fchown*(loop: PLoop, req: PFS, file: File, uid: cint, gid: cint, cb: FsProc): cint{.
-    importc: "uv_fs_fchown", header: "uv.h".}
-
-proc event_init*(loop: PLoop, handle: PFSEvent, filename: cstring, cb: FsEventProc): cint{.
-    importc: "uv_fs_event_init", header: "uv.h".}
-
-proc ip4_addr*(ip: cstring, port: cint): SockAddrIn{.
-    importc: "uv_ip4_addr", header: "uv.h".}
-
-proc ip6_addr*(ip: cstring, port: cint): TSockAddrIn6{.
-    importc: "uv_ip6_addr", header: "uv.h".}
-
-proc ip4_name*(src: ptr SockAddrIn, dst: cstring, size: csize): cint{.
-    importc: "uv_ip4_name", header: "uv.h".}
-
-proc ip6_name*(src: ptr TSockAddrIn6, dst: cstring, size: csize): cint{.
-    importc: "uv_ip6_name", header: "uv.h".}
-
-proc exepath*(buffer: cstring, size: var csize): cint{.
-    importc: "uv_exepath", header: "uv.h".}
-
-proc hrtime*(): uint64{.
-    importc: "uv_hrtime", header: "uv.h".}
-
-proc loadavg*(load: var array[0..2, cdouble]) {.
-    importc: "uv_loadavg", header: "uv.h"}
-
-proc get_free_memory*(): cdouble {.
-    importc: "uv_get_free_memory", header: "uv.h".}
-
-proc get_total_memory*(): cdouble {.
-    importc: "uv_get_total_memory", header: "uv.h".}
-
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 5cbe2887c..55b0bc3f8 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -64,6 +64,8 @@ type
 
   des_key_schedule* = array[1..16, des_ks_struct]
 
+  pem_password_cb* = proc(buf: cstring, size, rwflag: cint, userdata: pointer): cint {.cdecl.}
+
 {.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].}
 
 const
@@ -432,6 +434,12 @@ proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_clear_error".}
 proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_free_strings".}
 proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".}
 
+proc PEM_read_bio_RSA_PUBKEY*(bp: BIO, x: ptr PRSA, pw: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLSSLName, importc.}
+
+proc RSA_verify*(kind: cint, origMsg: pointer, origMsgLen: cuint, signature: pointer,
+    signatureLen: cuint, rsa: PRSA): cint {.cdecl, dynlib: DLLSSLName, importc.}
+
 when true:
   discard
 else:
@@ -593,7 +601,7 @@ from strutils import toHex, toLowerAscii
 proc hexStr(buf: cstring): string =
   # turn md5s output into a nice hex str
   result = newStringOfCap(32)
-  for i in 0 .. <16:
+  for i in 0 ..< 16:
     result.add toHex(buf[i].ord, 2).toLowerAscii
 
 proc md5_File*(file: string): string {.raises: [IOError,Exception].} =
diff --git a/lib/wrappers/pdcurses.nim b/lib/wrappers/pdcurses.nim
deleted file mode 100644
index 2d64ac97a..000000000
--- a/lib/wrappers/pdcurses.nim
+++ /dev/null
@@ -1,1560 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-{.deadCodeElim: on.}
-
-discard """
-
-curses.h:
-#ifdef C2NIM
-#dynlib pdcursesdll
-#skipinclude
-#prefix PDC_
-#def FALSE
-#def TRUE
-#def NULL
-#def bool unsigned char
-#def chtype unsigned long
-#def cchar_t unsigned long
-#def attr_t unsigned long
-#def mmask_t unsigned long
-#def wchar_t char
-#def PDCEX
-#cdecl
-#endif
-
-pdcwin.h:
-#ifdef C2NIM
-#dynlib pdcursesdll
-#skipinclude
-#prefix pdc_
-#prefix PDC_
-#stdcall
-#endif
-"""
-
-when defined(windows):
-  import windows
-
-  when defined(nimOldDlls):
-    const pdcursesdll = "pdcurses.dll"
-  elif defined(cpu64):
-    const pdcursesdll = "pdcurses64.dll"
-  else:
-    const pdcursesdll = "pdcurses32.dll"
-
-  const
-    unixOS = false
-  {.pragma: extdecl, stdcall.}
-
-when not defined(windows):
-  const
-    unixOS = true
-  {.pragma: extdecl, cdecl.}
-
-type
-  cunsignedchar = char
-  cunsignedlong = uint32
-
-const
-  BUILD* = 3401
-  PDCURSES* = 1               # PDCurses-only routines
-  XOPEN* = 1                  # X/Open Curses routines
-  SYSVcurses* = 1             # System V Curses routines
-  BSDcurses* = 1              # BSD Curses routines
-  CHTYPE_LONG* = 1            # size of chtype; long
-  ERR* = (- 1)
-  OK* = 0
-  BUTTON_RELEASED* = 0x00000000
-  BUTTON_PRESSED* = 0x00000001
-  BUTTON_CLICKED* = 0x00000002
-  BUTTON_DOUBLE_CLICKED* = 0x00000003
-  BUTTON_TRIPLE_CLICKED* = 0x00000004
-  BUTTON_MOVED* = 0x00000005  # PDCurses
-  WHEEL_SCROLLED* = 0x00000006 # PDCurses
-  BUTTON_ACTION_MASK* = 0x00000007 # PDCurses
-  BUTTON_MODIFIER_MASK* = 0x00000038 # PDCurses
-  MOUSE_MOVED* = 0x00000008
-  MOUSE_POSITION* = 0x00000010
-  MOUSE_WHEEL_UP* = 0x00000020
-  MOUSE_WHEEL_DOWN* = 0x00000040
-  BUTTON1_RELEASED* = 0x00000001
-  BUTTON1_PRESSED* = 0x00000002
-  BUTTON1_CLICKED* = 0x00000004
-  BUTTON1_DOUBLE_CLICKED* = 0x00000008
-  BUTTON1_TRIPLE_CLICKED* = 0x00000010
-  BUTTON1_MOVED* = 0x00000010 # PDCurses
-  BUTTON2_RELEASED* = 0x00000020
-  BUTTON2_PRESSED* = 0x00000040
-  BUTTON2_CLICKED* = 0x00000080
-  BUTTON2_DOUBLE_CLICKED* = 0x00000100
-  BUTTON2_TRIPLE_CLICKED* = 0x00000200
-  BUTTON2_MOVED* = 0x00000200 # PDCurses
-  BUTTON3_RELEASED* = 0x00000400
-  BUTTON3_PRESSED* = 0x00000800
-  BUTTON3_CLICKED* = 0x00001000
-  BUTTON3_DOUBLE_CLICKED* = 0x00002000
-  BUTTON3_TRIPLE_CLICKED* = 0x00004000
-  BUTTON3_MOVED* = 0x00004000 # PDCurses
-  BUTTON4_RELEASED* = 0x00008000
-  BUTTON4_PRESSED* = 0x00010000
-  BUTTON4_CLICKED* = 0x00020000
-  BUTTON4_DOUBLE_CLICKED* = 0x00040000
-  BUTTON4_TRIPLE_CLICKED* = 0x00080000
-  BUTTON5_RELEASED* = 0x00100000
-  BUTTON5_PRESSED* = 0x00200000
-  BUTTON5_CLICKED* = 0x00400000
-  BUTTON5_DOUBLE_CLICKED* = 0x00800000
-  BUTTON5_TRIPLE_CLICKED* = 0x01000000
-  MOUSE_WHEEL_SCROLL* = 0x02000000 # PDCurses
-  BUTTON_MODIFIER_SHIFT* = 0x04000000 # PDCurses
-  BUTTON_MODIFIER_CONTROL* = 0x08000000 # PDCurses
-  BUTTON_MODIFIER_ALT* = 0x10000000 # PDCurses
-  ALL_MOUSE_EVENTS* = 0x1FFFFFFF
-  REPORT_MOUSE_POSITION* = 0x20000000
-  A_NORMAL* = 0
-  A_ALTCHARSET* = 0x00010000
-  A_RIGHTLINE* = 0x00020000
-  A_LEFTLINE* = 0x00040000
-  A_INVIS* = 0x00080000
-  A_UNDERLINE* = 0x00100000
-  A_REVERSE* = 0x00200000
-  A_BLINK* = 0x00400000
-  A_BOLD* = 0x00800000
-  A_ATTRIBUTES* = 0xFFFF0000
-  A_CHARTEXT* = 0x0000FFFF
-  A_COLOR* = 0xFF000000
-  A_ITALIC* = A_INVIS
-  A_PROTECT* = (A_UNDERLINE or A_LEFTLINE or A_RIGHTLINE)
-  ATTR_SHIFT* = 19
-  COLOR_SHIFT* = 24
-  A_STANDOUT* = (A_REVERSE or A_BOLD) # X/Open
-  A_DIM* = A_NORMAL
-  CHR_MSK* = A_CHARTEXT       # Obsolete
-  ATR_MSK* = A_ATTRIBUTES     # Obsolete
-  ATR_NRM* = A_NORMAL         # Obsolete
-  WA_ALTCHARSET* = A_ALTCHARSET
-  WA_BLINK* = A_BLINK
-  WA_BOLD* = A_BOLD
-  WA_DIM* = A_DIM
-  WA_INVIS* = A_INVIS
-  WA_LEFT* = A_LEFTLINE
-  WA_PROTECT* = A_PROTECT
-  WA_REVERSE* = A_REVERSE
-  WA_RIGHT* = A_RIGHTLINE
-  WA_STANDOUT* = A_STANDOUT
-  WA_UNDERLINE* = A_UNDERLINE
-  WA_HORIZONTAL* = A_NORMAL
-  WA_LOW* = A_NORMAL
-  WA_TOP* = A_NORMAL
-  WA_VERTICAL* = A_NORMAL
-  COLOR_BLACK* = 0
-  COLOR_RED* = 1
-  COLOR_GREEN* = 2
-  COLOR_BLUE* = 4
-  COLOR_CYAN* = (COLOR_BLUE or COLOR_GREEN)
-  COLOR_MAGENTA* = (COLOR_RED or COLOR_BLUE)
-  COLOR_YELLOW* = (COLOR_RED or COLOR_GREEN)
-  COLOR_WHITE* = 7
-  KEY_CODE_YES* = 0x00000100  # If get_wch() gives a key code
-  KEY_BREAK* = 0x00000101     # Not on PC KBD
-  KEY_DOWN* = 0x00000102      # Down arrow key
-  KEY_UP* = 0x00000103        # Up arrow key
-  KEY_LEFT* = 0x00000104      # Left arrow key
-  KEY_RIGHT* = 0x00000105     # Right arrow key
-  KEY_HOME* = 0x00000106      # home key
-  KEY_BACKSPACE* = 0x00000107 # not on pc
-  KEY_F0* = 0x00000108        # function keys; 64 reserved
-  KEY_DL* = 0x00000148        # delete line
-  KEY_IL* = 0x00000149        # insert line
-  KEY_DC* = 0x0000014A        # delete character
-  KEY_IC* = 0x0000014B        # insert char or enter ins mode
-  KEY_EIC* = 0x0000014C       # exit insert char mode
-  KEY_CLEAR* = 0x0000014D     # clear screen
-  KEY_EOS* = 0x0000014E       # clear to end of screen
-  KEY_EOL* = 0x0000014F       # clear to end of line
-  KEY_SF* = 0x00000150        # scroll 1 line forward
-  KEY_SR* = 0x00000151        # scroll 1 line back (reverse)
-  KEY_NPAGE* = 0x00000152     # next page
-  KEY_PPAGE* = 0x00000153     # previous page
-  KEY_STAB* = 0x00000154      # set tab
-  KEY_CTAB* = 0x00000155      # clear tab
-  KEY_CATAB* = 0x00000156     # clear all tabs
-  KEY_ENTER* = 0x00000157     # enter or send (unreliable)
-  KEY_SRESET* = 0x00000158    # soft/reset (partial/unreliable)
-  KEY_RESET* = 0x00000159     # reset/hard reset (unreliable)
-  KEY_PRINT* = 0x0000015A     # print/copy
-  KEY_LL* = 0x0000015B        # home down/bottom (lower left)
-  KEY_ABORT* = 0x0000015C     # abort/terminate key (any)
-  KEY_SHELP* = 0x0000015D     # short help
-  KEY_LHELP* = 0x0000015E     # long help
-  KEY_BTAB* = 0x0000015F      # Back tab key
-  KEY_BEG* = 0x00000160       # beg(inning) key
-  KEY_CANCEL* = 0x00000161    # cancel key
-  KEY_CLOSE* = 0x00000162     # close key
-  KEY_COMMAND* = 0x00000163   # cmd (command) key
-  KEY_COPY* = 0x00000164      # copy key
-  KEY_CREATE* = 0x00000165    # create key
-  KEY_END* = 0x00000166       # end key
-  KEY_EXIT* = 0x00000167      # exit key
-  KEY_FIND* = 0x00000168      # find key
-  KEY_HELP* = 0x00000169      # help key
-  KEY_MARK* = 0x0000016A      # mark key
-  KEY_MESSAGE* = 0x0000016B   # message key
-  KEY_MOVE* = 0x0000016C      # move key
-  KEY_NEXT* = 0x0000016D      # next object key
-  KEY_OPEN* = 0x0000016E      # open key
-  KEY_OPTIONS* = 0x0000016F   # options key
-  KEY_PREVIOUS* = 0x00000170  # previous object key
-  KEY_REDO* = 0x00000171      # redo key
-  KEY_REFERENCE* = 0x00000172 # ref(erence) key
-  KEY_REFRESH* = 0x00000173   # refresh key
-  KEY_REPLACE* = 0x00000174   # replace key
-  KEY_RESTART* = 0x00000175   # restart key
-  KEY_RESUME* = 0x00000176    # resume key
-  KEY_SAVE* = 0x00000177      # save key
-  KEY_SBEG* = 0x00000178      # shifted beginning key
-  KEY_SCANCEL* = 0x00000179   # shifted cancel key
-  KEY_SCOMMAND* = 0x0000017A  # shifted command key
-  KEY_SCOPY* = 0x0000017B     # shifted copy key
-  KEY_SCREATE* = 0x0000017C   # shifted create key
-  KEY_SDC* = 0x0000017D       # shifted delete char key
-  KEY_SDL* = 0x0000017E       # shifted delete line key
-  KEY_SELECT* = 0x0000017F    # select key
-  KEY_SEND* = 0x00000180      # shifted end key
-  KEY_SEOL* = 0x00000181      # shifted clear line key
-  KEY_SEXIT* = 0x00000182     # shifted exit key
-  KEY_SFIND* = 0x00000183     # shifted find key
-  KEY_SHOME* = 0x00000184     # shifted home key
-  KEY_SIC* = 0x00000185       # shifted input key
-  KEY_SLEFT* = 0x00000187     # shifted left arrow key
-  KEY_SMESSAGE* = 0x00000188  # shifted message key
-  KEY_SMOVE* = 0x00000189     # shifted move key
-  KEY_SNEXT* = 0x0000018A     # shifted next key
-  KEY_SOPTIONS* = 0x0000018B  # shifted options key
-  KEY_SPREVIOUS* = 0x0000018C # shifted prev key
-  KEY_SPRINT* = 0x0000018D    # shifted print key
-  KEY_SREDO* = 0x0000018E     # shifted redo key
-  KEY_SREPLACE* = 0x0000018F  # shifted replace key
-  KEY_SRIGHT* = 0x00000190    # shifted right arrow
-  KEY_SRSUME* = 0x00000191    # shifted resume key
-  KEY_SSAVE* = 0x00000192     # shifted save key
-  KEY_SSUSPEND* = 0x00000193  # shifted suspend key
-  KEY_SUNDO* = 0x00000194     # shifted undo key
-  KEY_SUSPEND* = 0x00000195   # suspend key
-  KEY_UNDO* = 0x00000196      # undo key
-  ALT_0* = 0x00000197
-  ALT_1* = 0x00000198
-  ALT_2* = 0x00000199
-  ALT_3* = 0x0000019A
-  ALT_4* = 0x0000019B
-  ALT_5* = 0x0000019C
-  ALT_6* = 0x0000019D
-  ALT_7* = 0x0000019E
-  ALT_8* = 0x0000019F
-  ALT_9* = 0x000001A0
-  ALT_A* = 0x000001A1
-  ALT_B* = 0x000001A2
-  ALT_C* = 0x000001A3
-  ALT_D* = 0x000001A4
-  ALT_E* = 0x000001A5
-  ALT_F* = 0x000001A6
-  ALT_G* = 0x000001A7
-  ALT_H* = 0x000001A8
-  ALT_I* = 0x000001A9
-  ALT_J* = 0x000001AA
-  ALT_K* = 0x000001AB
-  ALT_L* = 0x000001AC
-  ALT_M* = 0x000001AD
-  ALT_N* = 0x000001AE
-  ALT_O* = 0x000001AF
-  ALT_P* = 0x000001B0
-  ALT_Q* = 0x000001B1
-  ALT_R* = 0x000001B2
-  ALT_S* = 0x000001B3
-  ALT_T* = 0x000001B4
-  ALT_U* = 0x000001B5
-  ALT_V* = 0x000001B6
-  ALT_W* = 0x000001B7
-  ALT_X* = 0x000001B8
-  ALT_Y* = 0x000001B9
-  ALT_Z* = 0x000001BA
-  CTL_LEFT* = 0x000001BB      # Control-Left-Arrow
-  CTL_RIGHT* = 0x000001BC
-  CTL_PGUP* = 0x000001BD
-  CTL_PGDN* = 0x000001BE
-  CTL_HOME* = 0x000001BF
-  CTL_END* = 0x000001C0
-  KEY_A1* = 0x000001C1        # upper left on Virtual keypad
-  KEY_A2* = 0x000001C2        # upper middle on Virt. keypad
-  KEY_A3* = 0x000001C3        # upper right on Vir. keypad
-  KEY_B1* = 0x000001C4        # middle left on Virt. keypad
-  KEY_B2* = 0x000001C5        # center on Virt. keypad
-  KEY_B3* = 0x000001C6        # middle right on Vir. keypad
-  KEY_C1* = 0x000001C7        # lower left on Virt. keypad
-  KEY_C2* = 0x000001C8        # lower middle on Virt. keypad
-  KEY_C3* = 0x000001C9        # lower right on Vir. keypad
-  PADSLASH* = 0x000001CA      # slash on keypad
-  PADENTER* = 0x000001CB      # enter on keypad
-  CTL_PADENTER* = 0x000001CC  # ctl-enter on keypad
-  ALT_PADENTER* = 0x000001CD  # alt-enter on keypad
-  PADSTOP* = 0x000001CE       # stop on keypad
-  PADSTAR* = 0x000001CF       # star on keypad
-  PADMINUS* = 0x000001D0      # minus on keypad
-  PADPLUS* = 0x000001D1       # plus on keypad
-  CTL_PADSTOP* = 0x000001D2   # ctl-stop on keypad
-  CTL_PADCENTER* = 0x000001D3 # ctl-enter on keypad
-  CTL_PADPLUS* = 0x000001D4   # ctl-plus on keypad
-  CTL_PADMINUS* = 0x000001D5  # ctl-minus on keypad
-  CTL_PADSLASH* = 0x000001D6  # ctl-slash on keypad
-  CTL_PADSTAR* = 0x000001D7   # ctl-star on keypad
-  ALT_PADPLUS* = 0x000001D8   # alt-plus on keypad
-  ALT_PADMINUS* = 0x000001D9  # alt-minus on keypad
-  ALT_PADSLASH* = 0x000001DA  # alt-slash on keypad
-  ALT_PADSTAR* = 0x000001DB   # alt-star on keypad
-  ALT_PADSTOP* = 0x000001DC   # alt-stop on keypad
-  CTL_INS* = 0x000001DD       # ctl-insert
-  ALT_DEL* = 0x000001DE       # alt-delete
-  ALT_INS* = 0x000001DF       # alt-insert
-  CTL_UP* = 0x000001E0        # ctl-up arrow
-  CTL_DOWN* = 0x000001E1      # ctl-down arrow
-  CTL_TAB* = 0x000001E2       # ctl-tab
-  ALT_TAB* = 0x000001E3
-  ALT_MINUS* = 0x000001E4
-  ALT_EQUAL* = 0x000001E5
-  ALT_HOME* = 0x000001E6
-  ALT_PGUP* = 0x000001E7
-  ALT_PGDN* = 0x000001E8
-  ALT_END* = 0x000001E9
-  ALT_UP* = 0x000001EA        # alt-up arrow
-  ALT_DOWN* = 0x000001EB      # alt-down arrow
-  ALT_RIGHT* = 0x000001EC     # alt-right arrow
-  ALT_LEFT* = 0x000001ED      # alt-left arrow
-  ALT_ENTER* = 0x000001EE     # alt-enter
-  ALT_ESC* = 0x000001EF       # alt-escape
-  ALT_BQUOTE* = 0x000001F0    # alt-back quote
-  ALT_LBRACKET* = 0x000001F1  # alt-left bracket
-  ALT_RBRACKET* = 0x000001F2  # alt-right bracket
-  ALT_SEMICOLON* = 0x000001F3 # alt-semi-colon
-  ALT_FQUOTE* = 0x000001F4    # alt-forward quote
-  ALT_COMMA* = 0x000001F5     # alt-comma
-  ALT_STOP* = 0x000001F6      # alt-stop
-  ALT_FSLASH* = 0x000001F7    # alt-forward slash
-  ALT_BKSP* = 0x000001F8      # alt-backspace
-  CTL_BKSP* = 0x000001F9      # ctl-backspace
-  PAD0* = 0x000001FA          # keypad 0
-  CTL_PAD0* = 0x000001FB      # ctl-keypad 0
-  CTL_PAD1* = 0x000001FC
-  CTL_PAD2* = 0x000001FD
-  CTL_PAD3* = 0x000001FE
-  CTL_PAD4* = 0x000001FF
-  CTL_PAD5* = 0x00000200
-  CTL_PAD6* = 0x00000201
-  CTL_PAD7* = 0x00000202
-  CTL_PAD8* = 0x00000203
-  CTL_PAD9* = 0x00000204
-  ALT_PAD0* = 0x00000205      # alt-keypad 0
-  ALT_PAD1* = 0x00000206
-  ALT_PAD2* = 0x00000207
-  ALT_PAD3* = 0x00000208
-  ALT_PAD4* = 0x00000209
-  ALT_PAD5* = 0x0000020A
-  ALT_PAD6* = 0x0000020B
-  ALT_PAD7* = 0x0000020C
-  ALT_PAD8* = 0x0000020D
-  ALT_PAD9* = 0x0000020E
-  CTL_DEL* = 0x0000020F       # clt-delete
-  ALT_BSLASH* = 0x00000210    # alt-back slash
-  CTL_ENTER* = 0x00000211     # ctl-enter
-  SHF_PADENTER* = 0x00000212  # shift-enter on keypad
-  SHF_PADSLASH* = 0x00000213  # shift-slash on keypad
-  SHF_PADSTAR* = 0x00000214   # shift-star  on keypad
-  SHF_PADPLUS* = 0x00000215   # shift-plus  on keypad
-  SHF_PADMINUS* = 0x00000216  # shift-minus on keypad
-  SHF_UP* = 0x00000217        # shift-up on keypad
-  SHF_DOWN* = 0x00000218      # shift-down on keypad
-  SHF_IC* = 0x00000219        # shift-insert on keypad
-  SHF_DC* = 0x0000021A        # shift-delete on keypad
-  KEY_MOUSE* = 0x0000021B     # "mouse" key
-  KEY_SHIFT_L* = 0x0000021C   # Left-shift
-  KEY_SHIFT_R* = 0x0000021D   # Right-shift
-  KEY_CONTROL_L* = 0x0000021E # Left-control
-  KEY_CONTROL_R* = 0x0000021F # Right-control
-  KEY_ALT_L* = 0x00000220     # Left-alt
-  KEY_ALT_R* = 0x00000221     # Right-alt
-  KEY_RESIZE* = 0x00000222    # Window resize
-  KEY_SUP* = 0x00000223       # Shifted up arrow
-  KEY_SDOWN* = 0x00000224     # Shifted down arrow
-  KEY_MIN* = KEY_BREAK        # Minimum curses key value
-  KEY_MAX* = KEY_SDOWN        # Maximum curses key
-  CLIP_SUCCESS* = 0
-  CLIP_ACCESS_ERROR* = 1
-  CLIP_EMPTY* = 2
-  CLIP_MEMORY_ERROR* = 3
-  KEY_MODIFIER_SHIFT* = 1
-  KEY_MODIFIER_CONTROL* = 2
-  KEY_MODIFIER_ALT* = 4
-  KEY_MODIFIER_NUMLOCK* = 8
-
-when appType == "gui":
-  const
-    BUTTON_SHIFT* = BUTTON_MODIFIER_SHIFT
-    BUTTON_CONTROL* = BUTTON_MODIFIER_CONTROL
-    BUTTON_CTRL* = BUTTON_MODIFIER_CONTROL
-    BUTTON_ALT* = BUTTON_MODIFIER_ALT
-else:
-  const
-    BUTTON_SHIFT* = 0x00000008
-    BUTTON_CONTROL* = 0x00000010
-    BUTTON_ALT* = 0x00000020
-
-type
-  TMOUSE_STATUS*{.pure, final.} = object
-    x*: cint                  # absolute column, 0 based, measured in characters
-    y*: cint                  # absolute row, 0 based, measured in characters
-    button*: array[0..3 - 1, cshort] # state of each button
-    changes*: cint            # flags indicating what has changed with the mouse
-
-  MEVENT*{.pure, final.} = object
-    id*: cshort               # unused, always 0
-    x*: cint
-    y*: cint
-    z*: cint                  # x, y same as TMOUSE_STATUS; z unused
-    bstate*: cunsignedlong    # equivalent to changes + button[], but
-                              # in the same format as used for mousemask()
-
-  WINDOW*{.pure, final.} = object
-    cury*: cint              # current pseudo-cursor
-    curx*: cint
-    maxy*: cint              # max window coordinates
-    maxx*: cint
-    begy*: cint              # origin on screen
-    begx*: cint
-    flags*: cint             # window properties
-    attrs*: cunsignedlong    # standard attributes and colors
-    bkgd*: cunsignedlong     # background, normally blank
-    clear*: cunsignedchar    # causes clear at next refresh
-    leaveit*: cunsignedchar  # leaves cursor where it is
-    scroll*: cunsignedchar   # allows window scrolling
-    nodelay*: cunsignedchar  # input character wait flag
-    immed*: cunsignedchar    # immediate update flag
-    sync*: cunsignedchar     # synchronise window ancestors
-    use_keypad*: cunsignedchar # flags keypad key mode active
-    y*: ptr ptr cunsignedlong # pointer to line pointer array
-    firstch*: ptr cint       # first changed character in line
-    lastch*: ptr cint        # last changed character in line
-    tmarg*: cint             # top of scrolling region
-    bmarg*: cint             # bottom of scrolling region
-    delayms*: cint           # milliseconds of delay for getch()
-    parx*: cint
-    pary*: cint              # coords relative to parent (0,0)
-    parent*: ptr WINDOW        # subwin's pointer to parent win
-
-  PANELOBS*{.pure, final.} = object
-    above*: ptr PANELOBS
-    pan*: ptr PANEL
-
-  PANEL*{.pure, final.} = object
-    win*: ptr WINDOW
-    wstarty*: cint
-    wendy*: cint
-    wstartx*: cint
-    wendx*: cint
-    below*: ptr PANEL
-    above*: ptr PANEL
-    user*: pointer
-    obscure*: ptr PANELOBS
-{.deprecated: [
-              #TMOUSE_STATUS: MOUSE_STATUS, # Name conflict when we drop the `T`
-              TMEVENT: MEVENT, TWINDOW: WINDOW,
-              TPANELOBS: PANELOBS, TPANEL:PANEL].}
-
-when unixOS:
-  type
-    SCREEN*{.pure, final.} = object
-      alive*: cunsignedchar     # if initscr() called, and not endwin()
-      autocr*: cunsignedchar    # if cr -> lf
-      cbreak*: cunsignedchar    # if terminal unbuffered
-      echo*: cunsignedchar      # if terminal echo
-      raw_inp*: cunsignedchar   # raw input mode (v. cooked input)
-      raw_out*: cunsignedchar   # raw output mode (7 v. 8 bits)
-      audible*: cunsignedchar   # FALSE if the bell is visual
-      mono*: cunsignedchar      # TRUE if current screen is mono
-      resized*: cunsignedchar   # TRUE if TERM has been resized
-      orig_attr*: cunsignedchar # TRUE if we have the original colors
-      orig_fore*: cshort        # original screen foreground color
-      orig_back*: cshort        # original screen foreground color
-      cursrow*: cint            # position of physical cursor
-      curscol*: cint            # position of physical cursor
-      visibility*: cint         # visibility of cursor
-      orig_cursor*: cint        # original cursor size
-      lines*: cint              # new value for LINES
-      cols*: cint               # new value for COLS
-      trap_mbe*: cunsignedlong # trap these mouse button events
-      map_mbe_to_key*: cunsignedlong # map mouse buttons to slk
-      mouse_wait*: cint # time to wait (in ms) for a button release after a press
-      slklines*: cint           # lines in use by slk_init()
-      slk_winptr*: ptr WINDOW   # window for slk
-      linesrippedoff*: cint     # lines ripped off via ripoffline()
-      linesrippedoffontop*: cint # lines ripped off on top via ripoffline()
-      delaytenths*: cint        # 1/10ths second to wait block getch() for
-      preserve*: cunsignedchar # TRUE if screen background to be preserved
-      restore*: cint           # specifies if screen background to be restored, and how
-      save_key_modifiers*: cunsignedchar # TRUE if each key modifiers saved with each key press
-      return_key_modifiers*: cunsignedchar # TRUE if modifier keys are returned as "real" keys
-      key_code*: cunsignedchar # TRUE if last key is a special key;
-      XcurscrSize*: cint        # size of Xcurscr shared memory block
-      sb_on*: cunsignedchar
-      sb_viewport_y*: cint
-      sb_viewport_x*: cint
-      sb_total_y*: cint
-      sb_total_x*: cint
-      sb_cur_y*: cint
-      sb_cur_x*: cint
-      line_color*: cshort       # color of line attributes - default -1
-  {.deprecated: [TSCREEN: SCREEN].}
-else:
-  type
-    SCREEN*{.pure, final.} = object
-      alive*: cunsignedchar     # if initscr() called, and not endwin()
-      autocr*: cunsignedchar    # if cr -> lf
-      cbreak*: cunsignedchar    # if terminal unbuffered
-      echo*: cunsignedchar      # if terminal echo
-      raw_inp*: cunsignedchar   # raw input mode (v. cooked input)
-      raw_out*: cunsignedchar   # raw output mode (7 v. 8 bits)
-      audible*: cunsignedchar   # FALSE if the bell is visual
-      mono*: cunsignedchar      # TRUE if current screen is mono
-      resized*: cunsignedchar   # TRUE if TERM has been resized
-      orig_attr*: cunsignedchar # TRUE if we have the original colors
-      orig_fore*: cshort        # original screen foreground color
-      orig_back*: cshort        # original screen foreground color
-      cursrow*: cint            # position of physical cursor
-      curscol*: cint            # position of physical cursor
-      visibility*: cint         # visibility of cursor
-      orig_cursor*: cint        # original cursor size
-      lines*: cint              # new value for LINES
-      cols*: cint               # new value for COLS
-      trap_mbe*: cunsignedlong # trap these mouse button events
-      map_mbe_to_key*: cunsignedlong # map mouse buttons to slk
-      mouse_wait*: cint # time to wait (in ms) for a button release after a press
-      slklines*: cint           # lines in use by slk_init()
-      slk_winptr*: ptr WINDOW   # window for slk
-      linesrippedoff*: cint     # lines ripped off via ripoffline()
-      linesrippedoffontop*: cint # lines ripped off on top via ripoffline()
-      delaytenths*: cint        # 1/10ths second to wait block getch() for
-      preserve*: cunsignedchar # TRUE if screen background to be preserved
-      restore*: cint           # specifies if screen background to be restored, and how
-      save_key_modifiers*: cunsignedchar # TRUE if each key modifiers saved with each key press
-      return_key_modifiers*: cunsignedchar # TRUE if modifier keys are returned as "real" keys
-      key_code*: cunsignedchar # TRUE if last key is a special key;
-      line_color*: cshort       # color of line attributes - default -1
-  {.deprecated: [TSCREEN: SCREEN].}
-
-var
-  LINES*{.importc: "LINES", dynlib: pdcursesdll.}: cint
-  COLS*{.importc: "COLS", dynlib: pdcursesdll.}: cint
-  stdscr*{.importc: "stdscr", dynlib: pdcursesdll.}: ptr WINDOW
-  curscr*{.importc: "curscr", dynlib: pdcursesdll.}: ptr WINDOW
-  SP*{.importc: "SP", dynlib: pdcursesdll.}: ptr SCREEN
-  Mouse_status*{.importc: "Mouse_status", dynlib: pdcursesdll.}: MOUSE_STATUS
-  COLORS*{.importc: "COLORS", dynlib: pdcursesdll.}: cint
-  COLOR_PAIRS*{.importc: "COLOR_PAIRS", dynlib: pdcursesdll.}: cint
-  TABSIZE*{.importc: "TABSIZE", dynlib: pdcursesdll.}: cint
-  acs_map*{.importc: "acs_map", dynlib: pdcursesdll.}: ptr cunsignedlong
-  ttytype*{.importc: "ttytype", dynlib: pdcursesdll.}: cstring
-
-template BUTTON_CHANGED*(x: expr): expr =
-  (Mouse_status.changes and (1 shl ((x) - 1)))
-
-template BUTTON_STATUS*(x: expr): expr =
-  (Mouse_status.button[(x) - 1])
-
-template ACS_PICK*(w, n: expr): expr = int32(w) or A_ALTCHARSET
-
-template KEY_F*(n: expr): expr = KEY_F0 + n
-
-template COLOR_PAIR*(n: expr): expr =
-  ((cunsignedlong(n) shl COLOR_SHIFT) and A_COLOR)
-
-template PAIR_NUMBER*(n: expr): expr =
-  (((n) and A_COLOR) shr COLOR_SHIFT)
-
-const
-  #MOUSE_X_POS* = (Mouse_status.x)
-  #MOUSE_Y_POS* = (Mouse_status.y)
-  #A_BUTTON_CHANGED* = (Mouse_status.changes and 7)
-  #MOUSE_MOVED* = (Mouse_status.changes and MOUSE_MOVED)
-  #MOUSE_POS_REPORT* = (Mouse_status.changes and MOUSE_POSITION)
-  #MOUSE_WHEEL_UP* = (Mouse_status.changes and MOUSE_WHEEL_UP)
-  #MOUSE_WHEEL_DOWN* = (Mouse_status.changes and MOUSE_WHEEL_DOWN)
-  ACS_ULCORNER* = ACS_PICK('l', '+')
-  ACS_LLCORNER* = ACS_PICK('m', '+')
-  ACS_URCORNER* = ACS_PICK('k', '+')
-  ACS_LRCORNER* = ACS_PICK('j', '+')
-  ACS_RTEE* = ACS_PICK('u', '+')
-  ACS_LTEE* = ACS_PICK('t', '+')
-  ACS_BTEE* = ACS_PICK('v', '+')
-  ACS_TTEE* = ACS_PICK('w', '+')
-  ACS_HLINE* = ACS_PICK('q', '-')
-  ACS_VLINE* = ACS_PICK('x', '|')
-  ACS_PLUS* = ACS_PICK('n', '+')
-  ACS_S1* = ACS_PICK('o', '-')
-  ACS_S9* = ACS_PICK('s', '_')
-  ACS_DIAMOND* = ACS_PICK('`', '+')
-  ACS_CKBOARD* = ACS_PICK('a', ':')
-  ACS_DEGREE* = ACS_PICK('f', '\'')
-  ACS_PLMINUS* = ACS_PICK('g', '#')
-  ACS_BULLET* = ACS_PICK('~', 'o')
-  ACS_LARROW* = ACS_PICK(',', '<')
-  ACS_RARROW* = ACS_PICK('+', '>')
-  ACS_DARROW* = ACS_PICK('.', 'v')
-  ACS_UARROW* = ACS_PICK('-', '^')
-  ACS_BOARD* = ACS_PICK('h', '#')
-  ACS_LANTERN* = ACS_PICK('i', '*')
-  ACS_BLOCK* = ACS_PICK('0', '#')
-  ACS_S3* = ACS_PICK('p', '-')
-  ACS_S7* = ACS_PICK('r', '-')
-  ACS_LEQUAL* = ACS_PICK('y', '<')
-  ACS_GEQUAL* = ACS_PICK('z', '>')
-  ACS_PI* = ACS_PICK('{', 'n')
-  ACS_NEQUAL* = ACS_PICK('|', '+')
-  ACS_STERLING* = ACS_PICK('}', 'L')
-  ACS_BSSB* = ACS_ULCORNER
-  ACS_SSBB* = ACS_LLCORNER
-  ACS_BBSS* = ACS_URCORNER
-  ACS_SBBS* = ACS_LRCORNER
-  ACS_SBSS* = ACS_RTEE
-  ACS_SSSB* = ACS_LTEE
-  ACS_SSBS* = ACS_BTEE
-  ACS_BSSS* = ACS_TTEE
-  ACS_BSBS* = ACS_HLINE
-  ACS_SBSB* = ACS_VLINE
-  ACS_SSSS* = ACS_PLUS
-discard """WACS_ULCORNER* = (addr((acs_map['l'])))
-  WACS_LLCORNER* = (addr((acs_map['m'])))
-  WACS_URCORNER* = (addr((acs_map['k'])))
-  WACS_LRCORNER* = (addr((acs_map['j'])))
-  WACS_RTEE* = (addr((acs_map['u'])))
-  WACS_LTEE* = (addr((acs_map['t'])))
-  WACS_BTEE* = (addr((acs_map['v'])))
-  WACS_TTEE* = (addr((acs_map['w'])))
-  WACS_HLINE* = (addr((acs_map['q'])))
-  WACS_VLINE* = (addr((acs_map['x'])))
-  WACS_PLUS* = (addr((acs_map['n'])))
-  WACS_S1* = (addr((acs_map['o'])))
-  WACS_S9* = (addr((acs_map['s'])))
-  WACS_DIAMOND* = (addr((acs_map['`'])))
-  WACS_CKBOARD* = (addr((acs_map['a'])))
-  WACS_DEGREE* = (addr((acs_map['f'])))
-  WACS_PLMINUS* = (addr((acs_map['g'])))
-  WACS_BULLET* = (addr((acs_map['~'])))
-  WACS_LARROW* = (addr((acs_map[','])))
-  WACS_RARROW* = (addr((acs_map['+'])))
-  WACS_DARROW* = (addr((acs_map['.'])))
-  WACS_UARROW* = (addr((acs_map['-'])))
-  WACS_BOARD* = (addr((acs_map['h'])))
-  WACS_LANTERN* = (addr((acs_map['i'])))
-  WACS_BLOCK* = (addr((acs_map['0'])))
-  WACS_S3* = (addr((acs_map['p'])))
-  WACS_S7* = (addr((acs_map['r'])))
-  WACS_LEQUAL* = (addr((acs_map['y'])))
-  WACS_GEQUAL* = (addr((acs_map['z'])))
-  WACS_PI* = (addr((acs_map['{'])))
-  WACS_NEQUAL* = (addr((acs_map['|'])))
-  WACS_STERLING* = (addr((acs_map['}'])))
-  WACS_BSSB* = WACS_ULCORNER
-  WACS_SSBB* = WACS_LLCORNER
-  WACS_BBSS* = WACS_URCORNER
-  WACS_SBBS* = WACS_LRCORNER
-  WACS_SBSS* = WACS_RTEE
-  WACS_SSSB* = WACS_LTEE
-  WACS_SSBS* = WACS_BTEE
-  WACS_BSSS* = WACS_TTEE
-  WACS_BSBS* = WACS_HLINE
-  WACS_SBSB* = WACS_VLINE
-  WACS_SSSS* = WACS_PLUS"""
-
-proc addch*(a2: cunsignedlong): cint{.extdecl, importc: "addch",
-                                      dynlib: pdcursesdll.}
-proc addchnstr*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "addchnstr", dynlib: pdcursesdll.}
-proc addchstr*(a2: ptr cunsignedlong): cint{.extdecl, importc: "addchstr",
-    dynlib: pdcursesdll.}
-proc addnstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "addnstr",
-    dynlib: pdcursesdll.}
-proc addstr*(a2: cstring): cint{.extdecl, importc: "addstr", dynlib: pdcursesdll.}
-proc attroff*(a2: cunsignedlong): cint{.extdecl, importc: "attroff",
-                                        dynlib: pdcursesdll.}
-proc attron*(a2: cunsignedlong): cint{.extdecl, importc: "attron",
-                                       dynlib: pdcursesdll.}
-proc attrset*(a2: cunsignedlong): cint{.extdecl, importc: "attrset",
-                                        dynlib: pdcursesdll.}
-proc attr_get*(a2: ptr cunsignedlong; a3: ptr cshort; a4: pointer): cint{.extdecl,
-    importc: "attr_get", dynlib: pdcursesdll.}
-proc attr_off*(a2: cunsignedlong; a3: pointer): cint{.extdecl,
-    importc: "attr_off", dynlib: pdcursesdll.}
-proc attr_on*(a2: cunsignedlong; a3: pointer): cint{.extdecl, importc: "attr_on",
-    dynlib: pdcursesdll.}
-proc attr_set*(a2: cunsignedlong; a3: cshort; a4: pointer): cint{.extdecl,
-    importc: "attr_set", dynlib: pdcursesdll.}
-proc baudrate*(): cint{.extdecl, importc: "baudrate", dynlib: pdcursesdll.}
-proc beep*(): cint{.extdecl, importc: "beep", dynlib: pdcursesdll.}
-proc bkgd*(a2: cunsignedlong): cint{.extdecl, importc: "bkgd", dynlib: pdcursesdll.}
-proc bkgdset*(a2: cunsignedlong){.extdecl, importc: "bkgdset", dynlib: pdcursesdll.}
-proc border*(a2: cunsignedlong; a3: cunsignedlong; a4: cunsignedlong;
-             a5: cunsignedlong; a6: cunsignedlong; a7: cunsignedlong;
-             a8: cunsignedlong; a9: cunsignedlong): cint{.extdecl,
-    importc: "border", dynlib: pdcursesdll.}
-proc box*(a2: ptr WINDOW; a3: cunsignedlong; a4: cunsignedlong): cint{.extdecl,
-    importc: "box", dynlib: pdcursesdll.}
-proc can_change_color*(): cunsignedchar{.extdecl, importc: "can_change_color",
-    dynlib: pdcursesdll.}
-proc cbreak*(): cint{.extdecl, importc: "cbreak", dynlib: pdcursesdll.}
-proc chgat*(a2: cint; a3: cunsignedlong; a4: cshort; a5: pointer): cint{.extdecl,
-    importc: "chgat", dynlib: pdcursesdll.}
-proc clearok*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "clearok", dynlib: pdcursesdll.}
-proc clear*(): cint{.extdecl, importc: "clear", dynlib: pdcursesdll.}
-proc clrtobot*(): cint{.extdecl, importc: "clrtobot", dynlib: pdcursesdll.}
-proc clrtoeol*(): cint{.extdecl, importc: "clrtoeol", dynlib: pdcursesdll.}
-proc color_content*(a2: cshort; a3: ptr cshort; a4: ptr cshort; a5: ptr cshort): cint{.
-    extdecl, importc: "color_content", dynlib: pdcursesdll.}
-proc color_set*(a2: cshort; a3: pointer): cint{.extdecl, importc: "color_set",
-    dynlib: pdcursesdll.}
-proc copywin*(a2: ptr WINDOW; a3: ptr WINDOW; a4: cint; a5: cint; a6: cint;
-              a7: cint; a8: cint; a9: cint; a10: cint): cint{.extdecl,
-    importc: "copywin", dynlib: pdcursesdll.}
-proc curs_set*(a2: cint): cint{.extdecl, importc: "curs_set", dynlib: pdcursesdll.}
-proc def_prog_mode*(): cint{.extdecl, importc: "def_prog_mode",
-                             dynlib: pdcursesdll.}
-proc def_shell_mode*(): cint{.extdecl, importc: "def_shell_mode",
-                              dynlib: pdcursesdll.}
-proc delay_output*(a2: cint): cint{.extdecl, importc: "delay_output",
-                                    dynlib: pdcursesdll.}
-proc delch*(): cint{.extdecl, importc: "delch", dynlib: pdcursesdll.}
-proc deleteln*(): cint{.extdecl, importc: "deleteln", dynlib: pdcursesdll.}
-proc delscreen*(a2: ptr SCREEN){.extdecl, importc: "delscreen",
-                                 dynlib: pdcursesdll.}
-proc delwin*(a2: ptr WINDOW): cint{.extdecl, importc: "delwin",
-                                    dynlib: pdcursesdll.}
-proc derwin*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cint): ptr WINDOW{.
-    extdecl, importc: "derwin", dynlib: pdcursesdll.}
-proc doupdate*(): cint{.extdecl, importc: "doupdate", dynlib: pdcursesdll.}
-proc dupwin*(a2: ptr WINDOW): ptr WINDOW{.extdecl, importc: "dupwin",
-    dynlib: pdcursesdll.}
-proc echochar*(a2: cunsignedlong): cint{.extdecl, importc: "echochar",
-    dynlib: pdcursesdll.}
-proc echo*(): cint{.extdecl, importc: "echo", dynlib: pdcursesdll.}
-proc endwin*(): cint{.extdecl, importc: "endwin", dynlib: pdcursesdll.}
-proc erasechar*(): char{.extdecl, importc: "erasechar", dynlib: pdcursesdll.}
-proc erase*(): cint{.extdecl, importc: "erase", dynlib: pdcursesdll.}
-proc filter*(){.extdecl, importc: "filter", dynlib: pdcursesdll.}
-proc flash*(): cint{.extdecl, importc: "flash", dynlib: pdcursesdll.}
-proc flushinp*(): cint{.extdecl, importc: "flushinp", dynlib: pdcursesdll.}
-proc getbkgd*(a2: ptr WINDOW): cunsignedlong{.extdecl, importc: "getbkgd",
-    dynlib: pdcursesdll.}
-proc getnstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "getnstr",
-    dynlib: pdcursesdll.}
-proc getstr*(a2: cstring): cint{.extdecl, importc: "getstr", dynlib: pdcursesdll.}
-proc getwin*(a2: File): ptr WINDOW{.extdecl, importc: "getwin",
-                                        dynlib: pdcursesdll.}
-proc halfdelay*(a2: cint): cint{.extdecl, importc: "halfdelay",
-                                 dynlib: pdcursesdll.}
-proc has_colors*(): cunsignedchar{.extdecl, importc: "has_colors",
-                                   dynlib: pdcursesdll.}
-proc has_ic*(): cunsignedchar{.extdecl, importc: "has_ic", dynlib: pdcursesdll.}
-proc has_il*(): cunsignedchar{.extdecl, importc: "has_il", dynlib: pdcursesdll.}
-proc hline*(a2: cunsignedlong; a3: cint): cint{.extdecl, importc: "hline",
-    dynlib: pdcursesdll.}
-proc idcok*(a2: ptr WINDOW; a3: cunsignedchar){.extdecl, importc: "idcok",
-    dynlib: pdcursesdll.}
-proc idlok*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl, importc: "idlok",
-    dynlib: pdcursesdll.}
-proc immedok*(a2: ptr WINDOW; a3: cunsignedchar){.extdecl, importc: "immedok",
-    dynlib: pdcursesdll.}
-proc inchnstr*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "inchnstr", dynlib: pdcursesdll.}
-proc inchstr*(a2: ptr cunsignedlong): cint{.extdecl, importc: "inchstr",
-    dynlib: pdcursesdll.}
-proc inch*(): cunsignedlong{.extdecl, importc: "inch", dynlib: pdcursesdll.}
-proc init_color*(a2: cshort; a3: cshort; a4: cshort; a5: cshort): cint{.extdecl,
-    importc: "init_color", dynlib: pdcursesdll.}
-proc init_pair*(a2: cshort; a3: cshort; a4: cshort): cint{.extdecl,
-    importc: "init_pair", dynlib: pdcursesdll.}
-proc initscr*(): ptr WINDOW{.extdecl, importc: "initscr", dynlib: pdcursesdll.}
-proc innstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "innstr",
-    dynlib: pdcursesdll.}
-proc insch*(a2: cunsignedlong): cint{.extdecl, importc: "insch",
-                                      dynlib: pdcursesdll.}
-proc insdelln*(a2: cint): cint{.extdecl, importc: "insdelln", dynlib: pdcursesdll.}
-proc insertln*(): cint{.extdecl, importc: "insertln", dynlib: pdcursesdll.}
-proc insnstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "insnstr",
-    dynlib: pdcursesdll.}
-proc insstr*(a2: cstring): cint{.extdecl, importc: "insstr", dynlib: pdcursesdll.}
-proc instr*(a2: cstring): cint{.extdecl, importc: "instr", dynlib: pdcursesdll.}
-proc intrflush*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "intrflush", dynlib: pdcursesdll.}
-proc isendwin*(): cunsignedchar{.extdecl, importc: "isendwin", dynlib: pdcursesdll.}
-proc is_linetouched*(a2: ptr WINDOW; a3: cint): cunsignedchar{.extdecl,
-    importc: "is_linetouched", dynlib: pdcursesdll.}
-proc is_wintouched*(a2: ptr WINDOW): cunsignedchar{.extdecl,
-    importc: "is_wintouched", dynlib: pdcursesdll.}
-proc keyname*(a2: cint): cstring{.extdecl, importc: "keyname", dynlib: pdcursesdll.}
-proc keypad*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl, importc: "keypad",
-    dynlib: pdcursesdll.}
-proc killchar*(): char{.extdecl, importc: "killchar", dynlib: pdcursesdll.}
-proc leaveok*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "leaveok", dynlib: pdcursesdll.}
-proc longname*(): cstring{.extdecl, importc: "longname", dynlib: pdcursesdll.}
-proc meta*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl, importc: "meta",
-    dynlib: pdcursesdll.}
-proc move*(a2: cint; a3: cint): cint{.extdecl, importc: "move",
-                                      dynlib: pdcursesdll.}
-proc mvaddch*(a2: cint; a3: cint; a4: cunsignedlong): cint{.extdecl,
-    importc: "mvaddch", dynlib: pdcursesdll.}
-proc mvaddchnstr*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvaddchnstr", dynlib: pdcursesdll.}
-proc mvaddchstr*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvaddchstr", dynlib: pdcursesdll.}
-proc mvaddnstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvaddnstr", dynlib: pdcursesdll.}
-proc mvaddstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvaddstr", dynlib: pdcursesdll.}
-proc mvchgat*(a2: cint; a3: cint; a4: cint; a5: cunsignedlong; a6: cshort;
-              a7: pointer): cint{.extdecl, importc: "mvchgat", dynlib: pdcursesdll.}
-proc mvcur*(a2: cint; a3: cint; a4: cint; a5: cint): cint{.extdecl,
-    importc: "mvcur", dynlib: pdcursesdll.}
-proc mvdelch*(a2: cint; a3: cint): cint{.extdecl, importc: "mvdelch",
-    dynlib: pdcursesdll.}
-proc mvderwin*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "mvderwin", dynlib: pdcursesdll.}
-proc mvgetch*(a2: cint; a3: cint): cint{.extdecl, importc: "mvgetch",
-    dynlib: pdcursesdll.}
-proc mvgetnstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvgetnstr", dynlib: pdcursesdll.}
-proc mvgetstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvgetstr", dynlib: pdcursesdll.}
-proc mvhline*(a2: cint; a3: cint; a4: cunsignedlong; a5: cint): cint{.extdecl,
-    importc: "mvhline", dynlib: pdcursesdll.}
-proc mvinch*(a2: cint; a3: cint): cunsignedlong{.extdecl, importc: "mvinch",
-    dynlib: pdcursesdll.}
-proc mvinchnstr*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvinchnstr", dynlib: pdcursesdll.}
-proc mvinchstr*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvinchstr", dynlib: pdcursesdll.}
-proc mvinnstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvinnstr", dynlib: pdcursesdll.}
-proc mvinsch*(a2: cint; a3: cint; a4: cunsignedlong): cint{.extdecl,
-    importc: "mvinsch", dynlib: pdcursesdll.}
-proc mvinsnstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvinsnstr", dynlib: pdcursesdll.}
-proc mvinsstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvinsstr", dynlib: pdcursesdll.}
-proc mvinstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl, importc: "mvinstr",
-    dynlib: pdcursesdll.}
-proc mvprintw*(a2: cint; a3: cint; a4: cstring): cint{.varargs, extdecl,
-    importc: "mvprintw", dynlib: pdcursesdll.}
-proc mvscanw*(a2: cint; a3: cint; a4: cstring): cint{.varargs, extdecl,
-    importc: "mvscanw", dynlib: pdcursesdll.}
-proc mvvline*(a2: cint; a3: cint; a4: cunsignedlong; a5: cint): cint{.extdecl,
-    importc: "mvvline", dynlib: pdcursesdll.}
-proc mvwaddchnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                   a6: cint): cint{.extdecl, importc: "mvwaddchnstr",
-                                    dynlib: pdcursesdll.}
-proc mvwaddchstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwaddchstr", dynlib: pdcursesdll.}
-proc mvwaddch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong): cint{.
-    extdecl, importc: "mvwaddch", dynlib: pdcursesdll.}
-proc mvwaddnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwaddnstr", dynlib: pdcursesdll.}
-proc mvwaddstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.extdecl,
-    importc: "mvwaddstr", dynlib: pdcursesdll.}
-proc mvwchgat*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cunsignedlong;
-               a7: cshort; a8: pointer): cint{.extdecl, importc: "mvwchgat",
-    dynlib: pdcursesdll.}
-proc mvwdelch*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "mvwdelch", dynlib: pdcursesdll.}
-proc mvwgetch*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "mvwgetch", dynlib: pdcursesdll.}
-proc mvwgetnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwgetnstr", dynlib: pdcursesdll.}
-proc mvwgetstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.extdecl,
-    importc: "mvwgetstr", dynlib: pdcursesdll.}
-proc mvwhline*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong; a6: cint): cint{.
-    extdecl, importc: "mvwhline", dynlib: pdcursesdll.}
-proc mvwinchnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                  a6: cint): cint{.extdecl, importc: "mvwinchnstr",
-                                   dynlib: pdcursesdll.}
-proc mvwinchstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwinchstr", dynlib: pdcursesdll.}
-proc mvwinch*(a2: ptr WINDOW; a3: cint; a4: cint): cunsignedlong{.extdecl,
-    importc: "mvwinch", dynlib: pdcursesdll.}
-proc mvwinnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwinnstr", dynlib: pdcursesdll.}
-proc mvwinsch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong): cint{.
-    extdecl, importc: "mvwinsch", dynlib: pdcursesdll.}
-proc mvwinsnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwinsnstr", dynlib: pdcursesdll.}
-proc mvwinsstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.extdecl,
-    importc: "mvwinsstr", dynlib: pdcursesdll.}
-proc mvwinstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.extdecl,
-    importc: "mvwinstr", dynlib: pdcursesdll.}
-proc mvwin*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl, importc: "mvwin",
-    dynlib: pdcursesdll.}
-proc mvwprintw*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.varargs,
-    extdecl, importc: "mvwprintw", dynlib: pdcursesdll.}
-proc mvwscanw*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.varargs,
-    extdecl, importc: "mvwscanw", dynlib: pdcursesdll.}
-proc mvwvline*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong; a6: cint): cint{.
-    extdecl, importc: "mvwvline", dynlib: pdcursesdll.}
-proc napms*(a2: cint): cint{.extdecl, importc: "napms", dynlib: pdcursesdll.}
-proc newpad*(a2: cint; a3: cint): ptr WINDOW{.extdecl, importc: "newpad",
-    dynlib: pdcursesdll.}
-proc newterm*(a2: cstring; a3: File; a4: File): ptr SCREEN{.extdecl,
-    importc: "newterm", dynlib: pdcursesdll.}
-proc newwin*(a2: cint; a3: cint; a4: cint; a5: cint): ptr WINDOW{.extdecl,
-    importc: "newwin", dynlib: pdcursesdll.}
-proc nl*(): cint{.extdecl, importc: "nl", dynlib: pdcursesdll.}
-proc nocbreak*(): cint{.extdecl, importc: "nocbreak", dynlib: pdcursesdll.}
-proc nodelay*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "nodelay", dynlib: pdcursesdll.}
-proc noecho*(): cint{.extdecl, importc: "noecho", dynlib: pdcursesdll.}
-proc nonl*(): cint{.extdecl, importc: "nonl", dynlib: pdcursesdll.}
-proc noqiflush*(){.extdecl, importc: "noqiflush", dynlib: pdcursesdll.}
-proc noraw*(): cint{.extdecl, importc: "noraw", dynlib: pdcursesdll.}
-proc notimeout*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "notimeout", dynlib: pdcursesdll.}
-proc overlay*(a2: ptr WINDOW; a3: ptr WINDOW): cint{.extdecl, importc: "overlay",
-    dynlib: pdcursesdll.}
-proc overwrite*(a2: ptr WINDOW; a3: ptr WINDOW): cint{.extdecl,
-    importc: "overwrite", dynlib: pdcursesdll.}
-proc pair_content*(a2: cshort; a3: ptr cshort; a4: ptr cshort): cint{.extdecl,
-    importc: "pair_content", dynlib: pdcursesdll.}
-proc pechochar*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "pechochar", dynlib: pdcursesdll.}
-proc pnoutrefresh*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cint;
-                   a7: cint; a8: cint): cint{.extdecl, importc: "pnoutrefresh",
-    dynlib: pdcursesdll.}
-proc prefresh*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cint; a7: cint;
-               a8: cint): cint{.extdecl, importc: "prefresh", dynlib: pdcursesdll.}
-proc printw*(a2: cstring): cint{.varargs, extdecl, importc: "printw",
-                                 dynlib: pdcursesdll.}
-proc putwin*(a2: ptr WINDOW; a3: File): cint{.extdecl, importc: "putwin",
-    dynlib: pdcursesdll.}
-proc qiflush*(){.extdecl, importc: "qiflush", dynlib: pdcursesdll.}
-proc raw*(): cint{.extdecl, importc: "raw", dynlib: pdcursesdll.}
-proc redrawwin*(a2: ptr WINDOW): cint{.extdecl, importc: "redrawwin",
-                                       dynlib: pdcursesdll.}
-proc refresh*(): cint{.extdecl, importc: "refresh", dynlib: pdcursesdll.}
-proc reset_prog_mode*(): cint{.extdecl, importc: "reset_prog_mode",
-                               dynlib: pdcursesdll.}
-proc reset_shell_mode*(): cint{.extdecl, importc: "reset_shell_mode",
-                                dynlib: pdcursesdll.}
-proc resetty*(): cint{.extdecl, importc: "resetty", dynlib: pdcursesdll.}
-#int     ripoffline(int, int (*)(WINDOW *, int));
-proc savetty*(): cint{.extdecl, importc: "savetty", dynlib: pdcursesdll.}
-proc scanw*(a2: cstring): cint{.varargs, extdecl, importc: "scanw",
-                                dynlib: pdcursesdll.}
-proc scr_dump*(a2: cstring): cint{.extdecl, importc: "scr_dump",
-                                   dynlib: pdcursesdll.}
-proc scr_init*(a2: cstring): cint{.extdecl, importc: "scr_init",
-                                   dynlib: pdcursesdll.}
-proc scr_restore*(a2: cstring): cint{.extdecl, importc: "scr_restore",
-                                      dynlib: pdcursesdll.}
-proc scr_set*(a2: cstring): cint{.extdecl, importc: "scr_set", dynlib: pdcursesdll.}
-proc scrl*(a2: cint): cint{.extdecl, importc: "scrl", dynlib: pdcursesdll.}
-proc scroll*(a2: ptr WINDOW): cint{.extdecl, importc: "scroll",
-                                    dynlib: pdcursesdll.}
-proc scrollok*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl,
-    importc: "scrollok", dynlib: pdcursesdll.}
-proc set_term*(a2: ptr SCREEN): ptr SCREEN{.extdecl, importc: "set_term",
-    dynlib: pdcursesdll.}
-proc setscrreg*(a2: cint; a3: cint): cint{.extdecl, importc: "setscrreg",
-    dynlib: pdcursesdll.}
-proc slk_attroff*(a2: cunsignedlong): cint{.extdecl, importc: "slk_attroff",
-    dynlib: pdcursesdll.}
-proc slk_attr_off*(a2: cunsignedlong; a3: pointer): cint{.extdecl,
-    importc: "slk_attr_off", dynlib: pdcursesdll.}
-proc slk_attron*(a2: cunsignedlong): cint{.extdecl, importc: "slk_attron",
-    dynlib: pdcursesdll.}
-proc slk_attr_on*(a2: cunsignedlong; a3: pointer): cint{.extdecl,
-    importc: "slk_attr_on", dynlib: pdcursesdll.}
-proc slk_attrset*(a2: cunsignedlong): cint{.extdecl, importc: "slk_attrset",
-    dynlib: pdcursesdll.}
-proc slk_attr_set*(a2: cunsignedlong; a3: cshort; a4: pointer): cint{.extdecl,
-    importc: "slk_attr_set", dynlib: pdcursesdll.}
-proc slk_clear*(): cint{.extdecl, importc: "slk_clear", dynlib: pdcursesdll.}
-proc slk_color*(a2: cshort): cint{.extdecl, importc: "slk_color",
-                                   dynlib: pdcursesdll.}
-proc slk_init*(a2: cint): cint{.extdecl, importc: "slk_init", dynlib: pdcursesdll.}
-proc slk_label*(a2: cint): cstring{.extdecl, importc: "slk_label",
-                                    dynlib: pdcursesdll.}
-proc slk_noutrefresh*(): cint{.extdecl, importc: "slk_noutrefresh",
-                               dynlib: pdcursesdll.}
-proc slk_refresh*(): cint{.extdecl, importc: "slk_refresh", dynlib: pdcursesdll.}
-proc slk_restore*(): cint{.extdecl, importc: "slk_restore", dynlib: pdcursesdll.}
-proc slk_set*(a2: cint; a3: cstring; a4: cint): cint{.extdecl, importc: "slk_set",
-    dynlib: pdcursesdll.}
-proc slk_touch*(): cint{.extdecl, importc: "slk_touch", dynlib: pdcursesdll.}
-proc standend*(): cint{.extdecl, importc: "standend", dynlib: pdcursesdll.}
-proc standout*(): cint{.extdecl, importc: "standout", dynlib: pdcursesdll.}
-proc start_color*(): cint{.extdecl, importc: "start_color", dynlib: pdcursesdll.}
-proc subpad*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cint): ptr WINDOW{.
-    extdecl, importc: "subpad", dynlib: pdcursesdll.}
-proc subwin*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint; a6: cint): ptr WINDOW{.
-    extdecl, importc: "subwin", dynlib: pdcursesdll.}
-proc syncok*(a2: ptr WINDOW; a3: cunsignedchar): cint{.extdecl, importc: "syncok",
-    dynlib: pdcursesdll.}
-proc termattrs*(): cunsignedlong{.extdecl, importc: "termattrs",
-                                  dynlib: pdcursesdll.}
-proc termattrs2*(): cunsignedlong{.extdecl, importc: "term_attrs",
-                                   dynlib: pdcursesdll.}
-proc termname*(): cstring{.extdecl, importc: "termname", dynlib: pdcursesdll.}
-proc timeout*(a2: cint){.extdecl, importc: "timeout", dynlib: pdcursesdll.}
-proc touchline*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "touchline", dynlib: pdcursesdll.}
-proc touchwin*(a2: ptr WINDOW): cint{.extdecl, importc: "touchwin",
-                                      dynlib: pdcursesdll.}
-proc typeahead*(a2: cint): cint{.extdecl, importc: "typeahead",
-                                 dynlib: pdcursesdll.}
-proc untouchwin*(a2: ptr WINDOW): cint{.extdecl, importc: "untouchwin",
-                                        dynlib: pdcursesdll.}
-proc use_env*(a2: cunsignedchar){.extdecl, importc: "use_env", dynlib: pdcursesdll.}
-proc vidattr*(a2: cunsignedlong): cint{.extdecl, importc: "vidattr",
-                                        dynlib: pdcursesdll.}
-proc vid_attr*(a2: cunsignedlong; a3: cshort; a4: pointer): cint{.extdecl,
-    importc: "vid_attr", dynlib: pdcursesdll.}
-#int     vidputs(chtype, int (*)(int));
-#int     vid_puts(attr_t, short, void *, int (*)(int));
-proc vline*(a2: cunsignedlong; a3: cint): cint{.extdecl, importc: "vline",
-    dynlib: pdcursesdll.}
-proc vwprintw*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, varargs,
-    importc: "vw_printw", dynlib: pdcursesdll.}
-proc vwprintw2*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, varargs,
-    importc: "vwprintw", dynlib: pdcursesdll.}
-proc vwscanw*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, varargs,
-    importc: "vw_scanw", dynlib: pdcursesdll.}
-proc vwscanw2*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, varargs,
-    importc: "vwscanw", dynlib: pdcursesdll.}
-proc waddchnstr*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "waddchnstr", dynlib: pdcursesdll.}
-proc waddchstr*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "waddchstr", dynlib: pdcursesdll.}
-proc waddch*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl, importc: "waddch",
-    dynlib: pdcursesdll.}
-proc waddnstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "waddnstr", dynlib: pdcursesdll.}
-proc waddstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, importc: "waddstr",
-    dynlib: pdcursesdll.}
-proc wattroff*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "wattroff", dynlib: pdcursesdll.}
-proc wattron*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "wattron", dynlib: pdcursesdll.}
-proc wattrset*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "wattrset", dynlib: pdcursesdll.}
-proc wattr_get*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: ptr cshort;
-                a5: pointer): cint{.extdecl, importc: "wattr_get",
-                                    dynlib: pdcursesdll.}
-proc wattr_off*(a2: ptr WINDOW; a3: cunsignedlong; a4: pointer): cint{.extdecl,
-    importc: "wattr_off", dynlib: pdcursesdll.}
-proc wattr_on*(a2: ptr WINDOW; a3: cunsignedlong; a4: pointer): cint{.extdecl,
-    importc: "wattr_on", dynlib: pdcursesdll.}
-proc wattr_set*(a2: ptr WINDOW; a3: cunsignedlong; a4: cshort; a5: pointer): cint{.
-    extdecl, importc: "wattr_set", dynlib: pdcursesdll.}
-proc wbkgdset*(a2: ptr WINDOW; a3: cunsignedlong){.extdecl, importc: "wbkgdset",
-    dynlib: pdcursesdll.}
-proc wbkgd*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl, importc: "wbkgd",
-    dynlib: pdcursesdll.}
-proc wborder*(a2: ptr WINDOW; a3: cunsignedlong; a4: cunsignedlong;
-              a5: cunsignedlong; a6: cunsignedlong; a7: cunsignedlong;
-              a8: cunsignedlong; a9: cunsignedlong; a10: cunsignedlong): cint{.
-    extdecl, importc: "wborder", dynlib: pdcursesdll.}
-proc wchgat*(a2: ptr WINDOW; a3: cint; a4: cunsignedlong; a5: cshort;
-             a6: pointer): cint{.extdecl, importc: "wchgat", dynlib: pdcursesdll.}
-proc wclear*(a2: ptr WINDOW): cint{.extdecl, importc: "wclear",
-                                    dynlib: pdcursesdll.}
-proc wclrtobot*(a2: ptr WINDOW): cint{.extdecl, importc: "wclrtobot",
-                                       dynlib: pdcursesdll.}
-proc wclrtoeol*(a2: ptr WINDOW): cint{.extdecl, importc: "wclrtoeol",
-                                       dynlib: pdcursesdll.}
-proc wcolor_set*(a2: ptr WINDOW; a3: cshort; a4: pointer): cint{.extdecl,
-    importc: "wcolor_set", dynlib: pdcursesdll.}
-proc wcursyncup*(a2: ptr WINDOW){.extdecl, importc: "wcursyncup",
-                                  dynlib: pdcursesdll.}
-proc wdelch*(a2: ptr WINDOW): cint{.extdecl, importc: "wdelch",
-                                    dynlib: pdcursesdll.}
-proc wdeleteln*(a2: ptr WINDOW): cint{.extdecl, importc: "wdeleteln",
-                                       dynlib: pdcursesdll.}
-proc wechochar*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "wechochar", dynlib: pdcursesdll.}
-proc werase*(a2: ptr WINDOW): cint{.extdecl, importc: "werase",
-                                    dynlib: pdcursesdll.}
-proc wgetch*(a2: ptr WINDOW): cint{.extdecl, importc: "wgetch",
-                                    dynlib: pdcursesdll.}
-proc wgetnstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "wgetnstr", dynlib: pdcursesdll.}
-proc wgetstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, importc: "wgetstr",
-    dynlib: pdcursesdll.}
-proc whline*(a2: ptr WINDOW; a3: cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "whline", dynlib: pdcursesdll.}
-proc winchnstr*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "winchnstr", dynlib: pdcursesdll.}
-proc winchstr*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "winchstr", dynlib: pdcursesdll.}
-proc winch*(a2: ptr WINDOW): cunsignedlong{.extdecl, importc: "winch",
-    dynlib: pdcursesdll.}
-proc winnstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "winnstr", dynlib: pdcursesdll.}
-proc winsch*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl, importc: "winsch",
-    dynlib: pdcursesdll.}
-proc winsdelln*(a2: ptr WINDOW; a3: cint): cint{.extdecl, importc: "winsdelln",
-    dynlib: pdcursesdll.}
-proc winsertln*(a2: ptr WINDOW): cint{.extdecl, importc: "winsertln",
-                                       dynlib: pdcursesdll.}
-proc winsnstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "winsnstr", dynlib: pdcursesdll.}
-proc winsstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, importc: "winsstr",
-    dynlib: pdcursesdll.}
-proc winstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, importc: "winstr",
-    dynlib: pdcursesdll.}
-proc wmove*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl, importc: "wmove",
-    dynlib: pdcursesdll.}
-proc wnoutrefresh*(a2: ptr WINDOW): cint{.extdecl, importc: "wnoutrefresh",
-    dynlib: pdcursesdll.}
-proc wprintw*(a2: ptr WINDOW; a3: cstring): cint{.varargs, extdecl,
-    importc: "wprintw", dynlib: pdcursesdll.}
-proc wredrawln*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "wredrawln", dynlib: pdcursesdll.}
-proc wrefresh*(a2: ptr WINDOW): cint{.extdecl, importc: "wrefresh",
-                                      dynlib: pdcursesdll.}
-proc wscanw*(a2: ptr WINDOW; a3: cstring): cint{.varargs, extdecl,
-    importc: "wscanw", dynlib: pdcursesdll.}
-proc wscrl*(a2: ptr WINDOW; a3: cint): cint{.extdecl, importc: "wscrl",
-    dynlib: pdcursesdll.}
-proc wsetscrreg*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "wsetscrreg", dynlib: pdcursesdll.}
-proc wstandend*(a2: ptr WINDOW): cint{.extdecl, importc: "wstandend",
-                                       dynlib: pdcursesdll.}
-proc wstandout*(a2: ptr WINDOW): cint{.extdecl, importc: "wstandout",
-                                       dynlib: pdcursesdll.}
-proc wsyncdown*(a2: ptr WINDOW){.extdecl, importc: "wsyncdown",
-                                 dynlib: pdcursesdll.}
-proc wsyncup*(a2: ptr WINDOW){.extdecl, importc: "wsyncup", dynlib: pdcursesdll.}
-proc wtimeout*(a2: ptr WINDOW; a3: cint){.extdecl, importc: "wtimeout",
-    dynlib: pdcursesdll.}
-proc wtouchln*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cint): cint{.extdecl,
-    importc: "wtouchln", dynlib: pdcursesdll.}
-proc wvline*(a2: ptr WINDOW; a3: cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "wvline", dynlib: pdcursesdll.}
-proc addnwstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "addnwstr",
-    dynlib: pdcursesdll.}
-proc addwstr*(a2: cstring): cint{.extdecl, importc: "addwstr",
-                                      dynlib: pdcursesdll.}
-proc add_wch*(a2: ptr cunsignedlong): cint{.extdecl, importc: "add_wch",
-    dynlib: pdcursesdll.}
-proc add_wchnstr*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "add_wchnstr", dynlib: pdcursesdll.}
-proc add_wchstr*(a2: ptr cunsignedlong): cint{.extdecl, importc: "add_wchstr",
-    dynlib: pdcursesdll.}
-proc border_set*(a2: ptr cunsignedlong; a3: ptr cunsignedlong;
-                 a4: ptr cunsignedlong; a5: ptr cunsignedlong;
-                 a6: ptr cunsignedlong; a7: ptr cunsignedlong;
-                 a8: ptr cunsignedlong; a9: ptr cunsignedlong): cint{.extdecl,
-    importc: "border_set", dynlib: pdcursesdll.}
-proc box_set*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: ptr cunsignedlong): cint{.
-    extdecl, importc: "box_set", dynlib: pdcursesdll.}
-proc echo_wchar*(a2: ptr cunsignedlong): cint{.extdecl, importc: "echo_wchar",
-    dynlib: pdcursesdll.}
-proc erasewchar*(a2: cstring): cint{.extdecl, importc: "erasewchar",
-    dynlib: pdcursesdll.}
-proc getbkgrnd*(a2: ptr cunsignedlong): cint{.extdecl, importc: "getbkgrnd",
-    dynlib: pdcursesdll.}
-proc getcchar*(a2: ptr cunsignedlong; a3: cstring; a4: ptr cunsignedlong;
-               a5: ptr cshort; a6: pointer): cint{.extdecl, importc: "getcchar",
-    dynlib: pdcursesdll.}
-proc getn_wstr*(a2: ptr cint; a3: cint): cint{.extdecl, importc: "getn_wstr",
-    dynlib: pdcursesdll.}
-proc get_wch*(a2: ptr cint): cint{.extdecl, importc: "get_wch",
-                                     dynlib: pdcursesdll.}
-proc get_wstr*(a2: ptr cint): cint{.extdecl, importc: "get_wstr",
-                                      dynlib: pdcursesdll.}
-proc hline_set*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "hline_set", dynlib: pdcursesdll.}
-proc innwstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "innwstr",
-    dynlib: pdcursesdll.}
-proc ins_nwstr*(a2: cstring; a3: cint): cint{.extdecl, importc: "ins_nwstr",
-    dynlib: pdcursesdll.}
-proc ins_wch*(a2: ptr cunsignedlong): cint{.extdecl, importc: "ins_wch",
-    dynlib: pdcursesdll.}
-proc ins_wstr*(a2: cstring): cint{.extdecl, importc: "ins_wstr",
-                                       dynlib: pdcursesdll.}
-proc inwstr*(a2: cstring): cint{.extdecl, importc: "inwstr",
-                                     dynlib: pdcursesdll.}
-proc in_wch*(a2: ptr cunsignedlong): cint{.extdecl, importc: "in_wch",
-    dynlib: pdcursesdll.}
-proc in_wchnstr*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "in_wchnstr", dynlib: pdcursesdll.}
-proc in_wchstr*(a2: ptr cunsignedlong): cint{.extdecl, importc: "in_wchstr",
-    dynlib: pdcursesdll.}
-proc key_name*(a2: char): cstring{.extdecl, importc: "key_name",
-                                      dynlib: pdcursesdll.}
-proc killwchar*(a2: cstring): cint{.extdecl, importc: "killwchar",
-                                        dynlib: pdcursesdll.}
-proc mvaddnwstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvaddnwstr", dynlib: pdcursesdll.}
-proc mvaddwstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvaddwstr", dynlib: pdcursesdll.}
-proc mvadd_wch*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvadd_wch", dynlib: pdcursesdll.}
-proc mvadd_wchnstr*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvadd_wchnstr", dynlib: pdcursesdll.}
-proc mvadd_wchstr*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvadd_wchstr", dynlib: pdcursesdll.}
-proc mvgetn_wstr*(a2: cint; a3: cint; a4: ptr cint; a5: cint): cint{.extdecl,
-    importc: "mvgetn_wstr", dynlib: pdcursesdll.}
-proc mvget_wch*(a2: cint; a3: cint; a4: ptr cint): cint{.extdecl,
-    importc: "mvget_wch", dynlib: pdcursesdll.}
-proc mvget_wstr*(a2: cint; a3: cint; a4: ptr cint): cint{.extdecl,
-    importc: "mvget_wstr", dynlib: pdcursesdll.}
-proc mvhline_set*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvhline_set", dynlib: pdcursesdll.}
-proc mvinnwstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvinnwstr", dynlib: pdcursesdll.}
-proc mvins_nwstr*(a2: cint; a3: cint; a4: cstring; a5: cint): cint{.extdecl,
-    importc: "mvins_nwstr", dynlib: pdcursesdll.}
-proc mvins_wch*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvins_wch", dynlib: pdcursesdll.}
-proc mvins_wstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvins_wstr", dynlib: pdcursesdll.}
-proc mvinwstr*(a2: cint; a3: cint; a4: cstring): cint{.extdecl,
-    importc: "mvinwstr", dynlib: pdcursesdll.}
-proc mvin_wch*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvin_wch", dynlib: pdcursesdll.}
-proc mvin_wchnstr*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvin_wchnstr", dynlib: pdcursesdll.}
-proc mvin_wchstr*(a2: cint; a3: cint; a4: ptr cunsignedlong): cint{.extdecl,
-    importc: "mvin_wchstr", dynlib: pdcursesdll.}
-proc mvvline_set*(a2: cint; a3: cint; a4: ptr cunsignedlong; a5: cint): cint{.
-    extdecl, importc: "mvvline_set", dynlib: pdcursesdll.}
-proc mvwaddnwstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwaddnwstr", dynlib: pdcursesdll.}
-proc mvwaddwstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.
-    extdecl, importc: "mvwaddwstr", dynlib: pdcursesdll.}
-proc mvwadd_wch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwadd_wch", dynlib: pdcursesdll.}
-proc mvwadd_wchnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                     a6: cint): cint{.extdecl, importc: "mvwadd_wchnstr",
-                                      dynlib: pdcursesdll.}
-proc mvwadd_wchstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwadd_wchstr", dynlib: pdcursesdll.}
-proc mvwgetn_wstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cint; a6: cint): cint{.
-    extdecl, importc: "mvwgetn_wstr", dynlib: pdcursesdll.}
-proc mvwget_wch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cint): cint{.
-    extdecl, importc: "mvwget_wch", dynlib: pdcursesdll.}
-proc mvwget_wstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cint): cint{.
-    extdecl, importc: "mvwget_wstr", dynlib: pdcursesdll.}
-proc mvwhline_set*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                   a6: cint): cint{.extdecl, importc: "mvwhline_set",
-                                    dynlib: pdcursesdll.}
-proc mvwinnwstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwinnwstr", dynlib: pdcursesdll.}
-proc mvwins_nwstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring; a6: cint): cint{.
-    extdecl, importc: "mvwins_nwstr", dynlib: pdcursesdll.}
-proc mvwins_wch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwins_wch", dynlib: pdcursesdll.}
-proc mvwins_wstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.
-    extdecl, importc: "mvwins_wstr", dynlib: pdcursesdll.}
-proc mvwin_wch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwin_wch", dynlib: pdcursesdll.}
-proc mvwin_wchnstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                    a6: cint): cint{.extdecl, importc: "mvwin_wchnstr",
-                                     dynlib: pdcursesdll.}
-proc mvwin_wchstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong): cint{.
-    extdecl, importc: "mvwin_wchstr", dynlib: pdcursesdll.}
-proc mvwinwstr*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cstring): cint{.
-    extdecl, importc: "mvwinwstr", dynlib: pdcursesdll.}
-proc mvwvline_set*(a2: ptr WINDOW; a3: cint; a4: cint; a5: ptr cunsignedlong;
-                   a6: cint): cint{.extdecl, importc: "mvwvline_set",
-                                    dynlib: pdcursesdll.}
-proc pecho_wchar*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "pecho_wchar", dynlib: pdcursesdll.}
-proc setcchar*(a2: ptr cunsignedlong; a3: cstring; a4: cunsignedlong;
-               a5: cshort; a6: pointer): cint{.extdecl, importc: "setcchar",
-    dynlib: pdcursesdll.}
-proc slk_wset*(a2: cint; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "slk_wset", dynlib: pdcursesdll.}
-proc unget_wch*(a2: char): cint{.extdecl, importc: "unget_wch",
-                                    dynlib: pdcursesdll.}
-proc vline_set*(a2: ptr cunsignedlong; a3: cint): cint{.extdecl,
-    importc: "vline_set", dynlib: pdcursesdll.}
-proc waddnwstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "waddnwstr", dynlib: pdcursesdll.}
-proc waddwstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl,
-    importc: "waddwstr", dynlib: pdcursesdll.}
-proc wadd_wch*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wadd_wch", dynlib: pdcursesdll.}
-proc wadd_wchnstr*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.
-    extdecl, importc: "wadd_wchnstr", dynlib: pdcursesdll.}
-proc wadd_wchstr*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wadd_wchstr", dynlib: pdcursesdll.}
-proc wbkgrnd*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wbkgrnd", dynlib: pdcursesdll.}
-proc wbkgrndset*(a2: ptr WINDOW; a3: ptr cunsignedlong){.extdecl,
-    importc: "wbkgrndset", dynlib: pdcursesdll.}
-proc wborder_set*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: ptr cunsignedlong;
-                  a5: ptr cunsignedlong; a6: ptr cunsignedlong;
-                  a7: ptr cunsignedlong; a8: ptr cunsignedlong;
-                  a9: ptr cunsignedlong; a10: ptr cunsignedlong): cint{.extdecl,
-    importc: "wborder_set", dynlib: pdcursesdll.}
-proc wecho_wchar*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wecho_wchar", dynlib: pdcursesdll.}
-proc wgetbkgrnd*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wgetbkgrnd", dynlib: pdcursesdll.}
-proc wgetn_wstr*(a2: ptr WINDOW; a3: ptr cint; a4: cint): cint{.extdecl,
-    importc: "wgetn_wstr", dynlib: pdcursesdll.}
-proc wget_wch*(a2: ptr WINDOW; a3: ptr cint): cint{.extdecl,
-    importc: "wget_wch", dynlib: pdcursesdll.}
-proc wget_wstr*(a2: ptr WINDOW; a3: ptr cint): cint{.extdecl,
-    importc: "wget_wstr", dynlib: pdcursesdll.}
-proc whline_set*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "whline_set", dynlib: pdcursesdll.}
-proc winnwstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "winnwstr", dynlib: pdcursesdll.}
-proc wins_nwstr*(a2: ptr WINDOW; a3: cstring; a4: cint): cint{.extdecl,
-    importc: "wins_nwstr", dynlib: pdcursesdll.}
-proc wins_wch*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "wins_wch", dynlib: pdcursesdll.}
-proc wins_wstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl,
-    importc: "wins_wstr", dynlib: pdcursesdll.}
-proc winwstr*(a2: ptr WINDOW; a3: cstring): cint{.extdecl, importc: "winwstr",
-    dynlib: pdcursesdll.}
-proc win_wch*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "win_wch", dynlib: pdcursesdll.}
-proc win_wchnstr*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "win_wchnstr", dynlib: pdcursesdll.}
-proc win_wchstr*(a2: ptr WINDOW; a3: ptr cunsignedlong): cint{.extdecl,
-    importc: "win_wchstr", dynlib: pdcursesdll.}
-proc wunctrl*(a2: ptr cunsignedlong): cstring{.extdecl, importc: "wunctrl",
-    dynlib: pdcursesdll.}
-proc wvline_set*(a2: ptr WINDOW; a3: ptr cunsignedlong; a4: cint): cint{.extdecl,
-    importc: "wvline_set", dynlib: pdcursesdll.}
-proc getattrs*(a2: ptr WINDOW): cunsignedlong{.extdecl, importc: "getattrs",
-    dynlib: pdcursesdll.}
-proc getbegx*(a2: ptr WINDOW): cint{.extdecl, importc: "getbegx",
-                                     dynlib: pdcursesdll.}
-proc getbegy*(a2: ptr WINDOW): cint{.extdecl, importc: "getbegy",
-                                     dynlib: pdcursesdll.}
-proc getmaxx*(a2: ptr WINDOW): cint{.extdecl, importc: "getmaxx",
-                                     dynlib: pdcursesdll.}
-proc getmaxy*(a2: ptr WINDOW): cint{.extdecl, importc: "getmaxy",
-                                     dynlib: pdcursesdll.}
-proc getparx*(a2: ptr WINDOW): cint{.extdecl, importc: "getparx",
-                                     dynlib: pdcursesdll.}
-proc getpary*(a2: ptr WINDOW): cint{.extdecl, importc: "getpary",
-                                     dynlib: pdcursesdll.}
-proc getcurx*(a2: ptr WINDOW): cint{.extdecl, importc: "getcurx",
-                                     dynlib: pdcursesdll.}
-proc getcury*(a2: ptr WINDOW): cint{.extdecl, importc: "getcury",
-                                     dynlib: pdcursesdll.}
-proc traceoff*(){.extdecl, importc: "traceoff", dynlib: pdcursesdll.}
-proc traceon*(){.extdecl, importc: "traceon", dynlib: pdcursesdll.}
-proc unctrl*(a2: cunsignedlong): cstring{.extdecl, importc: "unctrl",
-    dynlib: pdcursesdll.}
-proc crmode*(): cint{.extdecl, importc: "crmode", dynlib: pdcursesdll.}
-proc nocrmode*(): cint{.extdecl, importc: "nocrmode", dynlib: pdcursesdll.}
-proc draino*(a2: cint): cint{.extdecl, importc: "draino", dynlib: pdcursesdll.}
-proc resetterm*(): cint{.extdecl, importc: "resetterm", dynlib: pdcursesdll.}
-proc fixterm*(): cint{.extdecl, importc: "fixterm", dynlib: pdcursesdll.}
-proc saveterm*(): cint{.extdecl, importc: "saveterm", dynlib: pdcursesdll.}
-proc setsyx*(a2: cint; a3: cint): cint{.extdecl, importc: "setsyx",
-                                        dynlib: pdcursesdll.}
-proc mouse_set*(a2: cunsignedlong): cint{.extdecl, importc: "mouse_set",
-    dynlib: pdcursesdll.}
-proc mouse_on*(a2: cunsignedlong): cint{.extdecl, importc: "mouse_on",
-    dynlib: pdcursesdll.}
-proc mouse_off*(a2: cunsignedlong): cint{.extdecl, importc: "mouse_off",
-    dynlib: pdcursesdll.}
-proc request_mouse_pos*(): cint{.extdecl, importc: "request_mouse_pos",
-                                 dynlib: pdcursesdll.}
-proc map_button*(a2: cunsignedlong): cint{.extdecl, importc: "map_button",
-    dynlib: pdcursesdll.}
-proc wmouse_position*(a2: ptr WINDOW; a3: ptr cint; a4: ptr cint){.extdecl,
-    importc: "wmouse_position", dynlib: pdcursesdll.}
-proc getmouse*(): cunsignedlong{.extdecl, importc: "getmouse", dynlib: pdcursesdll.}
-proc getbmap*(): cunsignedlong{.extdecl, importc: "getbmap", dynlib: pdcursesdll.}
-proc assume_default_colors*(a2: cint; a3: cint): cint{.extdecl,
-    importc: "assume_default_colors", dynlib: pdcursesdll.}
-proc curses_version*(): cstring{.extdecl, importc: "curses_version",
-                                 dynlib: pdcursesdll.}
-proc has_key*(a2: cint): cunsignedchar{.extdecl, importc: "has_key",
-                                        dynlib: pdcursesdll.}
-proc use_default_colors*(): cint{.extdecl, importc: "use_default_colors",
-                                  dynlib: pdcursesdll.}
-proc wresize*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "wresize", dynlib: pdcursesdll.}
-proc mouseinterval*(a2: cint): cint{.extdecl, importc: "mouseinterval",
-                                     dynlib: pdcursesdll.}
-proc mousemask*(a2: cunsignedlong; a3: ptr cunsignedlong): cunsignedlong{.extdecl,
-    importc: "mousemask", dynlib: pdcursesdll.}
-proc mouse_trafo*(a2: ptr cint; a3: ptr cint; a4: cunsignedchar): cunsignedchar{.
-    extdecl, importc: "mouse_trafo", dynlib: pdcursesdll.}
-proc nc_getmouse*(a2: ptr MEVENT): cint{.extdecl, importc: "nc_getmouse",
-    dynlib: pdcursesdll.}
-proc ungetmouse*(a2: ptr MEVENT): cint{.extdecl, importc: "ungetmouse",
-                                        dynlib: pdcursesdll.}
-proc wenclose*(a2: ptr WINDOW; a3: cint; a4: cint): cunsignedchar{.extdecl,
-    importc: "wenclose", dynlib: pdcursesdll.}
-proc wmouse_trafo*(a2: ptr WINDOW; a3: ptr cint; a4: ptr cint; a5: cunsignedchar): cunsignedchar{.
-    extdecl, importc: "wmouse_trafo", dynlib: pdcursesdll.}
-proc addrawch*(a2: cunsignedlong): cint{.extdecl, importc: "addrawch",
-    dynlib: pdcursesdll.}
-proc insrawch*(a2: cunsignedlong): cint{.extdecl, importc: "insrawch",
-    dynlib: pdcursesdll.}
-proc is_termresized*(): cunsignedchar{.extdecl, importc: "is_termresized",
-                                       dynlib: pdcursesdll.}
-proc mvaddrawch*(a2: cint; a3: cint; a4: cunsignedlong): cint{.extdecl,
-    importc: "mvaddrawch", dynlib: pdcursesdll.}
-proc mvdeleteln*(a2: cint; a3: cint): cint{.extdecl, importc: "mvdeleteln",
-    dynlib: pdcursesdll.}
-proc mvinsertln*(a2: cint; a3: cint): cint{.extdecl, importc: "mvinsertln",
-    dynlib: pdcursesdll.}
-proc mvinsrawch*(a2: cint; a3: cint; a4: cunsignedlong): cint{.extdecl,
-    importc: "mvinsrawch", dynlib: pdcursesdll.}
-proc mvwaddrawch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong): cint{.
-    extdecl, importc: "mvwaddrawch", dynlib: pdcursesdll.}
-proc mvwdeleteln*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "mvwdeleteln", dynlib: pdcursesdll.}
-proc mvwinsertln*(a2: ptr WINDOW; a3: cint; a4: cint): cint{.extdecl,
-    importc: "mvwinsertln", dynlib: pdcursesdll.}
-proc mvwinsrawch*(a2: ptr WINDOW; a3: cint; a4: cint; a5: cunsignedlong): cint{.
-    extdecl, importc: "mvwinsrawch", dynlib: pdcursesdll.}
-proc raw_output*(a2: cunsignedchar): cint{.extdecl, importc: "raw_output",
-    dynlib: pdcursesdll.}
-proc resize_term*(a2: cint; a3: cint): cint{.extdecl, importc: "resize_term",
-    dynlib: pdcursesdll.}
-proc resize_window*(a2: ptr WINDOW; a3: cint; a4: cint): ptr WINDOW{.extdecl,
-    importc: "resize_window", dynlib: pdcursesdll.}
-proc waddrawch*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "waddrawch", dynlib: pdcursesdll.}
-proc winsrawch*(a2: ptr WINDOW; a3: cunsignedlong): cint{.extdecl,
-    importc: "winsrawch", dynlib: pdcursesdll.}
-proc wordchar*(): char{.extdecl, importc: "wordchar", dynlib: pdcursesdll.}
-proc slk_wlabel*(a2: cint): cstring{.extdecl, importc: "slk_wlabel",
-    dynlib: pdcursesdll.}
-proc debug*(a2: cstring){.varargs, extdecl, importc: "PDC_debug",
-                          dynlib: pdcursesdll.}
-proc ungetch*(a2: cint): cint{.extdecl, importc: "PDC_ungetch",
-                               dynlib: pdcursesdll.}
-proc set_blink*(a2: cunsignedchar): cint{.extdecl, importc: "PDC_set_blink",
-    dynlib: pdcursesdll.}
-proc set_line_color*(a2: cshort): cint{.extdecl, importc: "PDC_set_line_color",
-                                        dynlib: pdcursesdll.}
-proc set_title*(a2: cstring){.extdecl, importc: "PDC_set_title",
-                              dynlib: pdcursesdll.}
-proc clearclipboard*(): cint{.extdecl, importc: "PDC_clearclipboard",
-                              dynlib: pdcursesdll.}
-proc freeclipboard*(a2: cstring): cint{.extdecl, importc: "PDC_freeclipboard",
-                                        dynlib: pdcursesdll.}
-proc getclipboard*(a2: cstringArray; a3: ptr clong): cint{.extdecl,
-    importc: "PDC_getclipboard", dynlib: pdcursesdll.}
-proc setclipboard*(a2: cstring; a3: clong): cint{.extdecl,
-    importc: "PDC_setclipboard", dynlib: pdcursesdll.}
-proc get_input_fd*(): cunsignedlong{.extdecl, importc: "PDC_get_input_fd",
-                                     dynlib: pdcursesdll.}
-proc get_key_modifiers*(): cunsignedlong{.extdecl,
-    importc: "PDC_get_key_modifiers", dynlib: pdcursesdll.}
-proc return_key_modifiers*(a2: cunsignedchar): cint{.extdecl,
-    importc: "PDC_return_key_modifiers", dynlib: pdcursesdll.}
-proc save_key_modifiers*(a2: cunsignedchar): cint{.extdecl,
-    importc: "PDC_save_key_modifiers", dynlib: pdcursesdll.}
-proc bottom_panel*(pan: ptr PANEL): cint{.extdecl, importc: "bottom_panel",
-    dynlib: pdcursesdll.}
-proc del_panel*(pan: ptr PANEL): cint{.extdecl, importc: "del_panel",
-                                       dynlib: pdcursesdll.}
-proc hide_panel*(pan: ptr PANEL): cint{.extdecl, importc: "hide_panel",
-                                        dynlib: pdcursesdll.}
-proc move_panel*(pan: ptr PANEL; starty: cint; startx: cint): cint{.extdecl,
-    importc: "move_panel", dynlib: pdcursesdll.}
-proc new_panel*(win: ptr WINDOW): ptr PANEL{.extdecl, importc: "new_panel",
-    dynlib: pdcursesdll.}
-proc panel_above*(pan: ptr PANEL): ptr PANEL{.extdecl, importc: "panel_above",
-    dynlib: pdcursesdll.}
-proc panel_below*(pan: ptr PANEL): ptr PANEL{.extdecl, importc: "panel_below",
-    dynlib: pdcursesdll.}
-proc panel_hidden*(pan: ptr PANEL): cint{.extdecl, importc: "panel_hidden",
-    dynlib: pdcursesdll.}
-proc panel_userptr*(pan: ptr PANEL): pointer{.extdecl, importc: "panel_userptr",
-    dynlib: pdcursesdll.}
-proc panel_window*(pan: ptr PANEL): ptr WINDOW{.extdecl, importc: "panel_window",
-    dynlib: pdcursesdll.}
-proc replace_panel*(pan: ptr PANEL; win: ptr WINDOW): cint{.extdecl,
-    importc: "replace_panel", dynlib: pdcursesdll.}
-proc set_panel_userptr*(pan: ptr PANEL; uptr: pointer): cint{.extdecl,
-    importc: "set_panel_userptr", dynlib: pdcursesdll.}
-proc show_panel*(pan: ptr PANEL): cint{.extdecl, importc: "show_panel",
-                                        dynlib: pdcursesdll.}
-proc top_panel*(pan: ptr PANEL): cint{.extdecl, importc: "top_panel",
-                                       dynlib: pdcursesdll.}
-proc update_panels*(){.extdecl, importc: "update_panels", dynlib: pdcursesdll.}
-
-when unixOS:
-  proc Xinitscr*(a2: cint; a3: cstringArray): ptr WINDOW{.extdecl,
-    importc: "Xinitscr", dynlib: pdcursesdll.}
-  proc XCursesExit*(){.extdecl, importc: "XCursesExit", dynlib: pdcursesdll.}
-  proc sb_init*(): cint{.extdecl, importc: "sb_init", dynlib: pdcursesdll.}
-  proc sb_set_horz*(a2: cint; a3: cint; a4: cint): cint{.extdecl,
-    importc: "sb_set_horz", dynlib: pdcursesdll.}
-  proc sb_set_vert*(a2: cint; a3: cint; a4: cint): cint{.extdecl,
-    importc: "sb_set_vert", dynlib: pdcursesdll.}
-  proc sb_get_horz*(a2: ptr cint; a3: ptr cint; a4: ptr cint): cint{.extdecl,
-    importc: "sb_get_horz", dynlib: pdcursesdll.}
-  proc sb_get_vert*(a2: ptr cint; a3: ptr cint; a4: ptr cint): cint{.extdecl,
-    importc: "sb_get_vert", dynlib: pdcursesdll.}
-  proc sb_refresh*(): cint{.extdecl, importc: "sb_refresh", dynlib: pdcursesdll.}
-
-template getch*(): expr =
-  wgetch(stdscr)
-
-template ungetch*(ch: expr): expr =
-  ungetch(ch)
-
-template getbegyx*(w, y, x: expr): expr =
-  y = getbegy(w)
-  x = getbegx(w)
-
-template getmaxyx*(w, y, x: expr): expr =
-  y = getmaxy(w)
-  x = getmaxx(w)
-
-template getparyx*(w, y, x: expr): expr =
-  y = getpary(w)
-  x = getparx(w)
-
-template getyx*(w, y, x: expr): expr =
-  y = getcury(w)
-  x = getcurx(w)
-
-template getsyx*(y, x: expr): stmt =
-  if curscr.leaveit:
-    (x) = - 1
-    (y) = (x)
-  else: getyx(curscr, (y), (x))
-
-template getmouse*(x: expr): expr =
-  nc_getmouse(x)
-
-when defined(windows):
-  var
-    atrtab*{.importc: "pdc_atrtab", dynlib: pdcursesdll.}: cstring
-    con_out*{.importc: "pdc_con_out", dynlib: pdcursesdll.}: HANDLE
-    con_in*{.importc: "pdc_con_in", dynlib: pdcursesdll.}: HANDLE
-    quick_edit*{.importc: "pdc_quick_edit", dynlib: pdcursesdll.}: DWORD
-
-  proc get_buffer_rows*(): cint{.extdecl, importc: "PDC_get_buffer_rows",
-                               dynlib: pdcursesdll.}
diff --git a/lib/wrappers/tinyc.nim b/lib/wrappers/tinyc.nim
index 47b505abc..f2ce92d36 100644
--- a/lib/wrappers/tinyc.nim
+++ b/lib/wrappers/tinyc.nim
@@ -20,16 +20,12 @@ proc openCCState*(): PccState {.importc: "tcc_new", cdecl.}
 proc closeCCState*(s: PccState) {.importc: "tcc_delete", cdecl.}
   ## free a TCC compilation context
 
-proc enableDebug*(s: PccState) {.importc: "tcc_enable_debug", cdecl.}
-  ## add debug information in the generated code
-
 proc setErrorFunc*(s: PccState, errorOpaque: pointer, errorFun: ErrorFunc) {.
   cdecl, importc: "tcc_set_error_func".}
   ## set error/warning display callback
 
-proc setWarning*(s: PccState, warningName: cstring, value: int) {.cdecl,
-  importc: "tcc_set_warning".}
-  ## set/reset a warning
+proc setOptions*(s: PccState, options: cstring) {.cdecl, importc: "tcc_set_options".}
+  ## set a options
 
 # preprocessor
 
@@ -41,7 +37,6 @@ proc addSysincludePath*(s: PccState, pathname: cstring) {.cdecl,
   importc: "tcc_add_sysinclude_path".}
   ## add in system include path
 
-
 proc defineSymbol*(s: PccState, sym, value: cstring) {.cdecl,
   importc: "tcc_define_symbol".}
   ## define preprocessor symbol 'sym'. Can put optional value
@@ -65,16 +60,12 @@ proc compileString*(s: PccState, buf: cstring): cint {.cdecl,
 
 
 const
-  OutputMemory*: cint = 0 ## output will be ran in memory (no
+  OutputMemory*: cint = 1 ## output will be ran in memory (no
                           ## output file) (default)
-  OutputExe*: cint = 1 ## executable file
-  OutputDll*: cint = 2 ## dynamic library
-  OutputObj*: cint = 3 ## object file
-  OutputPreprocess*: cint = 4 ## preprocessed file (used internally)
-
-  OutputFormatElf*: cint = 0 ## default output format: ELF
-  OutputFormatBinary*: cint = 1 ## binary image output
-  OutputFormatCoff*: cint = 2 ## COFF
+  OutputExe*: cint = 2 ## executable file
+  OutputDll*: cint = 3 ## dynamic library
+  OutputObj*: cint = 4 ## object file
+  OutputPreprocess*: cint = 5 ## preprocessed file (used internally)
 
 proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
   importc: "tcc_set_output_type".}