summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/alloc.nim217
-rw-r--r--lib/ansi_c.nim6
-rw-r--r--lib/assign.nim8
-rw-r--r--lib/base/cgi.nim119
-rw-r--r--lib/excpt.nim20
-rw-r--r--lib/gc.nim368
-rw-r--r--lib/lexbase.nim2
-rw-r--r--lib/macros.nim18
-rw-r--r--lib/math.nim35
-rw-r--r--lib/memman.nim698
-rw-r--r--lib/mm.nim187
-rw-r--r--lib/os.nim2
-rw-r--r--lib/osproc.nim92
-rw-r--r--lib/parsecsv.nim176
-rw-r--r--lib/parsexml.nim2
-rw-r--r--lib/ptrset.nim205
-rw-r--r--lib/repr.nim6
-rw-r--r--lib/strutils.nim9
-rw-r--r--lib/sysio.nim9
-rw-r--r--lib/sysstr.nim23
-rw-r--r--lib/system.nim98
-rw-r--r--lib/windows/windows.nim2
-rw-r--r--lib/winlean.nim108
-rw-r--r--lib/wz_jsgraphics.js1107
-rw-r--r--lib/xmlgen.nim406
25 files changed, 1396 insertions, 2527 deletions
diff --git a/lib/alloc.nim b/lib/alloc.nim
index 8648b322a..e4d828449 100644
--- a/lib/alloc.nim
+++ b/lib/alloc.nim
@@ -7,29 +7,27 @@
 #    distribution, for details about the copyright.
 #
 
-# Low level allocator for Nimrod.
+# Low level allocator for Nimrod. Has been designed to support the GC.
 # TODO: 
 # - eliminate "used" field
 # - make searching for block O(1)
 
-proc raiseOutOfMem {.noinline.} =
-  assert false
-  quit(1)
-
 # ------------ platform specific chunk allocation code -----------------------
 
 when defined(posix): 
-  const # XXX: make these variables for portability?
+  const
     PROT_READ  = 1             # page can be read 
     PROT_WRITE = 2             # page can be written 
     MAP_PRIVATE = 2            # Changes are private 
   
-  when defined(linux):
+  when defined(linux) or defined(aix):
     const MAP_ANONYMOUS = 0x20       # don't use a file
-  elif defined(macosx):
+  elif defined(macosx) or defined(bsd):
     const MAP_ANONYMOUS = 0x1000
+  elif defined(solaris): 
+    const MAP_ANONYMOUS = 0x100
   else:
-    const MAP_ANONYMOUS = 0 # other operating systems may not know about this
+    {.error: "Port memory manager to your platform".}
 
   proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
             off: int): pointer {.header: "<sys/mman.h>".}
@@ -43,7 +41,7 @@ when defined(posix):
       raiseOutOfMem()
       
   proc osDeallocPages(p: pointer, size: int) {.inline} =
-    munmap(p, size)
+    when reallyOsDealloc: munmap(p, size)
   
 elif defined(windows): 
   const
@@ -69,7 +67,7 @@ elif defined(windows):
 
   proc osDeallocPages(p: pointer, size: int) {.inline.} = 
     # according to Microsoft, 0 is the only correct value here:
-    VirtualFree(p, 0, MEM_RELEASE)
+    when reallyOsDealloc: VirtualFree(p, 0, MEM_RELEASE)
 
 else: 
   {.error: "Port memory manager to your platform".}
@@ -77,48 +75,14 @@ else:
 # --------------------- end of non-portable code -----------------------------
 
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
-# The page size may or may not the operating system's 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.
-
-
-# Guess the page size of the system; if it is the
-# wrong value, performance may be worse (this is not
-# for sure though), but GC still works; must be a power of two!
-when defined(linux) or defined(windows) or defined(macosx):
-  const
-    PageShift = 12
-    PageSize = 1 shl PageShift # on 32 bit systems 4096
-else:
-  {.error: "unkown 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.
 
 const
-  PageMask = PageSize-1
-  
-  SmallChunkSize = PageSize # * 4
-
-  MemAlign = 8 # minimal memory block that can be allocated
-
-  BitsPerPage = PageSize div MemAlign
-  UnitsPerPage = BitsPerPage div (sizeof(int)*8)
-    # how many ints do we need to describe a page:
-    # on 32 bit systems this is only 16 (!)
-
-  ChunkOsReturn = 64 * PageSize
+  ChunkOsReturn = 256 * PageSize
   InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
-  
-  # Compile time options:
-  coalescRight = true
-  coalescLeft = true
-
-const
-  TrunkShift = 9
-  BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
-  TrunkMask = BitsPerTrunk - 1
-  IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
-  IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
-  IntMask = 1 shl IntShift - 1
+  SmallChunkSize = PageSize
 
 type 
   PTrunk = ptr TTrunk
@@ -132,10 +96,11 @@ type
     data: TTrunkBuckets
   
 type
-  TAlignType = float
+  TAlignType = biggestFloat
   TFreeCell {.final, pure.} = object
     next: ptr TFreeCell  # next free cell in chunk (overlaid with refcount)
-    zeroField: pointer   # nil means cell is not used (overlaid with typ field)
+    zeroField: int       # 0 means cell is not used (overlaid with typ field)
+                         # 1 means cell is manually managed pointer
 
   PChunk = ptr TBaseChunk
   PBigChunk = ptr TBigChunk
@@ -155,15 +120,20 @@ type
   TBigChunk = object of TBaseChunk # not necessarily > PageSize!
     next: PBigChunk      # chunks of the same (or bigger) size
     prev: PBigChunk
+    align: int
     data: TAlignType     # start of usable memory
 
 template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
 template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
 
-proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
+proc roundup(x, v: int): int {.inline.} = 
+  result = (x + (v-1)) and not (v-1)
+  assert(result >= x)
+  #return ((-x) and (v-1)) +% x
 
 assert(roundup(14, PageSize) == PageSize)
 assert(roundup(15, 8) == 16)
+assert(roundup(65, 8) == 72)
 
 # ------------- chunk table ---------------------------------------------------
 # We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
@@ -180,7 +150,7 @@ type
     
   TAllocator {.final, pure.} = object
     llmem: PLLChunk
-    currMem, maxMem: int  # currently and maximum used memory size (allocated from OS)
+    currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
     freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
     freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
     chunkStarts: TIntSet
@@ -277,8 +247,10 @@ var lastSize = PageSize
 
 proc requestOsChunks(a: var TAllocator, size: int): PBigChunk = 
   incCurrMem(a, size)
+  inc(a.freeMem, size)
   result = cast[PBigChunk](osAllocPages(size))
   assert((cast[TAddress](result) and PageMask) == 0)
+  #zeroMem(result, size)
   result.next = nil
   result.prev = nil
   result.used = false
@@ -312,11 +284,28 @@ proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
   excl(a.chunkStarts, pageIndex(p))
   osDeallocPages(p, size)
   decCurrMem(a, size)
+  dec(a.freeMem, size)
+  #c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size)
 
 proc isAccessible(p: pointer): bool {.inline.} = 
   result = Contains(allocator.chunkStarts, pageIndex(p))
 
+proc contains[T](list, x: T): bool = 
+  var it = list
+  while it != nil:
+    if it == x: return true
+    it = it.next
+    
+proc writeFreeList(a: TAllocator) =
+  var it = a.freeChunksList
+  c_fprintf(c_stdout, "freeChunksList: %p\n", it)
+  while it != nil: 
+    c_fprintf(c_stdout, "it: %p, next: %p, prev: %p\n", 
+              it, it.next, it.prev)
+    it = it.next
+
 proc ListAdd[T](head: var T, c: T) {.inline.} = 
+  assert(c notin head)
   assert c.prev == nil
   assert c.next == nil
   c.next = head
@@ -326,6 +315,7 @@ proc ListAdd[T](head: var T, c: T) {.inline.} =
   head = c
 
 proc ListRemove[T](head: var T, c: T) {.inline.} =
+  assert(c in head)
   if c == head: 
     head = c.next
     assert c.prev == nil
@@ -344,13 +334,22 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
 proc chunkUnused(c: PChunk): bool {.inline.} = 
   result = not c.used
   
+proc updatePrevSize(a: var TAllocator, c: PBigChunk, 
+                    prevSize: int) {.inline.} = 
+  var ri = cast[PChunk](cast[TAddress](c) +% c.size)
+  assert((cast[TAddress](ri) and PageMask) == 0)
+  if isAccessible(ri):
+    ri.prevSize = prevSize
+  
 proc freeBigChunk(a: var TAllocator, c: PBigChunk) = 
   var c = c
   assert(c.size >= PageSize)
+  inc(a.freeMem, c.size)
   when coalescRight:
     var ri = cast[PChunk](cast[TAddress](c) +% c.size)
     assert((cast[TAddress](ri) and PageMask) == 0)
     if isAccessible(ri) and chunkUnused(ri):
+      assert(not isSmallChunk(ri))
       if not isSmallChunk(ri):
         ListRemove(a.freeChunksList, cast[PBigChunk](ri))
         inc(c.size, ri.size)
@@ -360,6 +359,7 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
       var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
       assert((cast[TAddress](le) and PageMask) == 0)
       if isAccessible(le) and chunkUnused(le):
+        assert(not isSmallChunk(le))
         if not isSmallChunk(le):
           ListRemove(a.freeChunksList, cast[PBigChunk](le))
           inc(le.size, c.size)
@@ -367,6 +367,8 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
           c = cast[PBigChunk](le)
 
   if c.size < ChunkOsReturn: 
+    incl(a.chunkStarts, pageIndex(c))
+    updatePrevSize(a, c, c.size)
     ListAdd(a.freeChunksList, c)
     c.used = false
   else:
@@ -374,11 +376,16 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
 
 proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) = 
   var rest = cast[PBigChunk](cast[TAddress](c) +% size)
+  if rest in a.freeChunksList: 
+    c_fprintf(c_stdout, "to add: %p\n", rest)
+    writeFreeList(allocator)
+    assert false
   rest.size = c.size - size
   rest.used = false
-  rest.next = nil # XXX
+  rest.next = nil
   rest.prev = nil
   rest.prevSize = size
+  updatePrevSize(a, c, rest.size)
   c.size = size
   incl(a.chunkStarts, pageIndex(rest))
   ListAdd(a.freeChunksList, rest)
@@ -386,26 +393,32 @@ proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
 proc getBigChunk(a: var TAllocator, size: int): PBigChunk = 
   # use first fit for now:
   assert((size and PageMask) == 0)
+  assert(size > 0)
   result = a.freeChunksList
   block search:
     while result != nil:
+      #if not chunkUnused(result): 
+      #  c_fprintf(c_stdout, "%lld\n", int(result.used))
       assert chunkUnused(result)
       if result.size == size: 
         ListRemove(a.freeChunksList, result)
         break search
       elif result.size > size:
-        splitChunk(a, result, size)
+        #c_fprintf(c_stdout, "res size: %lld; size: %lld\n", result.size, size)
         ListRemove(a.freeChunksList, result)
+        splitChunk(a, result, size)
         break search
       result = result.next
+      assert result != a.freeChunksList
     if size < InitialMemoryRequest: 
       result = requestOsChunks(a, InitialMemoryRequest)
       splitChunk(a, result, size)
     else:
       result = requestOsChunks(a, size)
-  result.prevSize = 0
+  result.prevSize = 0 # XXX why is this needed?
   result.used = true
   incl(a.chunkStarts, pageIndex(result))
+  dec(a.freeMem, size)
 
 proc getSmallChunk(a: var TAllocator): PSmallChunk = 
   var res = getBigChunk(a, PageSize)
@@ -419,8 +432,11 @@ proc getCellSize(p: pointer): int {.inline.} =
   var c = pageAddr(p)
   result = c.size
   
-proc alloc(a: var TAllocator, requestedSize: int): pointer =
-  var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
+proc rawAlloc(a: var TAllocator, requestedSize: int): pointer =
+  assert(roundup(65, 8) == 72)
+  assert requestedSize >= sizeof(TFreeCell)
+  var size = roundup(requestedSize, MemAlign)
+  #c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
   if size <= SmallChunkSize-smallChunkOverhead(): 
     # allocate a small block: for small chunks, we use only its next pointer
     var s = size div MemAlign
@@ -436,8 +452,11 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
       c.prev = nil
       ListAdd(a.freeSmallChunks[s], c)
       result = addr(c.data)
+      assert((cast[TAddress](result) and (MemAlign-1)) == 0)
     else:
       assert c.next != c
+      #if c.size != size:
+      #  c_fprintf(c_stdout, "csize: %lld; size %lld\n", c.size, size)
       assert c.size == size
       if c.freeList == nil:
         assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize) 
@@ -445,9 +464,10 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
         inc(c.acc, size)      
       else:
         result = c.freeList
-        assert(c.freeList.zeroField == nil)
+        assert(c.freeList.zeroField == 0)
         c.freeList = c.freeList.next
       dec(c.free, size)
+      assert((cast[TAddress](result) and (MemAlign-1)) == 0)
     if c.free < size: 
       ListRemove(a.freeSmallChunks[s], c)
   else:
@@ -458,16 +478,10 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
     assert c.next == nil
     assert c.size == size
     result = addr(c.data)
-  cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
-  #echo("setting to one: ", $cast[TAddress](addr(cast[ptr TFreeCell](result).zeroField)))
-
-proc contains(list, x: PSmallChunk): bool = 
-  var it = list
-  while it != nil:
-    if it == x: return true
-    it = it.next
+    assert((cast[TAddress](result) and (MemAlign-1)) == 0)
+  assert(isAccessible(result))
 
-proc dealloc(a: var TAllocator, p: pointer) = 
+proc rawDealloc(a: var TAllocator, p: pointer) = 
   var c = pageAddr(p)
   if isSmallChunk(c):
     # `p` is within a small chunk:
@@ -475,8 +489,8 @@ proc dealloc(a: var TAllocator, p: pointer) =
     var s = c.size
     var f = cast[ptr TFreeCell](p)
     #echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
-    assert(f.zeroField != nil)
-    f.zeroField = nil
+    assert(f.zeroField != 0)
+    f.zeroField = 0
     f.next = c.freeList
     c.freeList = f
     # check if it is not in the freeSmallChunks[s] list:
@@ -495,23 +509,64 @@ proc dealloc(a: var TAllocator, p: pointer) =
     # free big chunk
     freeBigChunk(a, cast[PBigChunk](c))
 
-proc realloc(a: var TAllocator, p: pointer, size: int): pointer = 
-  # could be made faster, but this is unnecessary, the GC does not use it anyway
-  result = alloc(a, size)
-  copyMem(result, p, getCellSize(p))
-  dealloc(a, p)
-
 proc isAllocatedPtr(a: TAllocator, p: pointer): bool = 
   if isAccessible(p):
     var c = pageAddr(p)
     if not chunkUnused(c):
       if isSmallChunk(c):
-        result = (cast[TAddress](p) -% cast[TAddress](c) -%
-                 smallChunkOverhead()) %% c.size == 0 and
-          cast[ptr TFreeCell](p).zeroField != nil
+        var c = cast[PSmallChunk](c)
+        var offset = (cast[TAddress](p) and (PageSize-1)) -% 
+                     smallChunkOverhead()
+        result = (c.acc >% offset) and (offset %% c.size == 0) and
+          (cast[ptr TFreeCell](p).zeroField >% 1)
       else:
         var c = cast[PBigChunk](c)
-        result = p == addr(c.data)
+        result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1
+
+# ---------------------- interface to programs -------------------------------
+
+proc alloc(size: int): pointer =
+  result = rawAlloc(allocator, size+sizeof(TFreeCell))
+  cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
+  assert(not isAllocatedPtr(allocator, result))
+  result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
+
+proc alloc0(size: int): pointer =
+  result = alloc(size)
+  zeroMem(result, size)
+
+proc dealloc(p: pointer) =
+  var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
+  assert(cast[ptr TFreeCell](x).zeroField == 1)
+  rawDealloc(allocator, x)
+  assert(not isAllocatedPtr(allocator, x))
+
+proc ptrSize(p: pointer): int =
+  var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
+  result = pageAddr(x).size - sizeof(TFreeCell)
+
+proc realloc(p: pointer, newsize: int): pointer =
+  if newsize > 0:
+    result = alloc(newsize)
+    if p != nil:
+      copyMem(result, p, ptrSize(p))
+      dealloc(p)
+  elif p != nil:
+    dealloc(p)
+
+proc countFreeMem(): int =
+  # only used for assertions
+  var it = allocator.freeChunksList
+  while it != nil:
+    inc(result, it.size)
+    it = it.next
+
+proc getFreeMem(): int = 
+  result = allocator.freeMem
+  #assert(result == countFreeMem())
+
+proc getTotalMem(): int = return allocator.currMem
+proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
 
 when isMainModule:
   const iterations = 4000_000
diff --git a/lib/ansi_c.nim b/lib/ansi_c.nim
index 8e39c9e39..81abd2a3e 100644
--- a/lib/ansi_c.nim
+++ b/lib/ansi_c.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2006 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -82,3 +82,7 @@ proc c_ferror(stream: C_TextFileStar): bool {.importc: "ferror", nodecl.}
 proc c_fflush(stream: C_TextFileStar) {.importc: "fflush", nodecl.}
 proc c_abort() {.importc: "abort", nodecl.}
 proc c_feof(stream: C_TextFileStar): bool {.importc: "feof", nodecl.}
+
+proc c_malloc(size: int): pointer {.importc: "malloc", nodecl.}
+proc c_free(p: pointer) {.importc: "free", nodecl.}
+proc c_realloc(p: pointer, newsize: int): pointer {.importc: "realloc", nodecl.}
diff --git a/lib/assign.nim b/lib/assign.nim
index 17b4f5949..3d4bf4d61 100644
--- a/lib/assign.nim
+++ b/lib/assign.nim
@@ -43,12 +43,8 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
       x^ = nil
       return
     assert(dest != nil)
-    when defined(boehmGC):
-      unsureAsgnRef(cast[ppointer](dest),
-                    newObj(seq.len * mt.base.size + GenericSeqSize))
-    else:
-      unsureAsgnRef(cast[ppointer](dest),
-                    newObj(mt, seq.len * mt.base.size + GenericSeqSize))
+    unsureAsgnRef(cast[ppointer](dest),
+                  newObj(mt, seq.len * mt.base.size + GenericSeqSize))
     var dst = cast[taddress](cast[ppointer](dest)^)
     for i in 0..seq.len-1:
       genericAssign(
diff --git a/lib/base/cgi.nim b/lib/base/cgi.nim
index 0dc3a4394..5cf6d0bfa 100644
--- a/lib/base/cgi.nim
+++ b/lib/base/cgi.nim
@@ -103,71 +103,77 @@ proc cgiError*(msg: string) {.noreturn.} =
   e.msg = msg
   raise e
 
-proc readData*(allowedMethods: set[TRequestMethod] = 
-               {methodNone, methodPost, methodGet}): PStringTable = 
-  ## Read CGI data. If the client does not use a method listed in the
-  ## `allowedMethods` set, an `ECgi` exception is raised.
-  result = newStringTable()
-  var enc: string # encoded data
+proc getEncodedData(allowedMethods: set[TRequestMethod]): string = 
   case getenv("REQUEST_METHOD") 
   of "POST": 
     if methodPost notin allowedMethods: 
       cgiError("'REQUEST_METHOD' 'POST' is not supported")
-    # read from stdin:
     var L = parseInt(getenv("CONTENT_LENGTH"))
-    enc = newString(L)
-    if readBuffer(stdin, addr(enc[0]), L) != L:
+    result = newString(L)
+    if readBuffer(stdin, addr(result[0]), L) != L:
       cgiError("cannot read from stdin")
   of "GET":
     if methodGet notin allowedMethods: 
       cgiError("'REQUEST_METHOD' 'GET' is not supported")
-    # read from the QUERY_STRING environment variable:
-    enc = getenv("QUERY_STRING")
+    result = getenv("QUERY_STRING")
   else: 
-    if methodNone in allowedMethods:
-      return result
-    else:
+    if methodNone notin allowedMethods:
       cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
-  
-  # decode everything in one pass:
-  var i = 0
-  var name = ""
-  var value = ""
-  while true:
-    setLen(name, 0) # reuse memory
-    while true:
-      case enc[i]
-      of '\0': return
-      of '%': 
-        var x = 0
-        handleHexChar(enc[i+1], x)
-        handleHexChar(enc[i+2], x)
-        inc(i, 2)
-        add(name, chr(x))
-      of '+': add(name, ' ')
-      of '=', '&': break
-      else: add(name, enc[i])
-      inc(i)
-    if enc[i] != '=': cgiError("'=' expected")
-    inc(i) # skip '='
-    setLen(value, 0) # reuse memory
-    while true:
-      case enc[i]
-      of '%': 
-        var x = 0
-        handleHexChar(enc[i+1], x)
-        handleHexChar(enc[i+2], x)
-        inc(i, 2)
-        add(value, chr(x))
-      of '+': add(value, ' ')
-      of '&', '\0': break
-      else: add(value, enc[i])
-      inc(i)
-    result[name] = value
-    if enc[i] == '&': inc(i)
-    elif enc[i] == '\0': break
-    else: cgiError("'&' expected")
 
+iterator decodeData*(allowedMethods: set[TRequestMethod] = 
+       {methodNone, methodPost, methodGet}): tuple[key, value: string] = 
+  ## Reads and decodes CGI data and yields the (name, value) pairs the
+  ## data consists of. If the client does not use a method listed in the
+  ## `allowedMethods` set, an `ECgi` exception is raised.
+  var enc = getEncodedData(allowedMethods)
+  if not isNil(enc): 
+    # decode everything in one pass:
+    var i = 0
+    var name = ""
+    var value = ""
+    while enc[i] != '\0':
+      setLen(name, 0) # reuse memory
+      while true:
+        case enc[i]
+        of '\0': break
+        of '%': 
+          var x = 0
+          handleHexChar(enc[i+1], x)
+          handleHexChar(enc[i+2], x)
+          inc(i, 2)
+          add(name, chr(x))
+        of '+': add(name, ' ')
+        of '=', '&': break
+        else: add(name, enc[i])
+        inc(i)
+      if enc[i] != '=': cgiError("'=' expected")
+      inc(i) # skip '='
+      setLen(value, 0) # reuse memory
+      while true:
+        case enc[i]
+        of '%': 
+          var x = 0
+          handleHexChar(enc[i+1], x)
+          handleHexChar(enc[i+2], x)
+          inc(i, 2)
+          add(value, chr(x))
+        of '+': add(value, ' ')
+        of '&', '\0': break
+        else: add(value, enc[i])
+        inc(i)
+      yield (name, value)
+      if enc[i] == '&': inc(i)
+      elif enc[i] == '\0': break
+      else: cgiError("'&' expected")
+
+proc readData*(allowedMethods: set[TRequestMethod] = 
+               {methodNone, methodPost, methodGet}): PStringTable = 
+  ## Read CGI data. If the client does not use a method listed in the
+  ## `allowedMethods` set, an `ECgi` exception is raised.
+  result = newStringTable()
+  for name, value in decodeData(allowedMethods): 
+    result[name] = value
+  
 proc validateData*(data: PStringTable, validKeys: openarray[string]) = 
   ## validates data; raises `ECgi` if this fails. This checks that each variable
   ## name of the CGI `data` occurs in the `validKeys` array.
@@ -323,8 +329,13 @@ proc setTestData*(keysvalues: openarray[string]) =
 
 proc writeContentType*() = 
   ## call this before starting to send your HTML data to `stdout`. This
-  ## is just a shorthand for: 
+  ## implements this part of the CGI protocol: 
   ##
   ## .. code-block:: Nimrod
   ##     write(stdout, "Content-type: text/html\n\n")
+  ## 
+  ## It also modifies the debug stack traces so that they contain
+  ## ``<br />`` and are easily readable in a browser.  
   write(stdout, "Content-type: text/html\n\n")
+  system.stackTraceNewLine = "<br />\n"
+  
diff --git a/lib/excpt.nim b/lib/excpt.nim
index 38d34f3f4..293491fe9 100644
--- a/lib/excpt.nim
+++ b/lib/excpt.nim
@@ -70,6 +70,8 @@ var
   framePtr {.exportc.}: PFrame
 
   tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
+  
+  stackTraceNewLine* = "\n" ## undocumented feature
 
 proc auxWriteStackTrace(f: PFrame, s: var string) =
   const 
@@ -101,19 +103,21 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
     if tempFrames[j] == nil: 
       add(s, "(")
       add(s, $(total-i-1))
-      add(s, " calls omitted) ...\n")
+      add(s, " calls omitted) ...")
     else:
       add(s, $tempFrames[j].procname)
       if tempFrames[j].line > 0:
         add(s, ", line: ")
         add(s, $tempFrames[j].line)
-      add(s, "\n")
+    add(s, stackTraceNewLine)
 
 proc rawWriteStackTrace(s: var string) =
   if framePtr == nil:
-    add(s, "No stack traceback available\n")
+    add(s, "No stack traceback available")
+    add(s, stackTraceNewLine)
   else:
-    add(s, "Traceback (most recent call last)\n")
+    add(s, "Traceback (most recent call last)")
+    add(s, stackTraceNewLine)
     auxWriteStackTrace(framePtr, s)
 
 proc quitOrDebug() {.inline.} =
@@ -151,6 +155,8 @@ var
 
 proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
   if not cond:
+    #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
+    #quit(1)
     GC_disable() # BUGFIX: `$` allocates a new string object!
     if not isNil(assertBuf):
       # BUGFIX: when debugging the GC, assertBuf may be nil
@@ -162,7 +168,11 @@ proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
       add(assertBuf, "\n")
       gAssertionFailed.msg = assertBuf
     GC_enable()
-    raise gAssertionFailed # newException(EAssertionFailed, assertBuf)
+    if gAssertionFailed != nil:
+      raise gAssertionFailed
+    else:
+      c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
+      quit(1)
 
 proc WriteStackTrace() =
   var s = ""
diff --git a/lib/gc.nim b/lib/gc.nim
index aaef70c03..1c353b559 100644
--- a/lib/gc.nim
+++ b/lib/gc.nim
@@ -9,23 +9,17 @@
 
 
 #            Garbage Collector
-# Current Features:
-# * incremental
-# * non-recursive
-# * generational
-
+#
+# The basic algorithm is *Deferrent Reference Counting* with cycle detection.
+# Special care has been taken to avoid recursion as far as possible to avoid
+# stack overflows when traversing deep datastructures. This is comparable to
+# an incremental and generational GC. It should be well-suited for soft real
+# time applications (like games).
+#
 # Future Improvements:
 # * Support for multi-threading. However, locks for the reference counting
 #   might turn out to be too slow.
 
-# ---------------------------------------------------------------------------
-
-proc getOccupiedMem(): int = return tlsfUsed()
-proc getFreeMem(): int = return tlsfMax() - tlsfUsed()
-proc getTotalMem(): int = return tlsfMax()
-
-# ---------------------------------------------------------------------------
-
 const
   CycleIncrease = 2 # is a multiplicative increase
   InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
@@ -36,14 +30,14 @@ const
 const
   rcIncrement = 0b1000 # so that lowest 3 bits are not touched
   # NOTE: Most colors are currently unused
-  rcBlack = 0b000 # cell is colored black; in use or free
-  rcGray = 0b001  # possible member of a cycle
-  rcWhite = 0b010 # member of a garbage cycle
+  rcBlack = 0b000  # cell is colored black; in use or free
+  rcGray = 0b001   # possible member of a cycle
+  rcWhite = 0b010  # member of a garbage cycle
   rcPurple = 0b011 # possible root of a cycle
-  rcZct = 0b100  # in ZCT
-  rcRed = 0b101 # Candidate cycle undergoing sigma-computation
+  rcZct = 0b100    # in ZCT
+  rcRed = 0b101    # Candidate cycle undergoing sigma-computation
   rcOrange = 0b110 # Candidate cycle awaiting epoch boundary
-  rcShift = 3 # shift by rcShift to get the reference counter
+  rcShift = 3      # shift by rcShift to get the reference counter
   colorMask = 0b111
 type
   TWalkOp = enum
@@ -52,21 +46,22 @@ type
   TFinalizer {.compilerproc.} = proc (self: pointer)
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
-  TGcHeap {.final, pure.} = object # this contains the zero count and
-                                   # non-zero count table
-    mask: TAddress           # mask for fast pointer detection
-    zct: TCellSeq            # the zero count table
-    stackCells: TCellSet     # cells and addresses that look like a cell but
-                             # aren't of the hardware stack
 
+  TGcStat {.final, pure.} = object
     stackScans: int          # number of performed stack scans (for statistics)
     cycleCollections: int    # number of performed full collections
     maxThreshold: int        # max threshold that has been set
     maxStackSize: int        # max stack size
-    maxStackPages: int       # max number of pages in stack
-    cycleTableSize: int      # max entries in cycle table
+    maxStackCells: int       # max stack cells in ``decStack``
+    cycleTableSize: int      # max entries in cycle table  
+  
+  TGcHeap {.final, pure.} = object # this contains the zero count and
+                                   # non-zero count table
+    zct: TCellSeq            # the zero count table
+    decStack: TCellSeq       # cells in the stack that are to decref again
     cycleRoots: TCellSet
     tempStack: TCellSeq      # temporary stack for recursion elimination
+    stat: TGcStat
 
 var
   stackBottom: pointer
@@ -82,13 +77,13 @@ var
 proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
   # unsureAsgnRef updates the reference counters only if dest is not on the
   # stack. It is used by the code generator if it cannot decide wether a
-  # reference is in the stack or not (this can happen for out/var parameters).
+  # reference is in the stack or not (this can happen for var parameters).
 #proc growObj(old: pointer, newsize: int): pointer {.compilerproc.}
 proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
 
 proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
-  if (c.refcount and colorMask) != rcZct:
+  if (c.refcount and rcZct) == 0:
     c.refcount = c.refcount and not colorMask or rcZct
     add(s, c)
 
@@ -131,7 +126,7 @@ proc GC_disableMarkAndSweep() =
   # set to the max value to suppress the cycle detector
 
 # this that has to equals zero, otherwise we have to round up UnitsPerPage:
-when BitsPerPage mod BitsPerUnit != 0:
+when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
 when debugGC:
@@ -203,8 +198,7 @@ template gcTrace(cell, state: expr): stmt =
 # -----------------------------------------------------------------------------
 
 # forward declarations:
-proc updateZCT()
-proc collectCT(gch: var TGcHeap, zctUpdated: bool)
+proc collectCT(gch: var TGcHeap)
 proc IsOnStack(p: pointer): bool {.noinline.}
 proc forAllChildren(cell: PCell, op: TWalkOp)
 proc doOperation(p: pointer, op: TWalkOp)
@@ -232,7 +226,7 @@ proc decRef(c: PCell) {.inline.} =
   when stressGC:
     if c.refcount <% rcIncrement:
       writeCell("broken cell", c)
-  assert(c.refcount >% rcIncrement)
+  assert(c.refcount >=% rcIncrement)
   c.refcount = c.refcount -% rcIncrement
   if c.refcount <% rcIncrement:
     addZCT(gch.zct, c)
@@ -242,7 +236,6 @@ proc decRef(c: PCell) {.inline.} =
 proc incRef(c: PCell) {.inline.} = 
   c.refcount = c.refcount +% rcIncrement
   if canBeCycleRoot(c):
-    # OPT: the code generator should special case this
     incl(gch.cycleRoots, c)
 
 proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
@@ -277,19 +270,18 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) =
 
 proc initGC() =
   when traceGC:
-    for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
-  gch.stackScans = 0
-  gch.cycleCollections = 0
-  gch.maxThreshold = 0
-  gch.maxStackSize = 0
-  gch.maxStackPages = 0
-  gch.cycleTableSize = 0
+    for i in low(TCellState)..high(TCellState): Init(states[i])
+  gch.stat.stackScans = 0
+  gch.stat.cycleCollections = 0
+  gch.stat.maxThreshold = 0
+  gch.stat.maxStackSize = 0
+  gch.stat.maxStackCells = 0
+  gch.stat.cycleTableSize = 0
   # init the rt
   init(gch.zct)
   init(gch.tempStack)
-  CellSetInit(gch.cycleRoots)
-  CellSetInit(gch.stackCells)
-  gch.mask = 0
+  Init(gch.cycleRoots)
+  Init(gch.decStack)
   new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
@@ -333,54 +325,63 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
   of tyString: nil
   else: assert(false)
 
-proc checkCollection(zctUpdated: bool) {.inline.} =
+proc checkCollection {.inline.} =
   # checks if a collection should be done
   if recGcLock == 0:
-    collectCT(gch, zctUpdated)
+    collectCT(gch)
 
 proc newObj(typ: PNimType, size: int): pointer =
   # generates a new object and sets its reference counter to 0
   assert(typ.kind in {tyRef, tyString, tySequence})
-  var zctUpdated = false
-  if gch.zct.len >= ZctThreshold:
-    updateZCT()
-    zctUpdated = true
-  # check if we have to collect:
-  checkCollection(zctUpdated)
-  var res = cast[PCell](gcAlloc(size + sizeof(TCell)))
-  when stressGC: assert((cast[TAddress](res) and (MemAlignment-1)) == 0)
+  checkCollection()
+  var res = cast[PCell](rawAlloc(allocator, size + sizeof(TCell)))
+  zeroMem(res, size+sizeof(TCell))
+  assert((cast[TAddress](res) and (MemAlign-1)) == 0)
   # now it is buffered in the ZCT
   res.typ = typ
   when debugGC:
     if framePtr != nil and framePtr.prev != nil:
       res.filename = framePtr.prev.filename
       res.line = framePtr.prev.line
-  res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
-  add(gch.zct, res) # its refcount is zero, so add it to the ZCT
-  gch.mask = gch.mask or cast[TAddress](res)
+  res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT  
+  assert(isAllocatedPtr(allocator, res))
+  # its refcount is zero, so add it to the ZCT:
+  block addToZCT: 
+    # we check the last 8 entries (cache line) for a slot
+    # that could be reused
+    var L = gch.zct.len
+    var d = gch.zct.d
+    for i in countdown(L-1, max(0, L-8)):
+      var c = d[i]
+      if c.refcount >=% rcIncrement:
+        c.refcount = c.refcount and not colorMask
+        d[i] = res
+        break addToZCT
+    add(gch.zct, res)
   when logGC: writeCell("new cell", res)
   gcTrace(res, csAllocated)
   result = cellToUsr(res)
 
 proc newSeq(typ: PNimType, len: int): pointer =
-  # XXX: overflow checks!
-  result = newObj(typ, len * typ.base.size + GenericSeqSize)
+  result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).space = len
 
 proc growObj(old: pointer, newsize: int): pointer =
-  checkCollection(false)
+  checkCollection()
   var ol = usrToCell(old)
   assert(ol.typ != nil)
   assert(ol.typ.kind in {tyString, tySequence})
-  var res = cast[PCell](gcAlloc(newsize + sizeof(TCell)))
+  var res = cast[PCell](rawAlloc(allocator, newsize + sizeof(TCell)))
   var elemSize = 1
   if ol.typ.kind != tyString:
     elemSize = ol.typ.base.size
-  copyMem(res, ol, cast[PGenericSeq](old).len*elemSize +
-          GenericSeqSize + sizeof(TCell))
-
-  assert((cast[TAddress](res) and (MemAlignment-1)) == 0)
+  
+  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
+  copyMem(res, ol, oldsize + sizeof(TCell))
+  zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)),
+          newsize-oldsize)
+  assert((cast[TAddress](res) and (MemAlign-1)) == 0)
   assert(res.refcount shr rcShift <=% 1)
   #if res.refcount <% rcIncrement:
   #  add(gch.zct, res)
@@ -395,13 +396,12 @@ proc growObj(old: pointer, newsize: int): pointer =
         break
       dec(j)
   if canBeCycleRoot(ol): excl(gch.cycleRoots, ol)
-  gch.mask = gch.mask or cast[TAddress](res)
   when logGC:
     writeCell("growObj old cell", ol)
     writeCell("growObj new cell", res)
   gcTrace(ol, csZctFreed)
   gcTrace(res, csAllocated)
-  when reallyDealloc: tlsf_free(ol)
+  when reallyDealloc: rawDealloc(allocator, ol)
   else:
     assert(ol.typ != nil)
     zeroMem(ol, sizeof(TCell))
@@ -409,13 +409,6 @@ proc growObj(old: pointer, newsize: int): pointer =
 
 # ---------------- cycle collector -------------------------------------------
 
-# When collecting cycles, we have to consider the following:
-# * there may still be references in the stack
-# * some cells may still be in the ZCT, because they are referenced from
-#   the stack (!), so their refcounts are zero
-# the ZCT is a subset of stackCells here, so we only need to care
-# for stackcells
-
 proc doOperation(p: pointer, op: TWalkOp) =
   if p == nil: return
   var c: PCell = usrToCell(p)
@@ -438,36 +431,25 @@ proc collectCycles(gch: var TGcHeap) =
   for c in elements(gch.cycleRoots):
     inc(tabSize)
     forallChildren(c, waCycleDecRef)
-  gch.cycleTableSize = max(gch.cycleTableSize, tabSize)
+  gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
 
   # restore reference counts (a depth-first traversal is needed):
-  var marker, newRoots: TCellSet
-  CellSetInit(marker)
-  CellSetInit(newRoots)
+  var marker: TCellSet
+  Init(marker)
   for c in elements(gch.cycleRoots):
-    var needsRestore = false
-    if c in gch.stackCells:
-      needsRestore = true
-      incl(newRoots, c)
-      # we need to scan this later again; maybe stack changes
-      # NOTE: adding to ZCT here does NOT work
-    elif c.refcount >=% rcIncrement:
-      needsRestore = true
-    if needsRestore:
-      if c notin marker:
-        incl(marker, c)
+    if c.refcount >=% rcIncrement:
+      if not containsOrIncl(marker, c):
         gch.tempStack.len = 0
         forAllChildren(c, waPush)
         while gch.tempStack.len > 0:
           dec(gch.tempStack.len)
           var d = gch.tempStack.d[gch.tempStack.len]
           d.refcount = d.refcount +% rcIncrement
-          if d notin marker and d in gch.cycleRoots:
-            incl(marker, d)
+          if d in gch.cycleRoots and not containsOrIncl(marker, d):
             forAllChildren(d, waPush)
   # remove cycles:
   for c in elements(gch.cycleRoots):
-    if c.refcount <% rcIncrement and c notin gch.stackCells:
+    if c.refcount <% rcIncrement:
       gch.tempStack.len = 0
       forAllChildren(c, waPush)
       while gch.tempStack.len > 0:
@@ -480,21 +462,23 @@ proc collectCycles(gch: var TGcHeap) =
       prepareDealloc(c)
       gcTrace(c, csCycFreed)
       when logGC: writeCell("cycle collector dealloc cell", c)
-      when reallyDealloc: tlsf_free(c)
+      when reallyDealloc: rawDealloc(allocator, c)
       else:
         assert(c.typ != nil)
         zeroMem(c, sizeof(TCell))
-  CellSetDeinit(gch.cycleRoots)
-  gch.cycleRoots = newRoots
+  Deinit(gch.cycleRoots)
+  Init(gch.cycleRoots)
 
-proc gcMark(p: pointer) {.noinline.} =
+proc gcMark(p: pointer) {.inline.} =
   # the addresses are not as objects on the stack, so turn them to objects:
   var cell = usrToCell(p)
   var c = cast[TAddress](cell)
-  if ((c and gch.mask) == c) and c >% 1024:
+  if c >% PageSize:
     # fast check: does it look like a cell?
-    when logGC: cfprintf(cstdout, "in stackcells %p\n", cell)
-    incl(gch.stackCells, cell)  # yes: mark it
+    if isAllocatedPtr(allocator, cell): 
+      # mark the cell:
+      cell.refcount = cell.refcount +% rcIncrement
+      add(gch.decStack, cell)
 
 # ----------------- stack management --------------------------------------
 #  inspired from Smart Eiffel
@@ -560,53 +544,6 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
         gcMark(sp^)
         sp = cast[ppointer](cast[TAddress](sp) -% sizeof(pointer))
 
-elif defined(I386) and asmVersion:
-  # addresses decrease as the stack grows:
-  proc isOnStack(p: pointer): bool =
-    var
-      stackTop: array [0..1, pointer]
-    result = p >= addr(stackTop[0]) and p <= stackBottom
-
-  proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
-    # This code should be safe even for aggressive optimizers. The try
-    # statement safes all registers into the safepoint, which we
-    # scan additionally to the stack.
-    type
-      TPtrArray = array[0..0xffffff, pointer]
-    try:
-      var pa = cast[ptr TPtrArray](excHandler)
-      for i in 0 .. sizeof(TSafePoint) - 1:
-        gcMark(pa[i])
-    finally:
-      # iterate over the stack:
-      var max = cast[TAddress](stackBottom)
-      var stackTop{.volatile.}: array [0..15, pointer]
-      var sp {.volatile.} = cast[TAddress](addr(stackTop[0]))
-      while sp <= max:
-        gcMark(cast[ppointer](sp)^)
-        sp = sp +% sizeof(pointer)
-    when false:
-      var counter = 0
-      #mov ebx, OFFSET `stackBottom`
-      #mov ebx, [ebx]
-      asm """
-        pusha
-        mov edi, esp
-        call `getStackBottom`
-        mov ebx, eax
-      L1:
-        cmp edi, ebx
-        ja L2
-        mov eax, [edi]
-        call `gcMark`
-        add edi, 4
-        inc [`counter`]
-        jmp L1
-      L2:
-        popa
-      """
-      cfprintf(cstdout, "stack %ld\n", counter)
-
 else:
   # ---------------------------------------------------------------------------
   # Generic code for architectures where addresses decrease as the stack grows.
@@ -617,82 +554,47 @@ else:
     result = p >= addr(stackTop[0]) and p <= stackBottom
 
   var
-    gRegisters: C_JmpBuf
     jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
       # a little hack to get the size of a TJmpBuf in the generated C code
       # in a platform independant way
 
   proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
-    when false:
-      # new version: several C compilers are too smart here
-      var
-        max = cast[TAddress](stackBottom)
-        stackTop: array [0..15, pointer]
-      if c_setjmp(gregisters) == 0'i32: # To fill the C stack with registers.
-        # iterate over the registers:
-        var sp = cast[TAddress](addr(gregisters))
-        while sp < cast[TAddress](addr(gregisters))+%jmpbufSize:
-          gcMark(cast[ppointer](sp)^)
-          sp = sp +% sizeof(pointer)
-        # iterate over the stack:
-        sp = cast[TAddress](addr(stackTop[0]))
-        while sp <= max:
-          gcMark(cast[ppointer](sp)^)
-          sp = sp +% sizeof(pointer)
-      else:
-        c_longjmp(gregisters, 42)
-        # this can never happen, but should trick any compiler that is
-        # not as smart as a human
-    else:
-      var
-        max = stackBottom
-        registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
-        sp: PPointer        # Used to traverse the stack and registers assuming
-                            # that 'setjmp' will save registers in the C stack.
-      if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
-        sp = cast[ppointer](addr(registers))
-        while sp <= max:
-          gcMark(sp^)
-          sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
+    var
+      max = stackBottom
+      registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
+      sp: PPointer        # Used to traverse the stack and registers assuming
+                          # that 'setjmp' will save registers in the C stack.
+    if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
+      sp = cast[ppointer](addr(registers))
+      while sp <= max:
+        gcMark(sp^)
+        sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
 
 # ----------------------------------------------------------------------------
 # end of non-portable code
 # ----------------------------------------------------------------------------
 
-proc updateZCT() =
-  # We have to make an additional pass over the ZCT unfortunately, because 
-  # the ZCT may be out of date, which means it contains cells with a
-  # refcount > 0. The reason is that ``incRef`` does not bother to remove
-  # the cell from the ZCT as this might be too slow.
-  var j = 0
-  var L = gch.zct.len # because globals make it hard for the optimizer
-  var d = gch.zct.d
-  while j < L:
-    var c = d[j]
-    if c.refcount >=% rcIncrement:
-      when logGC: writeCell("remove from ZCT", c)
-      # remove from ZCT:
-      dec(L)
-      d[j] = d[L]
-      c.refcount = c.refcount and not colorMask
-      # we have a new cell at position j, so don't increment j
-    else:
-      inc(j)
-  gch.zct.len = L
-
 proc CollectZCT(gch: var TGcHeap) =
-  var i = 0
-  while i < gch.zct.len:
-    var c = gch.zct.d[i]
-    assert(c.refcount <% rcIncrement)
+  # Note: Freeing may add child objects to the ZCT! So essentially we do 
+  # deep freeing, which is bad for incremental operation. In order to 
+  # avoid a deep stack, we move objects to keep the ZCT small.
+  # This is performance critical!
+  var L = addr(gch.zct.len)
+  while L^ > 0:
+    var c = gch.zct.d[0]
+    # remove from ZCT:
     assert((c.refcount and colorMask) == rcZct)
-    if canBeCycleRoot(c): excl(gch.cycleRoots, c)
-    if c notin gch.stackCells:
-      # remove from ZCT:
-      c.refcount = c.refcount and not colorMask
-      gch.zct.d[i] = gch.zct.d[gch.zct.len-1]
-      # we have a new cell at position i, so don't increment i
-      dec(gch.zct.len)
+    c.refcount = c.refcount and not colorMask
+    gch.zct.d[0] = gch.zct.d[L^ - 1]
+    dec(L^)
+    if c.refcount <% rcIncrement: 
+      # It may have a RC > 0, if it is in the hardware stack or
+      # it has not been removed yet from the ZCT. This is because
+      # ``incref`` does not bother to remove the cell from the ZCT 
+      # as this might be too slow.
+      # In any case,  it should be removed from the ZCT. But not
+      # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
+      if canBeCycleRoot(c): excl(gch.cycleRoots, c)
       when logGC: writeCell("zct dealloc cell", c)
       gcTrace(c, csZctFreed)
       # We are about to free the object, call the finalizer BEFORE its
@@ -700,51 +602,53 @@ proc CollectZCT(gch: var TGcHeap) =
       # access invalid memory. This is done by prepareDealloc():
       prepareDealloc(c)
       forAllChildren(c, waZctDecRef)
-      when reallyDealloc: tlsf_free(c)
+      when reallyDealloc: rawDealloc(allocator, c)
       else:
         assert(c.typ != nil)
         zeroMem(c, sizeof(TCell))
-    else:
-      inc(i)
-  when stressGC:
-    for j in 0..gch.zct.len-1: assert(gch.zct.d[j] in gch.stackCells)
 
-proc collectCT(gch: var TGcHeap, zctUpdated: bool) =
+proc unmarkStackAndRegisters(gch: var TGcHeap) = 
+  var d = gch.decStack.d
+  for i in 0..gch.decStack.len-1:
+    assert isAllocatedPtr(allocator, d[i])
+    decRef(d[i]) # OPT: cannot create a cycle!
+  gch.decStack.len = 0
+
+proc collectCT(gch: var TGcHeap) =
   if gch.zct.len >= ZctThreshold or (cycleGC and
-      getOccupiedMem() >= cycleThreshold) or stressGC:    
-    if not zctUpdated: updateZCT()
-    gch.maxStackSize = max(gch.maxStackSize, stackSize())
-    CellSetInit(gch.stackCells)
+      getOccupiedMem() >= cycleThreshold) or stressGC:
+    gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
+    assert(gch.decStack.len == 0)
     markStackAndRegisters(gch)
-    gch.maxStackPages = max(gch.maxStackPages, gch.stackCells.counter)
-    inc(gch.stackScans)
+    gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
+    inc(gch.stat.stackScans)
     collectZCT(gch)
     when cycleGC:
       if getOccupiedMem() >= cycleThreshold or stressGC:
         collectCycles(gch)
         collectZCT(gch)
-        inc(gch.cycleCollections)
+        inc(gch.stat.cycleCollections)
         cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
                              cycleIncrease)
-        gch.maxThreshold = max(gch.maxThreshold, cycleThreshold)
-    CellSetDeinit(gch.stackCells)
+        gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
+    unmarkStackAndRegisters(gch)
 
 proc GC_fullCollect() =
   var oldThreshold = cycleThreshold
   cycleThreshold = 0 # forces cycle collection
-  collectCT(gch, false)
+  collectCT(gch)
   cycleThreshold = oldThreshold
 
 proc GC_getStatistics(): string =
   GC_disable()
   result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
            "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
-           "[GC] stack scans: " & $gch.stackScans & "\n" &
-           "[GC] stack pages: " & $gch.maxStackPages & "\n" &
-           "[GC] cycle collections: " & $gch.cycleCollections & "\n" &
-           "[GC] max threshold: " & $gch.maxThreshold & "\n" &
+           "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
+           "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
+           "[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
+           "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
            "[GC] zct capacity: " & $gch.zct.cap & "\n" &
-           "[GC] max cycle table size: " & $gch.cycleTableSize & "\n" &
-           "[GC] max stack size: " & $gch.maxStackSize
+           "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
+           "[GC] max stack size: " & $gch.stat.maxStackSize
   when traceGC: writeLeakage()
   GC_enable()
diff --git a/lib/lexbase.nim b/lib/lexbase.nim
index f6861a5b6..bb207e92a 100644
--- a/lib/lexbase.nim
+++ b/lib/lexbase.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2008 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
diff --git a/lib/macros.nim b/lib/macros.nim
index 2c71f31c8..2b75a1545 100644
--- a/lib/macros.nim
+++ b/lib/macros.nim
@@ -1,15 +1,17 @@
 #

 #

 #            Nimrod's Runtime Library

-#        (c) Copyright 2008 Andreas Rumpf

+#        (c) Copyright 2009 Andreas Rumpf

 #

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

 #    distribution, for details about the copyright.

 #

 

 

-## This module contains the interface to the compiler's abstract syntax tree.

-## Abstract syntax trees should be modified in macros.

+## This module contains the interface to the compiler's abstract syntax 
+## tree (`AST`:idx:). Macros operate on this tree.

+
+## .. include:: ../doc/astspec.txt
 

 #[[[cog

 #def toEnum(name, elems):

@@ -235,3 +237,13 @@ proc newCall*(theProc: string,
   result.add(newIdentNode(theProc))

   result.add(args)

 
+proc nestList*(theProc: TNimrodIdent,  
+               x: PNimrodNode): PNimrodNode {.compileTime.} = 
+  ## nests the list `x` into a tree of call expressions:
+  ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``
+  var L = x.len
+  result = newCall(theProc, x[L-2], x[L-1])
+  var a = result
+  for i in countdown(L-3, 0):
+    a = newCall(theProc, x[i], copyNimTree(a))
+
diff --git a/lib/math.nim b/lib/math.nim
index 31783efce..bc180f72c 100644
--- a/lib/math.nim
+++ b/lib/math.nim
@@ -208,5 +208,40 @@ else:
     var y = exp(2.0*x)
     return (y-1.0)/(y+1.0)
 
+
+type
+  TRunningStat* = object  ## an accumulator for statistical data
+    n*: int               ## number of pushed data
+    sum*, min*, max*, mean*: float ## self-explaining
+    oldM, oldS, newS: float
+
+proc push*(s: var TRunningStat, x: float) = 
+  ## pushes a value `x` for processing
+  inc(s.n)
+  # See Knuth TAOCP vol 2, 3rd edition, page 232
+  if s.n == 1:
+    s.oldM = x
+    s.mean = x
+    s.oldS = 0.0
+  else:
+    s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
+    s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
+
+    # set up for next iteration:
+    s.oldM = s.mean
+    s.oldS = s.newS
+  
+  s.sum = s.sum + x
+  if s.min > x: s.min = x
+  if s.max < x: s.max = x
+
+proc variance*(s: TRunningStat): float = 
+  ## computes the current variance of `s`
+  if s.n > 1: result = s.newS / (toFloat(s.n - 1))
+
+proc standardDeviation*(s: TRunningStat): float = 
+  ## computes the current standard deviation of `s`
+  result = sqrt(variance(s))
+
 {.pop.}
 {.pop.}
diff --git a/lib/memman.nim b/lib/memman.nim
deleted file mode 100644
index 249607e2d..000000000
--- a/lib/memman.nim
+++ /dev/null
@@ -1,698 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2008 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-
-# Memory manager. Based on:
-# Two Levels Segregate Fit memory allocator (TLSF)
-# Version 2.4.2
-#
-# Written by Miguel Masmano Tello <mimastel@doctor.upv.es>
-#
-# Thanks to Ismael Ripoll for his suggestions and reviews
-#
-# Copyright (C) 2008, 2007, 2006, 2005, 2004
-# 
-# This code is released using a dual license strategy: GPL/LGPL
-# You can choose the licence that better fits your requirements.
-# 
-# Released under the terms of the GNU General Public License Version 2.0
-# Released under the terms of the GNU Lesser General Public License Version 2.1
-
-
-# Some IMPORTANT TLSF parameters
-const
-  blockAlign = sizeof(pointer) * 2
-  maxFli = 30
-  maxLog2Sli = 5
-  maxSli = 1 shl maxLog2Sli
-  
-  fliOffset = 6 # tlsf structure just will manage blocks bigger than 128 bytes
-  smallBlock = 128
-  realFli = MaxFli - fliOffset
-  
-type
-  TFreePtr {.final.} = object
-    prev, next: PBhdr
-  Pbhdr = ptr Tbhdr
-  Tbhdr {.final.} = object
-    prevHdr: Pbhdr # this is just valid if the first bit of size is set
-    size: int # the size is stored in bytes 
-              # bit 0 indicates whether the block is used and
-              # bit 1 allows to know whether the previous block is free
-    freePtr: TFreePtr # at this offset bhdr.buffer starts (was a union in the
-                      # C version)
-  TAreaInfo  {.final.} = object  # This structure is embedded at the beginning
-                                 # of each area, giving us enough information 
-                                 # to cope with a set of areas
-    theEnd: Pbhdr
-    next: PAreaInfo
-  PAreaInfo = ptr TAreaInfo
-
-  TLSF {.final.} = object
-    tlsf_signature: int32 # the TLSF's structure signature
-    usedSize, maxSize: int
-    areaHead: PAreaInfo # A linked list holding all the existing areas
-
-    flBitmap: int32  # the first-level bitmap
-                     # This array should have a size of REAL_FLI bits
-    slBitmap: array[0..realFli, int32] # the second-level bitmap
-    matrix: array [0..realFli, array[0..maxSli, PBhdr]]
-  
-const
-  minBlockSize = sizeof(TFreePtr)
-  bhdrOverhead = sizeof(Tbhdr) - minBlockSize
-  tlsfSignature = 0x2A59FA59
-  ptrMask = sizeof(pointer) - 1
-  blockSize = 0xFFFFFFFF - ptrMask
-  memAlign = blockAlign - 1
-  blockState = 0x1
-  prevState = 0x2
-
-  freeBlock = 0x1 # bit 0 of the block size
-  usedBlock = 0x0
-
-  prevFree = 0x2 # bit 1 of the block size
-  prevUsed = 0x0
-  
-  defaultAreaSize = 64*1024 # 1024*10
-  pageSize = if defined(cpu32): 4096 else: 4096*2
-  
-
-proc getNextBlock(adr: pointer, r: int): PBhdr {.inline.} = 
-  return cast[PBhdr](cast[TAddress](adr) +% r)
-
-proc roundupSize(r: int): int = return (r +% memAlign) and not memAlign
-proc rounddownSize(r: int): int = return r and not memAlign
-proc roundup(x, v: int): int = return (((not x)+%1) and (v-%1)) +% x
-
-proc addSize(s: PTLSF, b: Pbhdr) =
-  inc(s.usedSize, (b.size and blockSize) + bhdrOverhead)
-  s.maxSize = max(s.maxSize, s.usedSize)
-
-proc removeSize(s: PTLSF, b: Pbhdr) =
-  dec(s.usedSize, (b.size and blockSize) + bhdrOverhead)
-
-# ------------ platform specific code -----------------------------------------
-
-when defined(posix): 
-  const # XXX: make these variables for portability?
-    PROT_READ  = 1             # page can be read 
-    PROT_WRITE = 2             # page can be written 
-    PROT_EXEC  = 4             # page can be executed 
-    PROT_NONE  = 0             # page can not be accessed 
-
-    MAP_SHARED    = 1          # Share changes 
-    MAP_PRIVATE   = 2          # Changes are private 
-    MAP_TYPE      = 0xf        # Mask for type of mapping 
-    MAP_FIXED     = 0x10       # Interpret addr exactly 
-    MAP_ANONYMOUS = 0x20       # don't use a file 
-
-    MAP_GROWSDOWN  = 0x100     # stack-like segment 
-    MAP_DENYWRITE  = 0x800     # ETXTBSY 
-    MAP_EXECUTABLE = 0x1000    # mark it as an executable 
-    MAP_LOCKED     = 0x2000    # pages are locked 
-    MAP_NORESERVE  = 0x4000    # don't check for reservations 
-
-  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
-            off: int): pointer {.header: "<sys/mman.h>".}
-
-  proc getNewArea(size: var int): pointer {.inline.} = 
-    size = roundup(size, PageSize)
-    result = mmap(0, size, PROT_READ or PROT_WRITE, 
-                           MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
-    if result == nil or result == cast[pointer](-1):
-      raiseOutOfMem()
-  
-elif defined(windows): 
-  const
-    MEM_RESERVE = 0x2000 
-    MEM_COMMIT = 0x1000
-    MEM_TOP_DOWN = 0x100000
-    PAGE_READWRITE = 0x04
-
-  proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
-                    flProtect: int32): pointer {.
-                    header: "<windows.h>", stdcall.}
-  
-  proc getNewArea(size: var int): pointer {.inline.} = 
-    size = roundup(size, PageSize)
-    result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT or MEM_TOP_DOWN,
-                          PAGE_READWRITE)
-    if result == nil: raiseOutOfMem()
-
-else: 
-  {.warning: "Generic code for allocating pages is used".}
-  # generic implementation relying on malloc:
-  proc malloc(size: int): pointer {.nodecl, importc.}
-  
-  proc getNewArea(size: var int): pointer {.inline.} = 
-    size = roundup(size, PageSize)
-    result = malloc(size)
-    if result == nil: raiseOutOfMem()
-
-# ---------------------------------------------------------------------------
-# small fixed size allocator:
-
-# Design: We manage pages. A page is of constant size, but not necessarily
-# the OS's page size. Pages are managed in a hash table taking advantage of
-# the fact that the OS is likely to give us pages with contingous numbers. 
-# A page contains either small fixed size objects of the same size or 
-# variable length objects. An object's size is always aligned at 16 byte
-# boundary. Huge objects are dealt with the TLSF algorithm. 
-# The design supports iterating over any object in a fast way. 
-
-# A bitset contains any page that starts an allocated page. The page may be
-# empty however. This bitset can be used to quickly determine if a given
-# page belongs to the GC heap. The design of the memory allocator makes it 
-# simple to return unused pages back to the OS.
-
-
-# Small bocks 
-# -----------
-#
-# If we use a list in the free object's space. Allocation and deallocation are
-# O(1). Since any object is of the same size, iteration is quite efficient too.
-# However, pointer detection is easy too: Just check if the type-field is nil.
-# Deallocation sets it to nil.
-# Algorithm:
-
-# i = 0
-# f = b.f # first free address
-# while i < max: 
-#  if a[i] == f: # not a valid block
-#    f = f.next  # next free address
-#  else:
-#    a[i] is a valid object of size s
-#  inc(i)
-
-# The zero count table is an array. Since we know that the RC is zero, we can
-# use the bits for an index into this array. Thus multiple ZCT tables are not
-# difficult to support and insertion and removal is O(1). We use negative
-# indexes for this. This makes it even fast enough (and necessary!) to do a ZCT
-# removal if the RC is incremented. 
-# 
-
-# Huge blocks
-# -----------
-#
-# Huge blocks are always rounded up to a multiple of the page size. These are
-# called *strides*. We also need to keep an index structure 
-# of (stridesize, pagelist). 
-# 
-
-const
-  MemAlign = 8
-  PageShift = if sizeof(int) == 4: 12 else: 13
-  PageSize = 1 shl PageShift
-type
-  TFreeList {.final.} = object
-    next, prev: ptr TFreeList
-
-  TPageDesc {.final.} = object  # the start of a stride always starts with this!
-    size: int                   # lowest bit is set, if it is a huge block
-    free: ptr TFreeList         # only used if it manages multiple cells
-    snext, sprev: ptr TPageDesc # next and prev pagedescs with the same size
-
-  TCellArray {.final.} = object
-    i: int # length
-    d: ptr array [0..1000_000, TCell]
-
-  TPageManager = table[page, ptr TPageDesc]
-
-  TGcHeap {.final.} = object
-    # structure that manages the garbage collected heap
-    zct: TCellArray
-    stackCells: TCellArray
-    smallBlocks: array [PageSize div MemAlign, ptr TPageDesc]
-    freeLists: array [PageSize div MemAlign, ptr TFreeList]
-    pages: TPageManager
-    usedPages: TPageList
-    freePages: TPageList
-
-# small blocks: 
-proc allocSmall(var h: TGcHeap, size: int): pointer = 
-  var s = align(size)
-  var f = h.freeLists[s]
-  if f != nil: 
-    f.prev = f.next # remove from list
-    f.next.prev = f.prev
-    return f
-  var p = h.smallBlocks[s]
-  if p == nil or p.free == nil:
-    p = newSmallBlock(s, p)
-    h.smallBlocks[s] = p
-  
-
-
-proc decRef(cell: PCell) {.inline.} =
-  assert(cell in ct.AT)
-  assert(cell.refcount > 0) # this should be the case!
-  assert(seqCheck(cell))
-  dec(cell.refcount)
-  if cell.refcount == 0:
-    # add to zero count table:
-    zct.d[zct.i] = cell
-    cell.recfcount = -zct.i
-    inc(zct.i)
-
-proc incRef(cell: PCell) {.inline.} =
-  assert(seqCheck(cell))
-  if cell.refcount < 0: 
-    # remove from zero count table:
-    zct.d[-cell.refcount] = zct.d[zct.i-1]
-    dec(zct.i) 
-    cell.refcount = 1
-  else:
-    inc(cell.refcount)
-
-proc asgnRef(dest: ppointer, src: pointer) =
-  # the code generator calls this proc!
-  assert(not isOnStack(dest))
-  # BUGFIX: first incRef then decRef!
-  if src != nil: incRef(usrToCell(src))
-  if dest^ != nil: decRef(usrToCell(dest^))
-  dest^ = src
-
-
-# ----------------------------------------------------------------------------
-#     helpers
-
-const
-  table: array[0..255, int8] = [
-      -1, 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 ls_bit(i: int32): int {.inline.} = 
-  var 
-    a: int = 0
-    x: int = i and -i
-  if x <=% 0xffff:
-    if x <=% ff: a = 0
-    else: a = 8
-  elif x <=% 0xffffff: a = 16
-  else: a = 24
-  return table[x shr a] + a
-
-proc ms_bit(i: int): int {.inline.} = 
-  var
-    a = if i <=% 0xffff: (if i <=% 0xff: 0 else: 8) elif 
-           i <=% 0xffffff: 16 else: 24
-  return table[i shr a] + a
-
-proc set_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
-  adr[nr shr 5] = adr[nr shr 5] or (1 shl (nr and 0x1f))
-
-proc clear_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
-  adr[nr shr 5] = adr[nr shr 5] and not (1 shl (nr and 0x1f))
-
-proc mappingSearch(r, fl, sl: var int) {.inline.} = 
-  if r < smallBlock: 
-    fl = 0
-    sl = r div (smallBlock div maxSli)
-  else:
-    var t = (1 shl (ms_bit(r) - maxLog2Sli)) - 1
-    r = r + t
-    fl = ms_bit(r)
-    sl = (r shl (fl - maxLog2Sli)) - maxSli
-    fl = fl - fliOffset
-    r = r and not t
-
-proc mappingInsert(r: int, fl, sl: var int) {.inline.} = 
-  if r < smallBlock:
-    fl = 0
-    sl = r div (smallBlock div maxSli)
-  else:
-    fl = ms_bit(r)
-    sl = (r shr (fl - maxLog2Sli)) - maxSli
-    fl = fl - fliOffset
-
-proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
-  var tmp = t.slBitmap[fl] and ((not 0) shl sl)
-  if tmp != 0:
-    sl = ls_bit(tmp)
-    result = t.matrix[fl][sl]
-  else:
-    fl = ls_bit(t.flBitmap and (not 0 shl (fl + 1)))
-    if fl > 0: # likely
-      sl = ls_bit(t.slBitmap[fl])
-      result = t.matrix[fl][sl]
-
-proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} = 
-  t.matrix[fl][sl] = b.freePtr.next
-  if t.matrix[fl][sl] != nil:
-    t.matrix[fl][sl].freePtr.prev = nil
-  else:
-    clear_bit(sl, t.slBitmap[fl])
-    if t.slBitmap[fl] == 0:
-      clear_bit(fl, t.flBitmap)
-  b.freePtr.prev = nil
-  b.freePtr.next = nil
-
-proc extractBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
-  if b.freePtr.next != nil:
-    b.freePtr.next.freePtr.prev = b.freePtr.prev
-  if b.freePtr.prev != nil:
-    b.freePtr.prev.freePtr.next = b.freePtr.next
-  if t.matrix[fl][sl] == b:
-    t.matrix[fl][sl] = b.freePtr.next
-    if t.matrix[fl][sl] == nil:
-      clear_bit(sl, t.slBitmap[fl])
-      if t.slBitmap[fl] == 0:
-        clear_bit(fl, t.flBitmap)
-  b.freePtr.prev = nil
-  b.freePtr.next = nil
-
-proc insertBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} = 
-  b.freePtr.prev = nil
-  b.freePtr.next = t.matrix[fl][sl] 
-  if t.matrix[fl][sl] != nil:
-    t.matrix[fl][sl].freePtr.prev = b		
-  t.matrix[fl][sl] = b
-  set_bit(sl, t.slBitmap[fl])
-  set_bit(fl, t.flBitmap)
-
-proc getBuffer(b: Pbhdr): pointer {.inline.} = 
-  result = cast[pointer](addr(b.freePtr))
-
-proc processArea(area: pointer, size: int): Pbhdr =
-  var 
-    b, lb, ib: Pbhdr
-    ai: PAreaInfo
-  ib = cast[Pbhdr](area)
-  if sizeof(TAreaInfo) < minBlockSize:
-    ib.size = minBlockSize or usedBlock or prevUsed
-  else
-    ib.size = roundupSize(sizeof(TAreaInfo)) or usedBlock or prevUsed
-  b = getNextBlock(getBuffer(ib), ib.size and blockSize)
-  b.size = rounddownSize(size - 3 * bhdrOverhead - (ib.size and blockSize)) or
-           usedBlock or prevUsed
-  b.freePtr.prev = nil
-  b.freePtr.next = nil
-  lb = getNextBlock(getBuffer(b), b.size and blockSize)
-  lb.prevHdr = b
-  lb.size = 0 or usedBlock or prevFree
-  ai = cast[PAreaInfo](getBuffer(ib))
-  ai.next = nil
-  ai.theEnd = lb
-  return ib
-
-# ----------------------------------------------------------------------------
-#                  Begin of the allocator code
-
-proc initMemoryPool(memPoolSize: int, memPool: pointer): int = 
-  var
-    t: PLSF
-    b, ib: Pbhdr
-
-  if memPool == nil or memPoolSize < sizeof(TLSF) + bhdrOverhead * 8:
-    writeToStdErr("initMemoryPool(): memory_pool invalid\n")
-    return -1
-
-  if (cast[TAddress](memPool) and ptrMask) != 0:
-    writeToStdErr("initMemoryPool(): memPool must be aligned to a word\n")
-    return -1
-  t = cast[PLSF](memPool)
-  # Check if already initialised
-  if t.signature == tlsfSignature:
-    b = getNextBlock(memPool, roundupSize(sizeof(TLSF)))
-    return b.size and blockSize
-  zeroMem(memPool, sizeof(TLSF))
-
-  t.signature = tlsfSignature
-  ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))), 
-                   rounddownSize(memPoolSize - sizeof(TLSF)))
-  b = getNextBlock(getBuffer(ib), ib.size and blockSize)
-  freeEx(getBuffer(b), t)
-  t.areaHead = cast[PAreaInfo](getBuffer(ib))
-
-  t.used_size = memPoolSize - (b.size and blockSize)
-  t.max_size = t.used_size
-  return b.size and blockSize
-
-
-proc addNewArea(area: pointer, areaSize: int, t: var TLSF): int = 
-  var
-    p, ptrPrev, ai: PAreaInfo
-    ib0, b0, lb0, ib1, b1, lb1, nextB: Pbhdr
-
-  zeroMem(area, areaSize)
-  p = t.areaHead
-  ptrPrev = 0
-
-  ib0 = processArea(area, areaSize)
-  b0 = getNextBlock(getBuffer(ib0), ib0.size and blockSize)
-  lb0 = getNextBlock(getBuffer(b0), b0.size and blockSize)
-
-  # Before inserting the new area, we have to merge this area with the
-  # already existing ones
-  while p != nil:
-    ib1 = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead) 
-    b1 = getNextBlock(getBuffer(ib1), ib1.size and blockSize)
-    lb1 = p.theEnd
-
-    # Merging the new area with the next physically contigous one
-    if cast[TAddress](ib1) == cast[TAddress](lb0) +% bhdrOverhead:
-      if t.areaHead == p:
-        t.areaHead = p.next
-        p = p.next
-      else:
-        ptrPrev.next = p.next
-        p = p.next
-      b0.size = rounddownSize((b0.size and blockSize) +
-                         (ib1.size and blockSize) + 2 * bhdrOverhead) or
-                         usedBlock or prevUsed
-      b1.prevHdr = b0
-      lb0 = lb1
-      continue
-
-    # Merging the new area with the previous physically contigous one
-    if getBuffer(lb1) == pointer(ib0):
-      if t.areaHead == p:
-        t.areaHead = p.next
-        p = p.next
-      else:
-        ptrPrev.next = p.next
-        p = p.next
-      lb1->size = rounddownSize((b0.size and blockSize) +
-                   (ib0.size and blockSize) + 2 * bhdrOverhead) or
-                   usedBlock or (lb1.size and prevState)
-      nextB = getNextBlock(getBuffer(lb1), lb1.size and blockSize)
-      nextB.prevHdr = lb1
-      b0 = lb1
-      ib0 = ib1
-      continue
-    ptrPrev = p
-    p = p.next
-
-  # Inserting the area in the list of linked areas 
-  ai = cast[PAreaInfo](getBuffer(ib0))
-  ai.next = t.areaHead
-  ai.theEnd = lb0
-  t.areaHead = ai
-  freeEx(getBuffer(b0), memPool)
-  return (b0.size and blockSize)
-
-proc mallocEx(asize: int, t: var TLSF): pointer = 
-  var
-    b, b2, nextB: Pbhdr
-    fl, sl, tmpSize, size: int
-
-  size = if asize < minBlockSize: minBlockSize else: roundupSize(asize)
-
-  # Rounding up the requested size and calculating fl and sl
-  mappingSearch(size, fl, sl)
-
-  # Searching a free block, recall that this function changes the values
-  # of fl and sl, so they are not longer valid when the function fails
-  b = findSuitableBlock(tlsf, fl, sl)
-  if b == nil: 
-    # Growing the pool size when needed 
-    # size plus enough room for the required headers:
-    var areaSize = max(size + bhdrOverhead * 8, defaultAreaSize)
-    var area = getNewArea(areaSize)
-    addNewArea(area, areaSize, t)
-    # Rounding up the requested size and calculating fl and sl
-    mappingSearch(size, fl, sl)
-    # Searching a free block
-    b = findSuitableBlock(t, fl, sl)
-    if b == nil: 
-      raiseOutOfMem()
-
-  extractBlockHdr(b, t, fl, sl)
-
-  #-- found:
-  nextB = getNextBlock(getBuffer(b), b.size and blockSize)
-  # Should the block be split?
-  tmpSize = (b.size and blockSize) - size
-  if tmpSize >= sizeof(Tbhdr):
-    dec(tmpSize, bhdrOverhead)
-    b2 = getNextBlock(getBuffer(b), size)
-    b2.size = tmpSize or freeBlock or prevUsed
-    nextB.prevHdr = b2
-    mappingInsert(tmpSize, fl, sl)
-    insertBlock(b2, t, fl, sl)
-
-    b.size = size or (b.size and prevState)
-  else:
-    nextB.size = nextB.size and not prevFree
-    b.size = b.size and not freeBlock # Now it's used
-
-  addSize(t, b)
-  return getBuffer(b)
-
-
-proc freeEx(p: pointer, t: var TLSF) =
-  var
-    fl = 0
-    sl = 0
-    b, tmpB: Pbhdr
-
-  assert(p != nil)
-  b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
-  b.size = b.size or freeBlock
-
-  removeSize(t, b)
-  b.freePtr.prev = nil
-  b.freePtr.next = nil
-  tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
-  if (tmpB.size and freeBlock) != 0:
-    mappingInsert(tmpB.size and blockSize, fl, sl)
-    extractBlock(tmpB, t, fl, sl)
-    inc(b.size, (tmpB.size and blockSize) + bhdrOverhead)
-  if (b.size and prevFree) != 0:
-    tmpB = b.prevHdr
-    mappingInsert(tmpB.size and blockSize, fl, sl)
-    extractBlock(tmpB, t, fl, sl)
-    inc(tmpB.size, (b.size and blockSize) + bhdrOverhead)
-    b = tmpB
-  mappingInsert(b.size and blockSize, fl, sl)
-  insertBlock(b, t, fl, sl)
-
-  tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
-  tmpB.size = tmpB.size or prevFree
-  tmpB.prevHdr = b
-
-proc reallocEx(p: pointer, newSize: int, t: var TLSF): pointer = 
-  var
-    cpsize, fl, sl, tmpSize: int
-    b, tmpB, nextB: Pbhdr
-
-  assert(p != nil)
-  assert(newSize > 0)
-
-  b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
-  nextB = getNextBlock(getBuffer(b), b.size and blockSize)
-  newSize = if newSize < minBlockSize: minBlockSize else: roundupSize(newSize)
-  tmpSize = b.size and blockSize
-  if newSize <= tmpSize:
-    removeSize(t, b)
-    if (nextB.size and freeBlock) != 0: 
-      mappingInsert(nextB.size and blockSize, fl, sl)
-      extractBlock(nextB, t, fl, sl)
-      inc(tmpSize, (nextB.size and blockSize) + bhdrOverhead)
-      nextB = getNextBlock(getBuffer(nextB), nextB.size and blockSize)
-      # We always reenter this free block because tmpSize will
-      # be greater then sizeof(Tbhdr)
-    dec(tmpSize, newSize)
-    if tmpSize >= sizeof(Tbhdr):
-      dec(tmpSize, bhdrOverhead)
-      tmpB = getNextBlock(getBuffer(b), newSize)
-      tmpB.size = tmpSize or freeBlock or prevUsed
-      nextB.prevHdr = tmpB
-      nextB.size = nextB.size or prevFree
-      mappingInsert(tmpSize, fl, sl)
-      insertBlock(tmpB, t, fl, sl)
-      b.size = newSize or (b.size and prevState)
-    addSize(t, b)
-    return getBuffer(b)
-  
-  if (nextB.size and freeBlock) != 0:
-    if newSize <= tmpSize + (nextB.size and blockSize):
-      removeSize(t, b)
-      mappingInsert(nextB.size and blockSize, fl, sl)
-      extractBlock(nextB, t, fl, sl)
-      inc(b.size, (nextB.size and blockSize) + bhdrOverhead)
-      nextB = getNextBlock(getBuffer(b), b.size and blockSize)
-      nextB.prevHdr = b
-      nextB.size = nextB.size and not prevFree
-      tmpSize = (b.size and blockSize) - newSize
-      if tmpSize >= sizeof(Tbhdr):
-        dec(tmpSize, bhdrOverhead)
-        tmpB = getNextBlock(getBuffer(b), newSize)
-        tmpB.size = tmpSize or freeBlock or prevUsed
-        nextB.prevHdr = tmpB
-        nextB.size = nextB.size or prevFree
-        mappingInsert(tmpSize, fl, sl)
-        insertBlock(tmpB, t, fl, sl)
-        b.size = newSize or (b.size and prevState)
-      addSize(t, b)
-      return getBuffer(b)
-
-  var ptrAux = mallocEx(newSize, t)
-  cpsize = if (b.size and blockSize) > newSize: newSize else:
-                                                (b.size and blockSize)
-  copyMem(ptrAux, p, cpsize)
-  freeEx(p, memPool)
-  return ptrAux
-
-
-proc ansiCrealloc(p: pointer, newSize: int, t: var TLSF): pointer = 
-  if p == nil: 
-    if newSize > 0: 
-      result = mallocEx(newSize, t)
-    else:
-      result = nil
-  elif newSize <= 0:
-    freeEx(p, t)
-    result = nil
-  else:
-    result = reallocEx(p, newSize, t)
-
-proc InitTLSF(t: var TLSF) = 
-  var areaSize = sizeof(TLSF) + BHDR_OVERHEAD * 8 # Just a safety constant
-  areaSize = max(areaSize, DEFAULT_areaSize)
-  var area = getNewArea(areaSize)
-  
-  
-  initMemoryPool(areaSize, area)
-
-  var
-    t: PLSF
-    b, ib: Pbhdr
-
-  t = cast[PLSF](memPool)
-  
-  zeroMem(area, areaSize)
-
-  t.signature = tlsfSignature
-  var ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))), 
-                   rounddownSize(memPoolSize - sizeof(TLSF)))
-  var b = getNextBlock(getBuffer(ib), ib.size and blockSize)
-  freeEx(getBuffer(b), t)
-  t.areaHead = cast[PAreaInfo](getBuffer(ib))
-
-  t.used_size = memPoolSize - (b.size and blockSize)
-  t.max_size = t.used_size
-
-  # XXX
diff --git a/lib/mm.nim b/lib/mm.nim
new file mode 100644
index 000000000..5f16f7304
--- /dev/null
+++ b/lib/mm.nim
@@ -0,0 +1,187 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Nimrod high-level memory manager: It supports Boehm's GC, no GC and the
+# native Nimrod GC. The native Nimrod GC is the default.
+
+#{.push checks:on, assertions:on.}
+{.push checks:off.}
+
+const
+  debugGC = false # we wish to debug the GC...
+  logGC = false
+  traceGC = false # extensive debugging
+  reallyDealloc = true # for debugging purposes this can be set to false
+  cycleGC = true # (de)activate the cycle GC
+  stressGC = false
+  reallyOsDealloc = true
+  coalescRight = true
+  coalescLeft = true
+
+type
+  PPointer = ptr pointer
+  TByteArray = array[0..1000_0000, byte]
+  PByte = ptr TByteArray
+  PString = ptr string
+
+# Page size of the system; in most cases 4096 bytes. For exotic OS or
+# CPU this needs to be changed:
+const
+  PageShift = 12
+  PageSize = 1 shl PageShift
+  PageMask = PageSize-1
+
+  MemAlign = 8 # also minimal allocatable memory block
+
+  BitsPerPage = PageSize div MemAlign
+  UnitsPerPage = BitsPerPage div (sizeof(int)*8)
+    # how many ints do we need to describe a page:
+    # on 32 bit systems this is only 16 (!)
+
+  TrunkShift = 9
+  BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64
+  TrunkMask = BitsPerTrunk - 1
+  IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
+  IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
+  IntMask = 1 shl IntShift - 1
+
+var
+  gOutOfMem: ref EOutOfMemory
+
+proc raiseOutOfMem() {.noreturn.} =
+  if gOutOfMem == nil: quit("out of memory; cannot even throw an exception")
+  gOutOfMem.msg = "out of memory"
+  raise gOutOfMem
+
+when defined(boehmgc):
+  when defined(windows):
+    const boehmLib = "boehmgc.dll"
+  else:
+    const boehmLib = "/usr/lib/libgc.so.1"
+
+  proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} 
+  proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} 
+  proc boehmGCincremental {.
+    importc: "GC_enable_incremental", dynlib: boehmLib.} 
+  proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}  
+  proc boehmAlloc(size: int): pointer {.
+    importc: "GC_malloc", dynlib: boehmLib.}
+  proc boehmAllocAtomic(size: int): pointer {.
+    importc: "GC_malloc_atomic", dynlib: boehmLib.}
+  proc boehmRealloc(p: pointer, size: int): pointer {.
+    importc: "GC_realloc", dynlib: boehmLib.}
+  proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
+    
+  proc alloc(size: int): pointer =
+    result = boehmAlloc(size)
+    if result == nil: raiseOutOfMem()
+  proc alloc0(size: int): pointer =
+    result = alloc(size)
+    zeroMem(result, size)
+  proc realloc(p: Pointer, newsize: int): pointer =
+    result = boehmRealloc(p, newsize)
+    if result == nil: raiseOutOfMem()
+  proc dealloc(p: Pointer) =
+    boehmDealloc(p)
+
+  proc initGC() = nil
+  
+  #boehmGCincremental()
+
+  proc GC_disable() = boehmGC_disable()
+  proc GC_enable() = boehmGC_enable()
+  proc GC_fullCollect() = boehmGCfullCollect()
+  proc GC_setStrategy(strategy: TGC_Strategy) = nil
+  proc GC_enableMarkAndSweep() = nil
+  proc GC_disableMarkAndSweep() = nil
+  proc GC_getStatistics(): string = return ""
+  
+  proc getOccupiedMem(): int = return -1
+  proc getFreeMem(): int = return -1
+  proc getTotalMem(): int = return -1
+
+  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+    result = alloc(size)
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+    result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).space = len
+
+  proc growObj(old: pointer, newsize: int): pointer =
+    result = realloc(old, newsize)
+
+  proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
+  proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
+  proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
+  
+  proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+  proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+  proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+
+  include cellsets
+elif defined(nogc):
+  proc alloc(size: int): pointer =
+    result = c_malloc(size)
+    if result == nil: raiseOutOfMem()
+  proc alloc0(size: int): pointer =
+    result = alloc(size)
+    zeroMem(result, size)
+  proc realloc(p: Pointer, newsize: int): pointer =
+    result = c_realloc(p, newsize)
+    if result == nil: raiseOutOfMem()
+  proc dealloc(p: Pointer) =
+    c_free(p)
+
+  proc initGC() = nil
+  proc GC_disable() = nil
+  proc GC_enable() = nil
+  proc GC_fullCollect() = nil
+  proc GC_setStrategy(strategy: TGC_Strategy) = nil
+  proc GC_enableMarkAndSweep() = nil
+  proc GC_disableMarkAndSweep() = nil
+  proc GC_getStatistics(): string = return ""
+  
+  proc getOccupiedMem(): int = return -1
+  proc getFreeMem(): int = return -1
+  proc getTotalMem(): int = return -1
+
+  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+    result = alloc0(size)
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+    result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).space = len
+  proc growObj(old: pointer, newsize: int): pointer =
+    result = realloc(old, newsize)
+    # XXX BUG: we need realloc0 here, but C does not support this...
+
+  proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
+  proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
+  proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
+  
+  proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+  proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+  proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
+    dest^ = src
+
+  include cellsets
+else:
+  include alloc
+  include cellsets
+  assert(sizeof(TCell) == sizeof(TFreeCell))
+  include gc
+  
+{.pop.}
+
+  
diff --git a/lib/os.nim b/lib/os.nim
index b69002715..20e803ec0 100644
--- a/lib/os.nim
+++ b/lib/os.nim
@@ -546,7 +546,7 @@ proc getApplicationFilename(): string =
   # Solaris:
   # /proc/<pid>/object/a.out (filename only)
   # /proc/<pid>/path/a.out (complete pathname)
-  # *BSD (and maybe Darwing too):
+  # *BSD (and maybe Darwin too):
   # /proc/<pid>/file
   when defined(windows):
     result = newString(256)
diff --git a/lib/osproc.nim b/lib/osproc.nim
index 2282e802d..205460614 100644
--- a/lib/osproc.nim
+++ b/lib/osproc.nim
@@ -1,20 +1,21 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2008 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-## This module implements an advanced facility for executing OS processes.
-## On Windows this module is currently broken. Please help!
+## This module implements an advanced facility for executing OS processes
+## and process communication.
+## **On Windows this module does not work properly. Please help!**
 
 import
   os, strtabs, streams
 
 when defined(windows):
-  import windows
+  import winlean
 
 type
   TProcess = object of TObject
@@ -41,6 +42,10 @@ proc executeProcess*(command: string,
   ## A convience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
 
+proc executeCommand*(command: string): int
+  ## Executes ``command`` and returns its error code. Standard input, output,
+  ## error streams are inherited from the calling process.
+
 proc startProcess*(command: string,
                    workingDir: string = "",
                    args: openarray[string] = [],
@@ -133,14 +138,14 @@ when defined(Windows):
 
   proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
     var br: int32
-    var a = windows.ReadFile(s.handle, buffer, bufLen, br, nil)
+    var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil)
     if a == 0: OSError()
     result = br
     #atEnd = bytesRead < bufLen
 
   proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
     var bytesWritten: int32
-    var a = windows.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
+    var a = winlean.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
     if a == 0: OSError()
 
   proc newFileHandleStream(handle: THandle): PFileHandleStream =
@@ -180,12 +185,12 @@ when defined(Windows):
   #  O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
   #  O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
 
-  proc CreatePipeHandles(Inhandle, OutHandle: ptr THandle) =
+  proc CreatePipeHandles(Inhandle, OutHandle: var THandle) =
     var piInheritablePipe: TSecurityAttributes
     piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
-    piInheritablePipe.lpSecurityDescriptor = Nil
+    piInheritablePipe.lpSecurityDescriptor = nil
     piInheritablePipe.Binherithandle = 1
-    if CreatePipe(Inhandle, Outhandle, addr(piInheritablePipe), 0) == 0'i32:
+    if CreatePipe(Inhandle, Outhandle, piInheritablePipe, 0) == 0'i32:
       OSError()
 
   proc startProcess*(command: string,
@@ -201,15 +206,15 @@ when defined(Windows):
       hi, ho, he: THandle
     SI.cb = SizeOf(SI)
     SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
-    CreatePipeHandles(addr(SI.hStdInput), addr(HI))
-    CreatePipeHandles(addr(HO), addr(Si.hStdOutput))
+    CreatePipeHandles(SI.hStdInput, HI)
+    CreatePipeHandles(HO, Si.hStdOutput)
     #SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE())
     #SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE())
     if poStdErrToStdOut in options:
       SI.hStdError = SI.hStdOutput
       HE = HO
     else:
-      CreatePipeHandles(addr(HE), addr(Si.hStdError))
+      CreatePipeHandles(HE, Si.hStdError)
       #SI.hStdError = GetStdHandle(STD_ERROR_HANDLE())
     #result.inputHandle = open_osfhandle(HI, O_WRONLY)
     #if result.inputHandle == -1'i32: OSError()
@@ -224,16 +229,12 @@ when defined(Windows):
     var wd: cstring = nil
     if len(workingDir) > 0: wd = workingDir
     if env == nil:
-      success = Windows.CreateProcess(nil,
-        cmdl, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, nil, wd,
-        addr(SI), addr(ProcInfo))
+      success = winlean.CreateProcess(nil,
+        cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo)
     else:
       var e = buildEnv(env)
-      success = Windows.CreateProcess(nil,
-        cmdl, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, e, wd,
-        addr(SI), addr(ProcInfo))
+      success = winlean.CreateProcess(nil,
+        cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
       dealloc(e)
     dealloc(cmdl)
     if success == 0:
@@ -249,7 +250,7 @@ when defined(Windows):
     discard ResumeThread(p.FThreadHandle)
 
   proc running(p: PProcess): bool =
-    var x = waitForSingleObject(p.FThreadHandle, 50)
+    var x = waitForSingleObject(p.FProcessHandle, 50)
     return x == WAIT_TIMEOUT
 
   proc terminate(p: PProcess) =
@@ -257,11 +258,11 @@ when defined(Windows):
       discard TerminateProcess(p.FProcessHandle, 0)
 
   proc waitForExit(p: PProcess): int =
-    discard WaitForSingleObject(p.FThreadHandle, Infinite)
-    var res: dword
+    discard CloseHandle(p.FThreadHandle)
+    discard WaitForSingleObject(p.FProcessHandle, Infinite)
+    var res: int32
     discard GetExitCodeProcess(p.FProcessHandle, res)
     result = res
-    discard CloseHandle(p.FThreadHandle)
     discard CloseHandle(p.FProcessHandle)
 
   proc inputStream(p: PProcess): PStream =
@@ -273,6 +274,29 @@ when defined(Windows):
   proc errorStream(p: PProcess): PStream =
     result = newFileHandleStream(p.errorHandle)
 
+  proc executeCommand(command: string): int = 
+    var
+      SI: TStartupInfo
+      ProcInfo: TProcessInformation
+      process: THandle
+      L: int32
+    SI.cb = SizeOf(SI)
+    SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
+    SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
+    SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
+    if winlean.CreateProcess(nil, command, nil, nil, 0,
+        NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) == 0:
+      OSError()
+    else:
+      Process = ProcInfo.hProcess
+      discard CloseHandle(ProcInfo.hThread)
+      if WaitForSingleObject(Process, INFINITE) != -1:
+        discard GetExitCodeProcess(Process, L)
+        result = int(L)
+      else:
+        result = -1
+      discard CloseHandle(Process)
+
 else:
   import posix
 
@@ -321,15 +345,15 @@ else:
     if pid == 0:
       ## child process:
       discard close(p_stdin[writeIdx])
-      discard dup2(p_stdin[readIdx], readIdx)
+      if dup2(p_stdin[readIdx], readIdx) < 0: OSError()
       discard close(p_stdout[readIdx])
-      discard dup2(p_stdout[writeIdx], writeIdx)
+      if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError()
       if poStdErrToStdOut in options:
-        discard dup2(p_stdout[writeIdx], 2)
+        if dup2(p_stdout[writeIdx], 2) < 0: OSError()
       else:
         if pipe(p_stderr) != 0'i32: OSError("failed to create a pipe")
         discard close(p_stderr[readIdx])
-        discard dup2(p_stderr[writeIdx], 2)
+        if dup2(p_stderr[writeIdx], 2) < 0: OSError()
 
       if workingDir.len > 0:
         os.setCurrentDir(workingDir)
@@ -347,8 +371,7 @@ else:
         else:
           discard execve("/bin/sh", a, ToCStringArray(env))
       # too risky to raise an exception here:
-      echo("execve call failed: " & $strerror(errno))
-      quit(1)
+      quit("execve call failed: " & $strerror(errno))
     # Parent process. Copy process information.
     result.id = pid
 
@@ -376,6 +399,7 @@ else:
       if running(p): discard kill(p.id, SIGKILL)
 
   proc waitForExit(p: PProcess): int =
+    result = 1
     if waitPid(p.id, p.exitCode, 0) == int(p.id):
       result = p.exitCode
 
@@ -393,3 +417,11 @@ else:
     var f: TFile
     if not openFile(f, p.errorHandle, fmRead): OSError()
     result = newFileStream(f)
+
+  proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
+
+  proc executeCommand(command: string): int = 
+    result = csystem(command)
+
+when isMainModule:
+  echo executeCommand("gcc -v")
diff --git a/lib/parsecsv.nim b/lib/parsecsv.nim
new file mode 100644
index 000000000..7665c8287
--- /dev/null
+++ b/lib/parsecsv.nim
@@ -0,0 +1,176 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a simple high performance `CSV`:idx:
+## (`comma separated value`:idx:) parser. 
+##
+## Example: How to use the parser
+## ==============================
+##
+## .. code-block:: nimrod
+##   import os, parsecsv, streams
+##   var s = newFileStream(ParamStr(1), fmRead)
+##   if s == nil: quit("cannot open the file" & ParamStr(1))
+##   var x: TCsvParser
+##   open(x, s, ParamStr(1))
+##   while readRow(x):
+##     Echo "new row: "
+##     for val in items(x.row):
+##       Echo "##", val, "##"
+##   close(x)
+##
+
+import
+  lexbase, streams
+
+type
+  TCsvRow* = seq[string] ## a row in a CSV file
+  TCsvParser* = object of TBaseLexer ## the parser object.
+    row*: TCsvRow                    ## the current row
+    filename: string
+    sep, quote, esc: char
+    skipWhite: bool
+    currRow: int
+
+  EInvalidCsv* = object of EIO ## exception that is raised if
+                               ## a parsing error occurs
+
+proc raiseEInvalidCsv(filename: string, line, col: int, 
+                      msg: string) {.noreturn.} =
+  var e: ref EInvalidCsv
+  new(e)
+  e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
+  raise e
+
+proc error(my: TCsvParser, pos: int, msg: string) = 
+  raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
+
+proc open*(my: var TCsvParser, input: PStream, filename: string,
+           separator = ',', quote = '"', escape = '\0',
+           skipInitialSpace = false) =
+  ## initializes the parser with an input stream. `Filename` is only used
+  ## for nice error messages. The parser's behaviour can be controlled by
+  ## the diverse optional parameters:
+  ## - `separator`: character used to separate fields
+  ## - `quote`: Used to quote fields containing special characters like 
+  ##   `separator`, `quote` or new-line characters. '\0' disables the parsing
+  ##   of quotes.
+  ## - `escape`: removes any special meaning from the following character; 
+  ##   '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
+  ##   two `quote` characters are parsed one literal `quote` character.
+  ## - `skipInitialSpace`: If true, whitespace immediately following the 
+  ##   `separator` is ignored.
+  lexbase.open(my, input)
+  my.filename = filename
+  my.sep = separator
+  my.quote = quote
+  my.esc = escape
+  my.skipWhite = skipInitialSpace
+  my.row = @[]
+  my.currRow = 0
+
+proc parseField(my: var TCsvParser, a: var string) = 
+  var pos = my.bufpos
+  var buf = my.buf
+  if my.skipWhite:
+    while buf[pos] in {' ', '\t'}: inc(pos)
+  setLen(a, 0) # reuse memory
+  if buf[pos] == my.quote and my.quote != '\0': 
+    inc(pos)
+    while true: 
+      var c = buf[pos]
+      if c == '\0':
+        my.bufpos = pos # can continue after exception?
+        error(my, pos, my.quote & " expected")
+        break
+      elif c == my.quote: 
+        if my.esc == '\0' and buf[pos+1] == my.quote:
+          add(a, my.quote)
+          inc(pos, 2)
+        else:
+          inc(pos)
+          break
+      elif c == my.esc:
+        add(a, buf[pos+1])
+        inc(pos, 2)
+      else:
+        case c
+        of '\c': 
+          pos = handleCR(my, pos)
+          add(a, "\n")
+        of '\l': 
+          pos = handleLF(my, pos)
+          add(a, "\n")
+        else:
+          add(a, c)
+          inc(pos)
+  else:
+    while true:
+      var c = buf[pos]
+      if c == my.sep: break
+      if c in {'\c', '\l', '\0'}: break
+      add(a, c)
+      inc(pos)
+  my.bufpos = pos
+
+proc processedRows*(my: var TCsvParser): int = 
+  ## returns number of the processed rows
+  return my.currRow
+
+proc readRow*(my: var TCsvParser, columns = 0): bool = 
+  ## reads the next row; if `columns` > 0, it expects the row to have
+  ## exactly this many columns. Returns false if the end of the file
+  ## has been encountered else true.
+  var col = 0 # current column
+  var oldpos = my.bufpos
+  while my.buf[my.bufpos] != '\0':
+    var oldlen = my.row.len
+    if oldlen < col+1:
+      setLen(my.row, col+1)
+      my.row[col] = ""
+    parseField(my, my.row[col])
+    inc(col)
+    if my.buf[my.bufpos] == my.sep: 
+      inc(my.bufpos)
+    else:
+      case my.buf[my.bufpos]
+      of '\c', '\l': 
+        # skip empty lines:
+        while true: 
+          case my.buf[my.bufpos]
+          of '\c': my.bufpos = handleCR(my, my.bufpos)
+          of '\l': my.bufpos = handleLF(my, my.bufpos)
+          else: break
+      of '\0': nil
+      else: error(my, my.bufpos, my.sep & " expected")
+      break
+  
+  setlen(my.row, col)
+  result = col > 0
+  if result and col != columns and columns > 0: 
+    error(my, oldpos+1, $columns & " columns expected, but found " & 
+          $col & " columns")
+  inc(my.currRow)
+  
+proc close*(my: var TCsvParser) {.inline.} = 
+  ## closes the parser `my` and its associated input stream.
+  lexbase.close(my)
+
+when isMainModule:
+  import os
+  var s = newFileStream(ParamStr(1), fmRead)
+  if s == nil: quit("cannot open the file" & ParamStr(1))
+  var x: TCsvParser
+  open(x, s, ParamStr(1))
+  while readRow(x):
+    Echo "new row: "
+    for val in items(x.row):
+      Echo "##", val, "##"
+  close(x)
+
diff --git a/lib/parsexml.nim b/lib/parsexml.nim
index 81b6da1de..78be404d1 100644
--- a/lib/parsexml.nim
+++ b/lib/parsexml.nim
@@ -22,7 +22,7 @@
 ## * Thus the checks would have been very difficult to implement properly with
 ##   little benefit, especially since they are simple to implement in the 
 ##   client. The client should use the `errorMsgExpected` proc to generate
-##   a nice error message that fits to the other error messages this library
+##   a nice error message that fits the other error messages this library
 ##   creates.
 ##
 ##
diff --git a/lib/ptrset.nim b/lib/ptrset.nim
deleted file mode 100644
index 95f01b16f..000000000
--- a/lib/ptrset.nim
+++ /dev/null
@@ -1,205 +0,0 @@
-# This implements a new pointer set. Access time O(1). For 32 bit systems, we
-# currently need 3 memory accesses. 
-
-const
-  PageSize = 1024 * sizeof(int)
-  MemAlignment = 8 # minimal memory block that can be allocated
-  BitsPerUnit = sizeof(int)*8 
-    # a "unit" is a word, i.e. 4 bytes
-    # on a 32 bit system; I do not use the term "word" because under 32-bit
-    # Windows it is sometimes only 16 bits
-
-  BitsPerPage = PageSize div MemAlignment
-  UnitsPerPage = BitsPerPage div BitsPerUnit
-    # how many units do we need to describe a page:
-    # on 32 bit systems this is only 16 (!)
-
-type
-  PPointer = ptr pointer
-
-  TCollectorData = int
-  TCell {.final.} = object
-    refcount: TCollectorData  # the refcount and bit flags
-    typ: PNimType                  
-    stackcount: int           # stack counter for debugging
-    drefc: int                # real reference counter for debugging
-
-  PCell = ptr TCell
-
-proc cellToUsr(cell: PCell): pointer {.inline.} =
-  # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[TAddress](cell)+%TAddress(sizeof(TCell)))
-
-proc usrToCell(usr: pointer): PCell {.inline.} =
-  # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[TAddress](usr)-%TAddress(sizeof(TCell)))
-
-proc gcAlloc(size: int): pointer =
-  result = alloc0(size)
-  if result == nil: raiseOutOfMem() 
-
-# ------------------ Zero count table (ZCT) and any table (AT) -------------
-
-# this that has to equals zero, otherwise we have to round up UnitsPerPage:
-when BitsPerPage mod BitsPerUnit != 0:
-  {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
-
-# ------------------- cell set handling ------------------------------
-# A cellset consists of a hash table of page descriptors. A page
-# descriptor has a bit for
-# every Memalignment'th byte in the page.
-# However, only bits corresponding to addresses that start memory blocks
-# are set.
-# Page descriptors are also linked to a list; the list
-# is used for easy traversing of all page descriptors; this allows a
-# fast iterator.
-# We use a specialized hashing scheme; the formula is :
-# hash = Page bitand max
-# We use linear probing with the formular: (5*h)+1
-# Thus we likely get no collisions at all if the pages are given to us
-# in a sequential manner by the operating system!
-const
-  bitsPerNode = 10 # we use 10 bits per node; this means 3 memory accesses on
-                   # 32 bit systems
-
-type
-  PPageDesc = ptr TPageDesc
-
-  TBitIndex = range[0..UnitsPerPage-1]
-
-  TPageDesc {.final.} = object
-    next: PPageDesc # all nodes are connected with this pointer
-    key: TAddress   # start address at bit 0
-    bits: array[TBitIndex, int] # a bit vector
-
-  PPageDescArray = ptr array[0..1000_000, PPageDesc]
-  TCellSet {.final.} = object
-    counter, max: int
-    head: PPageDesc
-    data: PPageDescArray
-    
-  TSetNode {.final.} = object
-    n: array[0.. (1 shl bitsPerNode)-1, PSetNode]
-  PSetNode = ptr TSetNode
-
-const
-  InitCellSetSize = 1024 # must be a power of two!
-
-proc CellSetInit(s: var TCellSet) =
-  s.data = gcAlloc(InitCellSetSize * sizeof(PPageDesc))
-  s.max = InitCellSetSize-1
-  s.counter = 0
-  s.head = nil
-
-proc CellSetDeinit(s: var TCellSet) =
-  var it = s.head
-  while it != nil:
-    var n = it.next
-    dealloc(it)
-    it = n
-  s.head = nil # play it safe here
-  dealloc(s.data)
-  s.data = nil
-  s.counter = 0
-
-proc CellSetGet(t: TCellSet, key: TAddress): PPageDesc =
-  var h = cast[int](key) and t.max
-  while t.data[h] != nil:
-    if t.data[h].key == key: return t.data[h]
-    h = nextTry(h, t.max)
-  return nil
-
-proc CellSetRawInsert(t: TCellSet, data: PPageDescArray,
-                      desc: PPageDesc) =
-  var h = cast[int](desc.key) and t.max
-  while data[h] != nil:
-    assert(data[h] != desc)
-    h = nextTry(h, t.max)
-  assert(data[h] == nil)
-  data[h] = desc
-
-proc CellSetEnlarge(t: var TCellSet) =
-  var
-    n: PPageDescArray
-    oldMax = t.max
-  t.max = ((t.max+1)*2)-1
-  n = gcAlloc((t.max + 1) * sizeof(PPageDesc))
-  for i in 0 .. oldmax:
-    if t.data[i] != nil:
-      CellSetRawInsert(t, n, t.data[i])
-  dealloc(t.data)
-  t.data = n
-
-proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
-  var h = cast[int](key) and t.max
-  while true:
-    var x = t.data[h]
-    if x == nil: break
-    if x.key == key: return x
-    h = nextTry(h, t.max)
-
-  if (t.max+1) * 2 < t.counter * 3: CellSetEnlarge(t)
-  inc(t.counter)
-  h = cast[int](key) and t.max
-  while t.data[h] != nil: h = nextTry(h, t.max)
-  assert(t.data[h] == nil)
-  # the new page descriptor goes into result
-  result = gcAlloc(sizeof(TPageDesc))
-  result.next = t.head
-  result.key = key
-  t.head = result
-  t.data[h] = result
-
-# ---------- slightly higher level procs ----------------------------------
-
-proc in_Operator(s: TCellSet, cell: PCell): bool =
-  var
-    u: TAddress
-    t: PPageDesc
-  u = cast[TAddress](cell)
-  t = CellSetGet(s, u /% PageSize)
-  if t != nil:
-    u = (u %% PageSize) /% MemAlignment
-    result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
-  else:
-    result = false
-
-proc incl(s: var TCellSet, cell: PCell) =
-  var
-    u: TAddress
-    t: PPageDesc
-  u = cast[TAddress](cell)
-  t = CellSetPut(s, u /% PageSize)
-  u = (u %% PageSize) /% MemAlignment
-  t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or 
-    (1 shl (u %% BitsPerUnit))
-
-proc excl(s: var TCellSet, cell: PCell) =
-  var
-    u: TAddress
-    t: PPageDesc
-  u = cast[TAddress](cell)
-  t = CellSetGet(s, u /% PageSize)
-  if t != nil:
-    u = (u %% PageSize) /% MemAlignment
-    t.bits[u %% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
-                                  not (1 shl (u %% BitsPerUnit)))
-
-iterator elements(t: TCellSet): PCell {.inline.} =
-  # while traversing it is forbidden to add pointers to the tree!
-  var r = t.head
-  while r != nil:
-    var i = 0
-    while i <= high(r.bits):
-      var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
-      # modifying operations are not allowed during traversation
-      var j = 0
-      while w != 0:         # test all remaining bits for zero
-        if (w and 1) != 0:  # the bit is set!
-          yield cast[PCell]((r.key *% PageSize) +%
-                              (i*%BitsPerUnit+%j) *% MemAlignment)
-        inc(j)
-        w = w shr 1
-      inc(i)
-    r = r.next
-
diff --git a/lib/repr.nim b/lib/repr.nim
index 35c5f9f42..765d66be0 100644
--- a/lib/repr.nim
+++ b/lib/repr.nim
@@ -104,7 +104,7 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerproc.} =
 type
   TReprClosure {.final.} = object # we cannot use a global variable here
                                   # as this wouldn't be thread-safe
-    marked: TCellSeq
+    marked: TCellSet
     recdepth: int       # do not recurse endless
     indent: int         # indentation
 
@@ -171,14 +171,14 @@ proc reprRecord(result: var string, p: pointer, typ: PNimType,
 proc reprRef(result: var string, p: pointer, typ: PNimType,
              cl: var TReprClosure) =
   # we know that p is not nil here:
-  when defined(boehmGC):
+  when defined(boehmGC) or defined(nogc):
     var cell = cast[PCell](p)
   else:
     var cell = usrToCell(p)
   add result, "ref " & reprPointer(p)
   if cell notin cl.marked:
     # only the address is shown:
-    add(cl.marked, cell)
+    incl(cl.marked, cell)
     add result, " --> "
     reprAux(result, p, typ.base, cl)
 
diff --git a/lib/strutils.nim b/lib/strutils.nim
index e3a412053..075e6b252 100644
--- a/lib/strutils.nim
+++ b/lib/strutils.nim
@@ -865,6 +865,15 @@ proc validEmailAddress*(s: string): bool =
      "aero", "jobs", "museum": return true
   return false
   
+proc validIdentifier*(s: string): bool = 
+  ## returns true if `s` is a valid identifier. A valid identifier starts
+  ## with a character of the set `IdentStartChars` and is followed by any
+  ## number of characters of the set `IdentChars`.
+  if s[0] in IdentStartChars:
+    for i in 1..s.len-1:
+      if s[i] notin IdentChars: return false
+    return true
+  
 proc editDistance*(a, b: string): int =
   ## returns the edit distance between `a` and `b`. This uses the Levenshtein
   ## distance algorithm with only a linear memory overhead. This implementation
diff --git a/lib/sysio.nim b/lib/sysio.nim
index d79b5e287..bd52ef5ea 100644
--- a/lib/sysio.nim
+++ b/lib/sysio.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2006 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -54,7 +54,12 @@ proc readLine(f: TFile): string =
   rawReadLine(f, result)
 
 proc write(f: TFile, s: string) = fputs(s, f)
-proc write(f: TFile, i: int) = fprintf(f, "%ld", i)
+proc write(f: TFile, i: int) = 
+  when sizeof(int) == 8:
+    fprintf(f, "%lld", i)
+  else:
+    fprintf(f, "%ld", i)
+    
 proc write(f: TFile, b: bool) =
   if b: write(f, "true")
   else: write(f, "false")
diff --git a/lib/sysstr.nim b/lib/sysstr.nim
index ea46fa503..783bce6af 100644
--- a/lib/sysstr.nim
+++ b/lib/sysstr.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2006 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -37,27 +37,20 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
 proc rawNewString(space: int): NimString {.compilerProc.} =
   var s = space
   if s < 8: s = 7
-  when defined(boehmGC):
-    result = cast[NimString](boehmAllocAtomic(
-      sizeof(TGenericSeq) + (s+1) * sizeof(char)))
-    result.len = 0
-    result.data[0] = '\0'
-  else:
-    result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
-                            (s+1) * sizeof(char)))
+  result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
+                           (s+1) * sizeof(char)))
   result.space = s
 
 proc mnewString(len: int): NimString {.exportc.} =
+  #c_fprintf(c_stdout, "[NEWSTRING] len: %ld\n", len)
   result = rawNewString(len)
   result.len = len
-  when defined(boehmGC):
-    result.data[len] = '\0'
 
 proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
   result = rawNewString(len)
   result.len = len
   c_memcpy(result.data, str, (len+1) * sizeof(Char))
-  result.data[len] = '\0' # IO.readline relies on this!
+  result.data[len] = '\0' # readline relies on this!
 
 proc cstrToNimstr(str: CString): NimString {.compilerProc.} =
   return toNimstr(str, c_strlen(str))
@@ -184,9 +177,9 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerProc.} =
 
 proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
   # increments the length by one:
-  # this is needed for supporting the &= operator;
+  # this is needed for supporting ``add``;
   #
-  #  add seq x  generates:
+  #  add(seq, x)  generates:
   #  seq = incrSeq(seq, sizeof(x));
   #  seq[seq->len-1] = x;
   when false:
@@ -229,7 +222,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
                                  GenericSeqSize))
     elif newLen < result.len:
       # we need to decref here, otherwise the GC leaks!
-      when not defined(boehmGC):
+      when not defined(boehmGC) and not defined(nogc):
         for i in newLen..result.len-1:
           forAllChildrenAux(cast[pointer](cast[TAddress](result) +% 
                             GenericSeqSize +% (i*%elemSize)),
diff --git a/lib/system.nim b/lib/system.nim
index 67a4221f1..23dbfc816 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2008 Andreas Rumpf
+#        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -415,11 +415,11 @@ proc `<=` *(x, y: int32): bool {.magic: "LeI", noSideEffect.}
 proc `<=` *(x, y: int64): bool {.magic: "LeI64", noSideEffect.}
   ## Returns true iff `x` is less than or equal to `y`.
 
-proc `<`  *(x, y: int): bool {.magic: "LtI", noSideEffect.}
-proc `<`  *(x, y: int8): bool {.magic: "LtI", noSideEffect.}
-proc `<`  *(x, y: int16): bool {.magic: "LtI", noSideEffect.}
-proc `<`  *(x, y: int32): bool {.magic: "LtI", noSideEffect.}
-proc `<`  *(x, y: int64): bool {.magic: "LtI64", noSideEffect.}
+proc `<` *(x, y: int): bool {.magic: "LtI", noSideEffect.}
+proc `<` *(x, y: int8): bool {.magic: "LtI", noSideEffect.}
+proc `<` *(x, y: int16): bool {.magic: "LtI", noSideEffect.}
+proc `<` *(x, y: int32): bool {.magic: "LtI", noSideEffect.}
+proc `<` *(x, y: int64): bool {.magic: "LtI64", noSideEffect.}
   ## Returns true iff `x` is less than `y`.
 
 proc abs*(x: int): int {.magic: "AbsI", noSideEffect.}
@@ -790,9 +790,10 @@ proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.}
 # In case of an unhandled exeption the exit handlers should
 # not be called explicitly! The user may decide to do this manually though.
 
-proc copy*(s: string, first = 0): string {.importc: "copyStr", noSideEffect.}
-proc copy*(s: string, first, last: int): string {.importc: "copyStrLast",
-                                                  noSideEffect.}
+proc copy*(s: string, first = 0): string {.
+  magic: "CopyStr", importc: "copyStr", noSideEffect.}
+proc copy*(s: string, first, last: int): string {.
+  magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.}
   ## copies a slice of `s` into a new string and returns this new
   ## string. The bounds `first` and `last` denote the indices of
   ## the first and last characters that shall be copied. If ``last``
@@ -803,7 +804,8 @@ proc setLen*(s: var string, newlen: int) {.magic: "SetLengthStr".}
   ## If the current length is greater than the new length,
   ## ``s`` will be truncated.
 
-proc newString*(len: int): string {.importc: "mnewString", noSideEffect.}
+proc newString*(len: int): string {.
+  magic: "NewString", importc: "mnewString", noSideEffect.}
   ## returns a new string of length ``len`` but with uninitialized
   ## content. One needs to fill the string character after character
   ## with the index operator ``s[i]``. This procedure exists only for
@@ -835,30 +837,23 @@ proc equalMem*(a, b: Pointer, size: int): bool {.
   ## otherwise. Like any procedure dealing with raw memory this is
   ## *unsafe*.
 
-const
-  mallocHeader = "<stdlib.h>"
-
-proc alloc*(size: int): pointer {.
-  importc: "malloc", header: mallocHeader, noconv.}
+proc alloc*(size: int): pointer {.noconv.}
   ## allocates a new memory block with at least ``size`` bytes. The
   ## block has to be freed with ``realloc(block, 0)`` or
   ## ``dealloc(block)``. The block is not initialized, so reading
   ## from it before writing to it is undefined behaviour!
-proc alloc0*(size: int): pointer {.
-  importc: "ALLOC_0", header: mallocHeader, noconv.}
+proc alloc0*(size: int): pointer {.noconv.}
   ## allocates a new memory block with at least ``size`` bytes. The
   ## block has to be freed with ``realloc(block, 0)`` or
   ## ``dealloc(block)``. The block is initialized with all bytes
   ## containing zero, so it is somewhat safer than ``alloc``.
-proc realloc*(p: Pointer, newsize: int): pointer {.
-  importc: "realloc", header: mallocHeader, noconv.}
+proc realloc*(p: Pointer, newsize: int): pointer {.noconv.}
   ## grows or shrinks a given memory block. If p is **nil** then a new
   ## memory block is returned. In either way the block has at least
   ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
   ## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
   ## be freed with ``dealloc``.
-proc dealloc*(p: Pointer) {.
-  importc: "free", header: mallocHeader, noconv.}
+proc dealloc*(p: Pointer) {.noconv.}
   ## frees the memory allocated with ``alloc``, ``alloc0`` or
   ## ``realloc``. This procedure is dangerous! If one forgets to
   ## free the memory a leak occurs; if one tries to access freed
@@ -1436,66 +1431,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     else:
       result = n.sons[n.len]
 
-  when defined(boehmgc):
-    const
-      boehmLib = "/opt/lib/libgc.so"
-  
-    proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} 
-    proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} 
-    proc boehmGCincremental {.
-      importc: "GC_enable_incremental", dynlib: boehmLib.} 
-    proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}  
-    proc boehmAlloc(size: int): pointer {.
-      importc: "GC_malloc", dynlib: boehmLib.}
-    proc boehmAllocAtomic(size: int): pointer {.
-      importc: "GC_malloc_atomic", dynlib: boehmLib.}
-    proc boehmRealloc(p: pointer, size: int): pointer {.
-      importc: "GC_realloc", dynlib: boehmLib.}
-    proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
-      
-  include cellsets
-  
-  when defined(boehmGC):
-    proc initGC() = nil
-    
-    #boehmGCincremental()
-
-    proc GC_disable() = boehmGC_disable()
-    proc GC_enable() = boehmGC_enable()
-    proc GC_fullCollect() = boehmGCfullCollect()
-    proc GC_setStrategy(strategy: TGC_Strategy) = nil
-    proc GC_enableMarkAndSweep() = nil
-    proc GC_disableMarkAndSweep() = nil
-    proc GC_getStatistics(): string = return ""
-    
-    proc getOccupiedMem(): int = return -1
-    proc getFreeMem(): int = return -1
-    proc getTotalMem(): int = return -1
-      
-    proc growObj(old: pointer, newsize: int): pointer {.inline.} = 
-      result = boehmRealloc(old, newsize)
-    proc newObj(size: int): pointer {.compilerproc.} =
-      result = boehmAlloc(size)      
-    proc newSeq(baseSize, len: int): pointer {.compilerproc.} =
-      # XXX: overflow checks!
-      result = newObj(len * baseSize + GenericSeqSize)
-      cast[PGenericSeq](result).len = len
-      cast[PGenericSeq](result).space = len
-
-    proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
-    proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
-    proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
-    
-    proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
-      dest^ = src
-    proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
-      dest^ = src
-    proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
-      dest^ = src
-
-  elif not defined(nogc):
-    include gc
-
+  include mm
   include sysstr
   include assign
   include repr
diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim
index ea650e80a..1865f3369 100644
--- a/lib/windows/windows.nim
+++ b/lib/windows/windows.nim
@@ -3879,7 +3879,7 @@ const
   SW_SHOWNORMAL* = 1
   WPF_RESTORETOMAXIMIZED* = 2
   WPF_SETMINPOSITION* = 1     # Sleep
-  INFINITE* = 0xFFFFFFFF      # SystemParametersInfo
+  INFINITE* = -1'i32      # SystemParametersInfo
   SPI_GETBEEP* = 1
   SPI_SETBEEP* = 2
   SPI_GETMOUSE* = 3
diff --git a/lib/winlean.nim b/lib/winlean.nim
new file mode 100644
index 000000000..747ce7db5
--- /dev/null
+++ b/lib/winlean.nim
@@ -0,0 +1,108 @@
+#

+#

+#            Nimrod's Runtime Library

+#        (c) Copyright 2009 Andreas Rumpf

+#

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

+#    distribution, for details about the copyright.

+#

+

+## This module implements a small wrapper for some needed Win API procedures,

+## so that the Nimrod compiler does not depend on the huge Windows module.

+

+type

+  THandle* = int

+  WINBOOL* = int32

+

+  TSECURITY_ATTRIBUTES* {.final, pure.} = object

+    nLength*: int32

+    lpSecurityDescriptor*: pointer

+    bInheritHandle*: WINBOOL

+  

+  TSTARTUPINFO* {.final, pure.} = object

+    cb*: int32

+    lpReserved*: cstring

+    lpDesktop*: cstring

+    lpTitle*: cstring

+    dwX*: int32

+    dwY*: int32

+    dwXSize*: int32

+    dwYSize*: int32

+    dwXCountChars*: int32

+    dwYCountChars*: int32

+    dwFillAttribute*: int32

+    dwFlags*: int32

+    wShowWindow*: int16

+    cbReserved2*: int16

+    lpReserved2*: pointer

+    hStdInput*: THANDLE

+    hStdOutput*: THANDLE

+    hStdError*: THANDLE

+

+  TPROCESS_INFORMATION* {.final, pure.} = object

+    hProcess*: THANDLE

+    hThread*: THANDLE

+    dwProcessId*: int32

+    dwThreadId*: int32

+

+const

+  STARTF_USESHOWWINDOW* = 1'i32

+  STARTF_USESTDHANDLES* = 256'i32

+  HIGH_PRIORITY_CLASS* = 128'i32

+  IDLE_PRIORITY_CLASS* = 64'i32

+  NORMAL_PRIORITY_CLASS* = 32'i32

+  REALTIME_PRIORITY_CLASS* = 256'i32

+  WAIT_TIMEOUT* = 0x00000102'i32

+  INFINITE* = -1'i32

+

+  STD_INPUT_HANDLE* = -10'i32

+  STD_OUTPUT_HANDLE* = -11'i32

+  STD_ERROR_HANDLE* = -12'i32

+

+proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",

+    importc: "CloseHandle".}

+    

+proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,

+               lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.

+    stdcall, dynlib: "kernel32", importc: "ReadFile".}

+    

+proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,

+                lpNumberOfBytesWritten: var int32, 

+                lpOverlapped: pointer): WINBOOL{.

+    stdcall, dynlib: "kernel32", importc: "WriteFile".}

+

+proc CreatePipe*(hReadPipe, hWritePipe: var THandle,

+                 lpPipeAttributes: var TSECURITY_ATTRIBUTES, 

+                 nSize: int32): WINBOOL{.

+    stdcall, dynlib: "kernel32", importc: "CreatePipe".}

+    

+proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,

+                    lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,

+                    lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,

+                    bInheritHandles: WINBOOL, dwCreationFlags: int32,

+                    lpEnvironment: pointer, lpCurrentDirectory: cstring,

+                    lpStartupInfo: var TSTARTUPINFO,

+                    lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.

+    stdcall, dynlib: "kernel32", importc: "CreateProcessA".}

+

+proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",

+    importc: "SuspendThread".}

+proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",

+    importc: "ResumeThread".}

+

+proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {.

+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}

+

+proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall,

+    dynlib: "kernel32", importc: "TerminateProcess".}

+

+proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {.

+    stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}

+

+proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32",

+    importc: "GetStdHandle".}

+proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall,

+    dynlib: "kernel32", importc: "SetStdHandle".}

+proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",

+    importc: "FlushFileBuffers".}

+

diff --git a/lib/wz_jsgraphics.js b/lib/wz_jsgraphics.js
deleted file mode 100644
index f4ba47b53..000000000
--- a/lib/wz_jsgraphics.js
+++ /dev/null
@@ -1,1107 +0,0 @@
-/* This notice must be untouched at all times.

-

-wz_jsgraphics.js    v. 3.03

-The latest version is available at

-http://www.walterzorn.com

-or http://www.devira.com

-or http://www.walterzorn.de

-

-Copyright (c) 2002-2004 Walter Zorn. All rights reserved.

-Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )

-Last modified: 28. 1. 2008

-

-Performance optimizations for Internet Explorer

-by Thomas Frank and John Holdsworth.

-fillPolygon method implemented by Matthieu Haller.

-

-High Performance JavaScript Graphics Library.

-Provides methods

-- to draw lines, rectangles, ellipses, polygons

-  with specifiable line thickness,

-- to fill rectangles, polygons, ellipses and arcs

-- to draw text.

-NOTE: Operations, functions and branching have rather been optimized

-to efficiency and speed than to shortness of source code.

-

-LICENSE: LGPL

-

-This library is free software; you can redistribute it and/or

-modify it under the terms of the GNU Lesser General Public

-License (LGPL) as published by the Free Software Foundation; either

-version 2.1 of the License, or (at your option) any later version.

-

-This library is distributed in the hope that it will be useful,

-but WITHOUT ANY WARRANTY; without even the implied warranty of

-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

-Lesser General Public License for more details.

-

-You should have received a copy of the GNU Lesser General Public

-License along with this library; if not, write to the Free Software

-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,

-or see http://www.gnu.org/copyleft/lesser.html

-*/

-

-

-var jg_ok, jg_ie, jg_fast, jg_dom, jg_moz;

-

-

-function _chkDHTM(x, i)

-{

-  x = document.body || null;

-  jg_ie = x && typeof x.insertAdjacentHTML != "undefined" && document.createElement;

-  jg_dom = (x && !jg_ie &&

-    typeof x.appendChild != "undefined" &&

-    typeof document.createRange != "undefined" &&

-    typeof (i = document.createRange()).setStartBefore != "undefined" &&

-    typeof i.createContextualFragment != "undefined");

-  jg_fast = jg_ie && document.all && !window.opera;

-  jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";

-  jg_ok = !!(jg_ie || jg_dom);

-}

-

-function _pntCnvDom()

-{

-  var x = this.wnd.document.createRange();

-  x.setStartBefore(this.cnv);

-  x = x.createContextualFragment(jg_fast? this._htmRpc() : this.htm);

-  if(this.cnv) this.cnv.appendChild(x);

-  this.htm = "";

-}

-

-function _pntCnvIe()

-{

-  if(this.cnv) this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast? this._htmRpc() : this.htm);

-  this.htm = "";

-}

-

-function _pntDoc()

-{

-  this.wnd.document.write(jg_fast? this._htmRpc() : this.htm);

-  this.htm = '';

-}

-

-function _pntN()

-{

-  ;

-}

-

-function _mkDiv(x, y, w, h)

-{

-  this.htm += '<div style="position:absolute;'+

-    'left:' + x + 'px;'+

-    'top:' + y + 'px;'+

-    'width:' + w + 'px;'+

-    'height:' + h + 'px;'+

-    'clip:rect(0,'+w+'px,'+h+'px,0);'+

-    'background-color:' + this.color +

-    (!jg_moz? ';overflow:hidden' : '')+

-    ';"><\/div>';

-}

-

-function _mkDivIe(x, y, w, h)

-{

-  this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';

-}

-

-function _mkDivPrt(x, y, w, h)

-{

-  this.htm += '<div style="position:absolute;'+

-    'border-left:' + w + 'px solid ' + this.color + ';'+

-    'left:' + x + 'px;'+

-    'top:' + y + 'px;'+

-    'width:0px;'+

-    'height:' + h + 'px;'+

-    'clip:rect(0,'+w+'px,'+h+'px,0);'+

-    'background-color:' + this.color +

-    (!jg_moz? ';overflow:hidden' : '')+

-    ';"><\/div>';

-}

-

-var _regex =  /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;

-function _htmRpc()

-{

-  return this.htm.replace(

-    _regex,

-    '<div style="overflow:hidden;position:absolute;background-color:'+

-    '$1;left:$2;top:$3;width:$4;height:$5"></div>\n');

-}

-

-function _htmPrtRpc()

-{

-  return this.htm.replace(

-    _regex,

-    '<div style="overflow:hidden;position:absolute;background-color:'+

-    '$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1"></div>\n');

-}

-

-function _mkLin(x1, y1, x2, y2)

-{

-  if(x1 > x2)

-  {

-    var _x2 = x2;

-    var _y2 = y2;

-    x2 = x1;

-    y2 = y1;

-    x1 = _x2;

-    y1 = _y2;

-  }

-  var dx = x2-x1, dy = Math.abs(y2-y1),

-  x = x1, y = y1,

-  yIncr = (y1 > y2)? -1 : 1;

-

-  if(dx >= dy)

-  {

-    var pr = dy<<1,

-    pru = pr - (dx<<1),

-    p = pr-dx,

-    ox = x;

-    while(dx > 0)

-    {--dx;

-      ++x;

-      if(p > 0)

-      {

-        this._mkDiv(ox, y, x-ox, 1);

-        y += yIncr;

-        p += pru;

-        ox = x;

-      }

-      else p += pr;

-    }

-    this._mkDiv(ox, y, x2-ox+1, 1);

-  }

-

-  else

-  {

-    var pr = dx<<1,

-    pru = pr - (dy<<1),

-    p = pr-dy,

-    oy = y;

-    if(y2 <= y1)

-    {

-      while(dy > 0)

-      {--dy;

-        if(p > 0)

-        {

-          this._mkDiv(x++, y, 1, oy-y+1);

-          y += yIncr;

-          p += pru;

-          oy = y;

-        }

-        else

-        {

-          y += yIncr;

-          p += pr;

-        }

-      }

-      this._mkDiv(x2, y2, 1, oy-y2+1);

-    }

-    else

-    {

-      while(dy > 0)

-      {--dy;

-        y += yIncr;

-        if(p > 0)

-        {

-          this._mkDiv(x++, oy, 1, y-oy);

-          p += pru;

-          oy = y;

-        }

-        else p += pr;

-      }

-      this._mkDiv(x2, oy, 1, y2-oy+1);

-    }

-  }

-}

-

-function _mkLin2D(x1, y1, x2, y2)

-{

-  if(x1 > x2)

-  {

-    var _x2 = x2;

-    var _y2 = y2;

-    x2 = x1;

-    y2 = y1;

-    x1 = _x2;

-    y1 = _y2;

-  }

-  var dx = x2-x1, dy = Math.abs(y2-y1),

-  x = x1, y = y1,

-  yIncr = (y1 > y2)? -1 : 1;

-

-  var s = this.stroke;

-  if(dx >= dy)

-  {

-    if(dx > 0 && s-3 > 0)

-    {

-      var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;

-      _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;

-    }

-    else var _s = s;

-    var ad = Math.ceil(s/2);

-

-    var pr = dy<<1,

-    pru = pr - (dx<<1),

-    p = pr-dx,

-    ox = x;

-    while(dx > 0)

-    {--dx;

-      ++x;

-      if(p > 0)

-      {

-        this._mkDiv(ox, y, x-ox+ad, _s);

-        y += yIncr;

-        p += pru;

-        ox = x;

-      }

-      else p += pr;

-    }

-    this._mkDiv(ox, y, x2-ox+ad+1, _s);

-  }

-

-  else

-  {

-    if(s-3 > 0)

-    {

-      var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;

-      _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;

-    }

-    else var _s = s;

-    var ad = Math.round(s/2);

-

-    var pr = dx<<1,

-    pru = pr - (dy<<1),

-    p = pr-dy,

-    oy = y;

-    if(y2 <= y1)

-    {

-      ++ad;

-      while(dy > 0)

-      {--dy;

-        if(p > 0)

-        {

-          this._mkDiv(x++, y, _s, oy-y+ad);

-          y += yIncr;

-          p += pru;

-          oy = y;

-        }

-        else

-        {

-          y += yIncr;

-          p += pr;

-        }

-      }

-      this._mkDiv(x2, y2, _s, oy-y2+ad);

-    }

-    else

-    {

-      while(dy > 0)

-      {--dy;

-        y += yIncr;

-        if(p > 0)

-        {

-          this._mkDiv(x++, oy, _s, y-oy+ad);

-          p += pru;

-          oy = y;

-        }

-        else p += pr;

-      }

-      this._mkDiv(x2, oy, _s, y2-oy+ad+1);

-    }

-  }

-}

-

-function _mkLinDott(x1, y1, x2, y2)

-{

-  if(x1 > x2)

-  {

-    var _x2 = x2;

-    var _y2 = y2;

-    x2 = x1;

-    y2 = y1;

-    x1 = _x2;

-    y1 = _y2;

-  }

-  var dx = x2-x1, dy = Math.abs(y2-y1),

-  x = x1, y = y1,

-  yIncr = (y1 > y2)? -1 : 1,

-  drw = true;

-  if(dx >= dy)

-  {

-    var pr = dy<<1,

-    pru = pr - (dx<<1),

-    p = pr-dx;

-    while(dx > 0)

-    {--dx;

-      if(drw) this._mkDiv(x, y, 1, 1);

-      drw = !drw;

-      if(p > 0)

-      {

-        y += yIncr;

-        p += pru;

-      }

-      else p += pr;

-      ++x;

-    }

-  }

-  else

-  {

-    var pr = dx<<1,

-    pru = pr - (dy<<1),

-    p = pr-dy;

-    while(dy > 0)

-    {--dy;

-      if(drw) this._mkDiv(x, y, 1, 1);

-      drw = !drw;

-      y += yIncr;

-      if(p > 0)

-      {

-        ++x;

-        p += pru;

-      }

-      else p += pr;

-    }

-  }

-  if(drw) this._mkDiv(x, y, 1, 1);

-}

-

-function _mkOv(left, top, width, height)

-{

-  var a = (++width)>>1, b = (++height)>>1,

-  wod = width&1, hod = height&1,

-  cx = left+a, cy = top+b,

-  x = 0, y = b,

-  ox = 0, oy = b,

-  aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,

-  st = (aa2>>1)*(1-(b<<1)) + bb2,

-  tt = (bb2>>1) - aa2*((b<<1)-1),

-  w, h;

-  while(y > 0)

-  {

-    if(st < 0)

-    {

-      st += bb2*((x<<1)+3);

-      tt += bb4*(++x);

-    }

-    else if(tt < 0)

-    {

-      st += bb2*((x<<1)+3) - aa4*(y-1);

-      tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-      w = x-ox;

-      h = oy-y;

-      if((w&2) && (h&2))

-      {

-        this._mkOvQds(cx, cy, x-2, y+2, 1, 1, wod, hod);

-        this._mkOvQds(cx, cy, x-1, y+1, 1, 1, wod, hod);

-      }

-      else this._mkOvQds(cx, cy, x-1, oy, w, h, wod, hod);

-      ox = x;

-      oy = y;

-    }

-    else

-    {

-      tt -= aa2*((y<<1)-3);

-      st -= aa4*(--y);

-    }

-  }

-  w = a-ox+1;

-  h = (oy<<1)+hod;

-  y = cy-oy;

-  this._mkDiv(cx-a, y, w, h);

-  this._mkDiv(cx+ox+wod-1, y, w, h);

-}

-

-function _mkOv2D(left, top, width, height)

-{

-  var s = this.stroke;

-  width += s+1;

-  height += s+1;

-  var a = width>>1, b = height>>1,

-  wod = width&1, hod = height&1,

-  cx = left+a, cy = top+b,

-  x = 0, y = b,

-  aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,

-  st = (aa2>>1)*(1-(b<<1)) + bb2,

-  tt = (bb2>>1) - aa2*((b<<1)-1);

-

-  if(s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))

-  {

-    var ox = 0, oy = b,

-    w, h,

-    pxw;

-    while(y > 0)

-    {

-      if(st < 0)

-      {

-        st += bb2*((x<<1)+3);

-        tt += bb4*(++x);

-      }

-      else if(tt < 0)

-      {

-        st += bb2*((x<<1)+3) - aa4*(y-1);

-        tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-        w = x-ox;

-        h = oy-y;

-

-        if(w-1)

-        {

-          pxw = w+1+(s&1);

-          h = s;

-        }

-        else if(h-1)

-        {

-          pxw = s;

-          h += 1+(s&1);

-        }

-        else pxw = h = s;

-        this._mkOvQds(cx, cy, x-1, oy, pxw, h, wod, hod);

-        ox = x;

-        oy = y;

-      }

-      else

-      {

-        tt -= aa2*((y<<1)-3);

-        st -= aa4*(--y);

-      }

-    }

-    this._mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);

-    this._mkDiv(cx+a+wod-s, cy-oy, s, (oy<<1)+hod);

-  }

-

-  else

-  {

-    var _a = (width-(s<<1))>>1,

-    _b = (height-(s<<1))>>1,

-    _x = 0, _y = _b,

-    _aa2 = (_a*_a)<<1, _aa4 = _aa2<<1, _bb2 = (_b*_b)<<1, _bb4 = _bb2<<1,

-    _st = (_aa2>>1)*(1-(_b<<1)) + _bb2,

-    _tt = (_bb2>>1) - _aa2*((_b<<1)-1),

-

-    pxl = new Array(),

-    pxt = new Array(),

-    _pxb = new Array();

-    pxl[0] = 0;

-    pxt[0] = b;

-    _pxb[0] = _b-1;

-    while(y > 0)

-    {

-      if(st < 0)

-      {

-        pxl[pxl.length] = x;

-        pxt[pxt.length] = y;

-        st += bb2*((x<<1)+3);

-        tt += bb4*(++x);

-      }

-      else if(tt < 0)

-      {

-        pxl[pxl.length] = x;

-        st += bb2*((x<<1)+3) - aa4*(y-1);

-        tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-        pxt[pxt.length] = y;

-      }

-      else

-      {

-        tt -= aa2*((y<<1)-3);

-        st -= aa4*(--y);

-      }

-

-      if(_y > 0)

-      {

-        if(_st < 0)

-        {

-          _st += _bb2*((_x<<1)+3);

-          _tt += _bb4*(++_x);

-          _pxb[_pxb.length] = _y-1;

-        }

-        else if(_tt < 0)

-        {

-          _st += _bb2*((_x<<1)+3) - _aa4*(_y-1);

-          _tt += _bb4*(++_x) - _aa2*(((_y--)<<1)-3);

-          _pxb[_pxb.length] = _y-1;

-        }

-        else

-        {

-          _tt -= _aa2*((_y<<1)-3);

-          _st -= _aa4*(--_y);

-          _pxb[_pxb.length-1]--;

-        }

-      }

-    }

-

-    var ox = -wod, oy = b,

-    _oy = _pxb[0],

-    l = pxl.length,

-    w, h;

-    for(var i = 0; i < l; i++)

-    {

-      if(typeof _pxb[i] != "undefined")

-      {

-        if(_pxb[i] < _oy || pxt[i] < oy)

-        {

-          x = pxl[i];

-          this._mkOvQds(cx, cy, x, oy, x-ox, oy-_oy, wod, hod);

-          ox = x;

-          oy = pxt[i];

-          _oy = _pxb[i];

-        }

-      }

-      else

-      {

-        x = pxl[i];

-        this._mkDiv(cx-x, cy-oy, 1, (oy<<1)+hod);

-        this._mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);

-        ox = x;

-        oy = pxt[i];

-      }

-    }

-    this._mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);

-    this._mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);

-  }

-}

-

-function _mkOvDott(left, top, width, height)

-{

-  var a = (++width)>>1, b = (++height)>>1,

-  wod = width&1, hod = height&1, hodu = hod^1,

-  cx = left+a, cy = top+b,

-  x = 0, y = b,

-  aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,

-  st = (aa2>>1)*(1-(b<<1)) + bb2,

-  tt = (bb2>>1) - aa2*((b<<1)-1),

-  drw = true;

-  while(y > 0)

-  {

-    if(st < 0)

-    {

-      st += bb2*((x<<1)+3);

-      tt += bb4*(++x);

-    }

-    else if(tt < 0)

-    {

-      st += bb2*((x<<1)+3) - aa4*(y-1);

-      tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-    }

-    else

-    {

-      tt -= aa2*((y<<1)-3);

-      st -= aa4*(--y);

-    }

-    if(drw && y >= hodu) this._mkOvQds(cx, cy, x, y, 1, 1, wod, hod);

-    drw = !drw;

-  }

-}

-

-function _mkRect(x, y, w, h)

-{

-  var s = this.stroke;

-  this._mkDiv(x, y, w, s);

-  this._mkDiv(x+w, y, s, h);

-  this._mkDiv(x, y+h, w+s, s);

-  this._mkDiv(x, y+s, s, h-s);

-}

-

-function _mkRectDott(x, y, w, h)

-{

-  this.drawLine(x, y, x+w, y);

-  this.drawLine(x+w, y, x+w, y+h);

-  this.drawLine(x, y+h, x+w, y+h);

-  this.drawLine(x, y, x, y+h);

-}

-

-function jsgFont()

-{

-  this.PLAIN = 'font-weight:normal;';

-  this.BOLD = 'font-weight:bold;';

-  this.ITALIC = 'font-style:italic;';

-  this.ITALIC_BOLD = this.ITALIC + this.BOLD;

-  this.BOLD_ITALIC = this.ITALIC_BOLD;

-}

-var Font = new jsgFont();

-

-function jsgStroke()

-{

-  this.DOTTED = -1;

-}

-var Stroke = new jsgStroke();

-

-function jsGraphics(cnv, wnd)

-{

-  this.setColor = function(x)

-  {

-    this.color = x.toLowerCase();

-  };

-

-  this.setStroke = function(x)

-  {

-    this.stroke = x;

-    if(!(x+1))

-    {

-      this.drawLine = _mkLinDott;

-      this._mkOv = _mkOvDott;

-      this.drawRect = _mkRectDott;

-    }

-    else if(x-1 > 0)

-    {

-      this.drawLine = _mkLin2D;

-      this._mkOv = _mkOv2D;

-      this.drawRect = _mkRect;

-    }

-    else

-    {

-      this.drawLine = _mkLin;

-      this._mkOv = _mkOv;

-      this.drawRect = _mkRect;

-    }

-  };

-

-  this.setPrintable = function(arg)

-  {

-    this.printable = arg;

-    if(jg_fast)

-    {

-      this._mkDiv = _mkDivIe;

-      this._htmRpc = arg? _htmPrtRpc : _htmRpc;

-    }

-    else this._mkDiv = arg? _mkDivPrt : _mkDiv;

-  };

-

-  this.setFont = function(fam, sz, sty)

-  {

-    this.ftFam = fam;

-    this.ftSz = sz;

-    this.ftSty = sty || Font.PLAIN;

-  };

-

-  this.drawPolyline = this.drawPolyLine = function(x, y)

-  {

-    for (var i=x.length - 1; i;)

-    {--i;

-      this.drawLine(x[i], y[i], x[i+1], y[i+1]);

-    }

-  };

-

-  this.fillRect = function(x, y, w, h)

-  {

-    this._mkDiv(x, y, w, h);

-  };

-

-  this.drawPolygon = function(x, y)

-  {

-    this.drawPolyline(x, y);

-    this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);

-  };

-

-  this.drawEllipse = this.drawOval = function(x, y, w, h)

-  {

-    this._mkOv(x, y, w, h);

-  };

-

-  this.fillEllipse = this.fillOval = function(left, top, w, h)

-  {

-    var a = w>>1, b = h>>1,

-    wod = w&1, hod = h&1,

-    cx = left+a, cy = top+b,

-    x = 0, y = b, oy = b,

-    aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,

-    st = (aa2>>1)*(1-(b<<1)) + bb2,

-    tt = (bb2>>1) - aa2*((b<<1)-1),

-    xl, dw, dh;

-    if(w) while(y > 0)

-    {

-      if(st < 0)

-      {

-        st += bb2*((x<<1)+3);

-        tt += bb4*(++x);

-      }

-      else if(tt < 0)

-      {

-        st += bb2*((x<<1)+3) - aa4*(y-1);

-        xl = cx-x;

-        dw = (x<<1)+wod;

-        tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-        dh = oy-y;

-        this._mkDiv(xl, cy-oy, dw, dh);

-        this._mkDiv(xl, cy+y+hod, dw, dh);

-        oy = y;

-      }

-      else

-      {

-        tt -= aa2*((y<<1)-3);

-        st -= aa4*(--y);

-      }

-    }

-    this._mkDiv(cx-a, cy-oy, w, (oy<<1)+hod);

-  };

-

-  this.fillArc = function(iL, iT, iW, iH, fAngA, fAngZ)

-  {

-    var a = iW>>1, b = iH>>1,

-    iOdds = (iW&1) | ((iH&1) << 16),

-    cx = iL+a, cy = iT+b,

-    x = 0, y = b, ox = x, oy = y,

-    aa2 = (a*a)<<1, aa4 = aa2<<1, bb2 = (b*b)<<1, bb4 = bb2<<1,

-    st = (aa2>>1)*(1-(b<<1)) + bb2,

-    tt = (bb2>>1) - aa2*((b<<1)-1),

-    // Vars for radial boundary lines

-    xEndA, yEndA, xEndZ, yEndZ,

-    iSects = (1 << (Math.floor((fAngA %= 360.0)/180.0) << 3))

-        | (2 << (Math.floor((fAngZ %= 360.0)/180.0) << 3))

-        | ((fAngA >= fAngZ) << 16),

-    aBndA = new Array(b+1), aBndZ = new Array(b+1);

-    

-    // Set up radial boundary lines

-    fAngA *= Math.PI/180.0;

-    fAngZ *= Math.PI/180.0;

-    xEndA = cx+Math.round(a*Math.cos(fAngA));

-    yEndA = cy+Math.round(-b*Math.sin(fAngA));

-    _mkLinVirt(aBndA, cx, cy, xEndA, yEndA);

-    xEndZ = cx+Math.round(a*Math.cos(fAngZ));

-    yEndZ = cy+Math.round(-b*Math.sin(fAngZ));

-    _mkLinVirt(aBndZ, cx, cy, xEndZ, yEndZ);

-

-    while(y > 0)

-    {

-      if(st < 0) // Advance x

-      {

-        st += bb2*((x<<1)+3);

-        tt += bb4*(++x);

-      }

-      else if(tt < 0) // Advance x and y

-      {

-        st += bb2*((x<<1)+3) - aa4*(y-1);

-        ox = x;

-        tt += bb4*(++x) - aa2*(((y--)<<1)-3);

-        this._mkArcDiv(ox, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);

-        oy = y;

-      }

-      else // Advance y

-      {

-        tt -= aa2*((y<<1)-3);

-        st -= aa4*(--y);

-        if(y && (aBndA[y] != aBndA[y-1] || aBndZ[y] != aBndZ[y-1]))

-        {

-          this._mkArcDiv(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);

-          ox = x;

-          oy = y;

-        }

-      }

-    }

-    this._mkArcDiv(x, 0, oy, cx, cy, iOdds, aBndA, aBndZ, iSects);

-    if(iOdds >> 16) // Odd height

-    {

-      if(iSects >> 16) // Start-angle > end-angle

-      {

-        var xl = (yEndA <= cy || yEndZ > cy)? (cx - x) : cx;

-        this._mkDiv(xl, cy, x + cx - xl + (iOdds & 0xffff), 1);

-      }

-      else if((iSects & 0x01) && yEndZ > cy)

-        this._mkDiv(cx - x, cy, x, 1);

-    }

-  };

-

-/* fillPolygon method, implemented by Matthieu Haller.

-This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.

-C source of GD 1.8.4 found at http://www.boutell.com/gd/

-

-THANKS to Kirsten Schulz for the polygon fixes!

-

-The intersection finding technique of this code could be improved

-by remembering the previous intertersection, and by using the slope.

-That could help to adjust intersections to produce a nice

-interior_extrema. */

-  this.fillPolygon = function(array_x, array_y)

-  {

-    var i;

-    var y;

-    var miny, maxy;

-    var x1, y1;

-    var x2, y2;

-    var ind1, ind2;

-    var ints;

-

-    var n = array_x.length;

-    if(!n) return;

-

-    miny = array_y[0];

-    maxy = array_y[0];

-    for(i = 1; i < n; i++)

-    {

-      if(array_y[i] < miny)

-        miny = array_y[i];

-

-      if(array_y[i] > maxy)

-        maxy = array_y[i];

-    }

-    for(y = miny; y <= maxy; y++)

-    {

-      var polyInts = new Array();

-      ints = 0;

-      for(i = 0; i < n; i++)

-      {

-        if(!i)

-        {

-          ind1 = n-1;

-          ind2 = 0;

-        }

-        else

-        {

-          ind1 = i-1;

-          ind2 = i;

-        }

-        y1 = array_y[ind1];

-        y2 = array_y[ind2];

-        if(y1 < y2)

-        {

-          x1 = array_x[ind1];

-          x2 = array_x[ind2];

-        }

-        else if(y1 > y2)

-        {

-          y2 = array_y[ind1];

-          y1 = array_y[ind2];

-          x2 = array_x[ind1];

-          x1 = array_x[ind2];

-        }

-        else continue;

-

-         //  Modified 11. 2. 2004 Walter Zorn

-        if((y >= y1) && (y < y2))

-          polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);

-

-        else if((y == maxy) && (y > y1) && (y <= y2))

-          polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);

-      }

-      polyInts.sort(_CompInt);

-      for(i = 0; i < ints; i+=2)

-        this._mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);

-    }

-  };

-

-  this.drawString = function(txt, x, y)

-  {

-    this.htm += '<div style="position:absolute;white-space:nowrap;'+

-      'left:' + x + 'px;'+

-      'top:' + y + 'px;'+

-      'font-family:' +  this.ftFam + ';'+

-      'font-size:' + this.ftSz + ';'+

-      'color:' + this.color + ';' + this.ftSty + '">'+

-      txt +

-      '<\/div>';

-  };

-

-/* drawStringRect() added by Rick Blommers.

-Allows to specify the size of the text rectangle and to align the

-text both horizontally (e.g. right) and vertically within that rectangle */

-  this.drawStringRect = function(txt, x, y, width, halign)

-  {

-    this.htm += '<div style="position:absolute;overflow:hidden;'+

-      'left:' + x + 'px;'+

-      'top:' + y + 'px;'+

-      'width:'+width +'px;'+

-      'text-align:'+halign+';'+

-      'font-family:' +  this.ftFam + ';'+

-      'font-size:' + this.ftSz + ';'+

-      'color:' + this.color + ';' + this.ftSty + '">'+

-      txt +

-      '<\/div>';

-  };

-

-  this.drawImage = function(imgSrc, x, y, w, h, a)

-  {

-    this.htm += '<div style="position:absolute;'+

-      'left:' + x + 'px;'+

-      'top:' + y + 'px;'+

-      // w (width) and h (height) arguments are now optional.

-      // Added by Mahmut Keygubatli, 14.1.2008

-      (w? ('width:' +  w + 'px;') : '') +

-      (h? ('height:' + h + 'px;'):'')+'">'+

-      '<img src="' + imgSrc +'"'+ (w ? (' width="' + w + '"'):'')+ (h ? (' height="' + h + '"'):'') + (a? (' '+a) : '') + '>'+

-      '<\/div>';

-  };

-

-  this.clear = function()

-  {

-    this.htm = "";

-    if(this.cnv) this.cnv.innerHTML = "";

-  };

-

-  this._mkOvQds = function(cx, cy, x, y, w, h, wod, hod)

-  {

-    var xl = cx - x, xr = cx + x + wod - w, yt = cy - y, yb = cy + y + hod - h;

-    if(xr > xl+w)

-    {

-      this._mkDiv(xr, yt, w, h);

-      this._mkDiv(xr, yb, w, h);

-    }

-    else

-      w = xr - xl + w;

-    this._mkDiv(xl, yt, w, h);

-    this._mkDiv(xl, yb, w, h);

-  };

-  

-  this._mkArcDiv = function(x, y, oy, cx, cy, iOdds, aBndA, aBndZ, iSects)

-  {

-    var xrDef = cx + x + (iOdds & 0xffff), y2, h = oy - y, xl, xr, w;

-

-    if(!h) h = 1;

-    x = cx - x;

-

-    if(iSects & 0xff0000) // Start-angle > end-angle

-    {

-      y2 = cy - y - h;

-      if(iSects & 0x00ff)

-      {

-        if(iSects & 0x02)

-        {

-          xl = Math.max(x, aBndZ[y]);

-          w = xrDef - xl;

-          if(w > 0) this._mkDiv(xl, y2, w, h);

-        }

-        if(iSects & 0x01)

-        {

-          xr = Math.min(xrDef, aBndA[y]);

-          w = xr - x;

-          if(w > 0) this._mkDiv(x, y2, w, h);

-        }

-      }

-      else

-        this._mkDiv(x, y2, xrDef - x, h);

-      y2 = cy + y + (iOdds >> 16);

-      if(iSects & 0xff00)

-      {

-        if(iSects & 0x0100)

-        {

-          xl = Math.max(x, aBndA[y]);

-          w = xrDef - xl;

-          if(w > 0) this._mkDiv(xl, y2, w, h);

-        }

-        if(iSects & 0x0200)

-        {

-          xr = Math.min(xrDef, aBndZ[y]);

-          w = xr - x;

-          if(w > 0) this._mkDiv(x, y2, w, h);

-        }

-      }

-      else

-        this._mkDiv(x, y2, xrDef - x, h);

-    }

-    else

-    {

-      if(iSects & 0x00ff)

-      {

-        if(iSects & 0x02)

-          xl = Math.max(x, aBndZ[y]);

-        else

-          xl = x;

-        if(iSects & 0x01)

-          xr = Math.min(xrDef, aBndA[y]);

-        else

-          xr = xrDef;

-        y2 = cy - y - h;

-        w = xr - xl;

-        if(w > 0) this._mkDiv(xl, y2, w, h);

-      }

-      if(iSects & 0xff00)

-      {

-        if(iSects & 0x0100)

-          xl = Math.max(x, aBndA[y]);

-        else

-          xl = x;

-        if(iSects & 0x0200)

-          xr = Math.min(xrDef, aBndZ[y]);

-        else

-          xr = xrDef;

-        y2 = cy + y + (iOdds >> 16);

-        w = xr - xl;

-        if(w > 0) this._mkDiv(xl, y2, w, h);

-      }

-    }

-  };

-

-  this.setStroke(1);

-  this.setFont("verdana,geneva,helvetica,sans-serif", "12px", Font.PLAIN);

-  this.color = "#000000";

-  this.htm = "";

-  this.wnd = wnd || window;

-

-  if(!jg_ok) _chkDHTM();

-  if(jg_ok)

-  {

-    if(cnv)

-    {

-      if(typeof(cnv) == "string")

-        this.cont = document.all? (this.wnd.document.all[cnv] || null)

-          : document.getElementById? (this.wnd.document.getElementById(cnv) || null)

-          : null;

-      else if(cnv == window.document)

-        this.cont = document.getElementsByTagName("body")[0];

-      // If cnv is a direct reference to a canvas DOM node

-      // (option suggested by Andreas Luleich)

-      else this.cont = cnv;

-      // Create new canvas inside container DIV. Thus the drawing and clearing

-      // methods won't interfere with the container's inner html.

-      // Solution suggested by Vladimir.

-      this.cnv = this.wnd.document.createElement("div");

-      this.cnv.style.fontSize=0;

-      this.cont.appendChild(this.cnv);

-      this.paint = jg_dom? _pntCnvDom : _pntCnvIe;

-    }

-    else

-      this.paint = _pntDoc;

-  }

-  else

-    this.paint = _pntN;

-

-  this.setPrintable(false);

-}

-

-function _mkLinVirt(aLin, x1, y1, x2, y2)

-{

-  var dx = Math.abs(x2-x1), dy = Math.abs(y2-y1),

-  x = x1, y = y1,

-  xIncr = (x1 > x2)? -1 : 1,

-  yIncr = (y1 > y2)? -1 : 1,

-  p,

-  i = 0;

-  if(dx >= dy)

-  {

-    var pr = dy<<1,

-    pru = pr - (dx<<1);

-    p = pr-dx;

-    while(dx > 0)

-    {--dx;

-      if(p > 0)    //  Increment y

-      {

-        aLin[i++] = x;

-        y += yIncr;

-        p += pru;

-      }

-      else p += pr;

-      x += xIncr;

-    }

-  }

-  else

-  {

-    var pr = dx<<1,

-    pru = pr - (dy<<1);

-    p = pr-dy;

-    while(dy > 0)

-    {--dy;

-      y += yIncr;

-      aLin[i++] = x;

-      if(p > 0)    //  Increment x

-      {

-        x += xIncr;

-        p += pru;

-      }

-      else p += pr;

-    }

-  }

-  for(var len = aLin.length, i = len-i; i;)

-    aLin[len-(i--)] = x;

-};

-

-function _CompInt(x, y)

-{

-  return(x - y);

-}

-

diff --git a/lib/xmlgen.nim b/lib/xmlgen.nim
new file mode 100644
index 000000000..79a782252
--- /dev/null
+++ b/lib/xmlgen.nim
@@ -0,0 +1,406 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a simple `XML`:idx: and `HTML`:idx: code 
+## generator. Each commonly used HTML tag has a corresponding macro
+## that generates a string with its HTML representation.
+##
+## Example:
+##
+## .. code-block:: nimrod
+##   var nim = "Nimrod"
+##   echo h1(a(href="http://force7.de/nimrod", nim))
+##  
+## Writes the string::
+##   
+##   <h1><a href="http://force7.de/nimrod">Nimrod</a></h1>
+##
+
+import
+  macros, strutils
+
+const
+  coreAttr* = " id class title style "
+  eventAttr* = " onclick ondblclick onmousedown onmouseup " &
+    "onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup "
+  commonAttr* = coreAttr & eventAttr
+
+proc getIdent(e: PNimrodNode): string {.compileTime.} = 
+  case e.kind
+  of nnkIdent: result = normalize($e.ident)
+  of nnkAccQuoted: result = getIdent(e[0])
+  else: error("cannot extract identifier from node: " & toStrLit(e).strVal)
+
+proc delete[T](s: var seq[T], attr: T): bool = 
+  var idx = find(s, attr)
+  if idx >= 0:
+    var L = s.len
+    s[idx] = s[L-1]
+    setLen(s, L-1)
+    result = true
+
+proc xmlCheckedTag*(e: PNimrodNode, tag: string,
+    optAttr = "", reqAttr = "",
+    isLeaf = false): PNimrodNode {.compileTime.} =
+  ## use this procedure to define a new XML tag
+  
+  # copy the attributes; when iterating over them these lists
+  # will be modified, so that each attribute is only given one value
+  var req = splitSeq(reqAttr)
+  var opt = splitSeq(optAttr)
+  result = newNimNode(nnkBracket, e)
+  result.add(newStrLitNode("<"))
+  result.add(newStrLitNode(tag))
+  # first pass over attributes:
+  for i in 1..e.len-1:
+    if e[i].kind == nnkExprEqExpr: 
+      var name = getIdent(e[i][0])
+      if delete(req, name) or delete(opt, name):
+        result.add(newStrLitNode(" "))
+        result.add(newStrLitNode(name))
+        result.add(newStrLitNode("=\""))
+        result.add(e[i][1])
+        result.add(newStrLitNode("\""))
+      else:
+        error("invalid attribute for '" & tag & "' element: " & name)
+  # check each required attribute exists:
+  if req.len > 0:
+    error(req[0] & " attribute for '" & tag & "' element expected")
+  if isLeaf:
+    for i in 1..e.len-1:
+      if e[i].kind != nnkExprEqExpr: 
+        error("element " & tag & " cannot be nested")
+    result.add(newStrLitNode(" />"))
+  else:
+    result.add(newStrLitNode(">"))
+    # second pass over elements:
+    for i in 1..e.len-1:
+      if e[i].kind != nnkExprEqExpr: result.add(e[i])
+    result.add(newStrLitNode("</"))
+    result.add(newStrLitNode(tag))
+    result.add(newStrLitNode(">"))
+  result = NestList(!"&", result)
+
+
+macro a*(e: expr): expr = 
+  ## generates the HTML ``a`` element.
+  result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
+    "accesskey tabindex" & commonAttr)
+
+macro acronym*(e: expr): expr = 
+  ## generates the HTML ``acronym`` element.
+  result = xmlCheckedTag(e, "acronym", commonAttr)
+
+macro address*(e: expr): expr = 
+  ## generates the HTML ``address`` element.
+  result = xmlCheckedTag(e, "address", commonAttr)
+
+macro area*(e: expr): expr = 
+  ## generates the HTML ``area`` element.
+  result = xmlCheckedTag(e, "area", "shape coords href nohref" &
+    " accesskey tabindex" & commonAttr, "alt", true)
+
+macro b*(e: expr): expr = 
+  ## generates the HTML ``b`` element.
+  result = xmlCheckedTag(e, "b", commonAttr)
+
+macro base*(e: expr): expr = 
+  ## generates the HTML ``base`` element.
+  result = xmlCheckedTag(e, "base", "", "href", true)
+
+macro big*(e: expr): expr = 
+  ## generates the HTML ``big`` element.
+  result = xmlCheckedTag(e, "big", commonAttr)
+
+macro blockquote*(e: expr): expr = 
+  ## generates the HTML ``blockquote`` element.
+  result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr)
+
+macro body*(e: expr): expr = 
+  ## generates the HTML ``body`` element.
+  result = xmlCheckedTag(e, "body", commonAttr)
+
+macro br*(e: expr): expr = 
+  ## generates the HTML ``br`` element.
+  result = xmlCheckedTag(e, "br", "", "", true)
+
+macro button*(e: expr): expr = 
+  ## generates the HTML ``button`` element.
+  result = xmlCheckedTag(e, "button", "accesskey tabindex " &
+    "disabled name type value" & commonAttr)
+
+macro caption*(e: expr): expr = 
+  ## generates the HTML ``caption`` element.
+  result = xmlCheckedTag(e, "caption", commonAttr)
+
+macro cite*(e: expr): expr = 
+  ## generates the HTML ``cite`` element.
+  result = xmlCheckedTag(e, "cite", commonAttr)
+
+macro code*(e: expr): expr = 
+  ## generates the HTML ``code`` element.
+  result = xmlCheckedTag(e, "code", commonAttr)
+
+macro col*(e: expr): expr = 
+  ## generates the HTML ``col`` element.
+  result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
+
+macro colgroup*(e: expr): expr = 
+  ## generates the HTML ``colgroup`` element.
+  result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
+
+macro dd*(e: expr): expr = 
+  ## generates the HTML ``dd`` element.
+  result = xmlCheckedTag(e, "dd", commonAttr)
+
+macro del*(e: expr): expr = 
+  ## generates the HTML ``del`` element.
+  result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
+
+macro dfn*(e: expr): expr = 
+  ## generates the HTML ``dfn`` element.
+  result = xmlCheckedTag(e, "dfn", commonAttr)
+
+macro `div`*(e: expr): expr = 
+  ## generates the HTML ``div`` element.
+  result = xmlCheckedTag(e, "div", commonAttr)
+
+macro dl*(e: expr): expr = 
+  ## generates the HTML ``dl`` element.
+  result = xmlCheckedTag(e, "dl", commonAttr)
+
+macro dt*(e: expr): expr = 
+  ## generates the HTML ``dt`` element.
+  result = xmlCheckedTag(e, "dt", commonAttr)
+
+macro em*(e: expr): expr = 
+  ## generates the HTML ``em`` element.
+  result = xmlCheckedTag(e, "em", commonAttr)
+
+macro fieldset*(e: expr): expr = 
+  ## generates the HTML ``fieldset`` element.
+  result = xmlCheckedTag(e, "fieldset", commonAttr)
+
+macro form*(e: expr): expr = 
+  ## generates the HTML ``form`` element.
+  result = xmlCheckedTag(e, "form", "method encype accept accept-charset" & 
+    commonAttr, "action")
+
+macro h1*(e: expr): expr = 
+  ## generates the HTML ``h1`` element.
+  result = xmlCheckedTag(e, "h1", commonAttr)
+
+macro h2*(e: expr): expr = 
+  ## generates the HTML ``h2`` element.
+  result = xmlCheckedTag(e, "h2", commonAttr)
+
+macro h3*(e: expr): expr = 
+  ## generates the HTML ``h3`` element.
+  result = xmlCheckedTag(e, "h3", commonAttr)
+
+macro h4*(e: expr): expr = 
+  ## generates the HTML ``h4`` element.
+  result = xmlCheckedTag(e, "h4", commonAttr)
+
+macro h5*(e: expr): expr = 
+  ## generates the HTML ``h5`` element.
+  result = xmlCheckedTag(e, "h5", commonAttr)
+
+macro h6*(e: expr): expr = 
+  ## generates the HTML ``h6`` element.
+  result = xmlCheckedTag(e, "h6", commonAttr)
+
+macro head*(e: expr): expr = 
+  ## generates the HTML ``head`` element.
+  result = xmlCheckedTag(e, "head", "profile")
+
+macro html*(e: expr): expr = 
+  ## generates the HTML ``html`` element.
+  result = xmlCheckedTag(e, "html", "", "xmlns")
+
+macro hr*(e: expr): expr = 
+  ## generates the HTML ``hr`` element.
+  result = xmlCheckedTag(e, "hr", commonAttr, "", true)
+
+macro i*(e: expr): expr = 
+  ## generates the HTML ``i`` element.
+  result = xmlCheckedTag(e, "i", commonAttr)
+
+macro img*(e: expr): expr = 
+  ## generates the HTML ``img`` element.
+  result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
+
+macro input*(e: expr): expr = 
+  ## generates the HTML ``input`` element.
+  result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
+    " alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
+
+macro ins*(e: expr): expr = 
+  ## generates the HTML ``ins`` element.
+  result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr)
+
+macro kbd*(e: expr): expr = 
+  ## generates the HTML ``kbd`` element.
+  result = xmlCheckedTag(e, "kbd", commonAttr)
+
+macro label*(e: expr): expr = 
+  ## generates the HTML ``label`` element.
+  result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
+
+macro legend*(e: expr): expr = 
+  ## generates the HTML ``legend`` element.
+  result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
+
+macro li*(e: expr): expr = 
+  ## generates the HTML ``li`` element.
+  result = xmlCheckedTag(e, "li", commonAttr)
+
+macro link*(e: expr): expr = 
+  ## generates the HTML ``link`` element.
+  result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" & 
+    commonAttr, "", true)
+
+macro map*(e: expr): expr = 
+  ## generates the HTML ``map`` element.
+  result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
+
+macro meta*(e: expr): expr = 
+  ## generates the HTML ``meta`` element.
+  result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
+
+macro noscript*(e: expr): expr = 
+  ## generates the HTML ``noscript`` element.
+  result = xmlCheckedTag(e, "noscript", commonAttr)
+
+macro `object`*(e: expr): expr = 
+  ## generates the HTML ``object`` element.
+  result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
+    "codetype archive standby width height name tabindex" & commonAttr)
+
+macro ol*(e: expr): expr = 
+  ## generates the HTML ``ol`` element.
+  result = xmlCheckedTag(e, "ol", commonAttr)
+
+macro optgroup*(e: expr): expr = 
+  ## generates the HTML ``optgroup`` element.
+  result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false)
+
+macro option*(e: expr): expr = 
+  ## generates the HTML ``option`` element.
+  result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
+
+macro p*(e: expr): expr = 
+  ## generates the HTML ``p`` element.
+  result = xmlCheckedTag(e, "p", commonAttr)
+
+macro param*(e: expr): expr = 
+  ## generates the HTML ``param`` element.
+  result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
+
+macro pre*(e: expr): expr = 
+  ## generates the HTML ``pre`` element.
+  result = xmlCheckedTag(e, "pre", commonAttr)
+
+macro q*(e: expr): expr = 
+  ## generates the HTML ``q`` element.
+  result = xmlCheckedTag(e, "q", "cite" & commonAttr)
+
+macro samp*(e: expr): expr = 
+  ## generates the HTML ``samp`` element.
+  result = xmlCheckedTag(e, "samp", commonAttr)
+
+macro script*(e: expr): expr = 
+  ## generates the HTML ``script`` element.
+  result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
+
+macro select*(e: expr): expr = 
+  ## generates the HTML ``select`` element.
+  result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" & 
+    commonAttr)
+
+macro small*(e: expr): expr = 
+  ## generates the HTML ``small`` element.
+  result = xmlCheckedTag(e, "small", commonAttr)
+
+macro span*(e: expr): expr = 
+  ## generates the HTML ``span`` element.
+  result = xmlCheckedTag(e, "span", commonAttr)
+
+macro strong*(e: expr): expr = 
+  ## generates the HTML ``strong`` element.
+  result = xmlCheckedTag(e, "strong", commonAttr)
+
+macro style*(e: expr): expr = 
+  ## generates the HTML ``style`` element.
+  result = xmlCheckedTag(e, "style", "media title", "type")
+
+macro sub*(e: expr): expr = 
+  ## generates the HTML ``sub`` element.
+  result = xmlCheckedTag(e, "sub", commonAttr)
+
+macro sup*(e: expr): expr = 
+  ## generates the HTML ``sup`` element.
+  result = xmlCheckedTag(e, "sup", commonAttr)
+
+macro table*(e: expr): expr = 
+  ## generates the HTML ``table`` element.
+  result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
+    " frame rules width" & commonAttr)
+
+macro tbody*(e: expr): expr = 
+  ## generates the HTML ``tbody`` element.
+  result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
+
+macro td*(e: expr): expr = 
+  ## generates the HTML ``td`` element.
+  result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
+    " align valign" & commonAttr)
+
+macro textarea*(e: expr): expr = 
+  ## generates the HTML ``textarea`` element.
+  result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
+    " tabindex" & commonAttr, "rows cols", false)
+
+macro tfoot*(e: expr): expr = 
+  ## generates the HTML ``tfoot`` element.
+  result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
+
+macro th*(e: expr): expr = 
+  ## generates the HTML ``th`` element.
+  result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
+    " align valign" & commonAttr)
+
+macro thead*(e: expr): expr = 
+  ## generates the HTML ``thead`` element.
+  result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
+
+macro title*(e: expr): expr = 
+  ## generates the HTML ``title`` element.
+  result = xmlCheckedTag(e, "title")
+
+macro tr*(e: expr): expr = 
+  ## generates the HTML ``tr`` element.
+  result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
+
+macro tt*(e: expr): expr = 
+  ## generates the HTML ``tt`` element.
+  result = xmlCheckedTag(e, "tt", commonAttr)
+
+macro ul*(e: expr): expr = 
+  ## generates the HTML ``ul`` element.
+  result = xmlCheckedTag(e, "ul", commonAttr)
+
+macro `var`*(e: expr): expr = 
+  ## generates the HTML ``var`` element.
+  result = xmlCheckedTag(e, "var", commonAttr)
+
+when isMainModule:
+  var nim = "Nimrod"
+  echo h1(a(href="http://force7.de/nimrod", nim))
+