summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2010-11-18 22:26:20 +0100
committerAraq <rumpf_a@web.de>2010-11-18 22:26:20 +0100
commitadf13aaea379d482ad4289d349a9d475bc2c06a6 (patch)
tree4c18d447545c35fa7c1f4ce7db566f96db67a080 /lib/pure
parent8ee63f98364259b2d1b6c02d050e0efccecbcf9b (diff)
downloadNim-adf13aaea379d482ad4289d349a9d475bc2c06a6.tar.gz
docgen understands and ignores *when false*
Diffstat (limited to 'lib/pure')
-rwxr-xr-xlib/pure/cgi.nim30
-rw-r--r--lib/pure/cookies.nim30
-rwxr-xr-xlib/pure/httpserver.nim21
-rwxr-xr-xlib/pure/pegs.nim179
-rwxr-xr-xlib/pure/sockets.nim83
5 files changed, 281 insertions, 62 deletions
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 643c67bec..af222caba 100755
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements helper procs for CGI applictions. Example:
+## This module implements helper procs for CGI applications. Example:
 ## 
 ## .. code-block:: Nimrod
 ##
@@ -29,7 +29,7 @@
 ##    writeln(stdout, "your password: " & myData["password"])
 ##    writeln(stdout, "</body></html>")
 
-import strutils, os, strtabs
+import strutils, os, strtabs, cookies
 
 proc URLencode*(s: string): string =
   ## Encodes a value to be HTTP safe: This means that characters in the set
@@ -355,32 +355,16 @@ proc setCookie*(name, value: string) =
   write(stdout, "Set-Cookie: ", name, "=", value, "\n")
 
 var
-  cookies: PStringTable = nil
-
-proc parseCookies(s: string): PStringTable = 
-  result = newStringTable(modeCaseInsensitive)
-  var i = 0
-  while true:
-    while s[i] == ' ' or s[i] == '\t': inc(i)
-    var keystart = i
-    while s[i] != '=' and s[i] != '\0': inc(i)
-    var keyend = i-1
-    if s[i] == '\0': break
-    inc(i) # skip '='
-    var valstart = i
-    while s[i] != ';' and s[i] != '\0': inc(i)
-    result[copy(s, keystart, keyend)] = copy(s, valstart, i-1)
-    if s[i] == '\0': break
-    inc(i) # skip ';'
+  gcookies: PStringTable = nil
     
 proc getCookie*(name: string): string = 
   ## Gets a cookie. If no cookie of `name` exists, "" is returned.
-  if cookies == nil: cookies = parseCookies(getHttpCookie())
-  result = cookies[name]
+  if gcookies == nil: gcookies = parseCookies(getHttpCookie())
+  result = gcookies[name]
 
 proc existsCookie*(name: string): bool = 
   ## Checks if a cookie of `name` exists.
-  if cookies == nil: cookies = parseCookies(getHttpCookie())
-  result = hasKey(cookies, name)
+  if gcookies == nil: gcookies = parseCookies(getHttpCookie())
+  result = hasKey(gcookies, name)
 
 
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
new file mode 100644
index 000000000..eed6c7512
--- /dev/null
+++ b/lib/pure/cookies.nim
@@ -0,0 +1,30 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2010 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements helper procs for parsing Cookies.
+
+import strtabs
+
+proc parseCookies*(s: string): PStringTable = 
+  ## parses cookies into a string table.
+  result = newStringTable(modeCaseInsensitive)
+  var i = 0
+  while true:
+    while s[i] == ' ' or s[i] == '\t': inc(i)
+    var keystart = i
+    while s[i] != '=' and s[i] != '\0': inc(i)
+    var keyend = i-1
+    if s[i] == '\0': break
+    inc(i) # skip '='
+    var valstart = i
+    while s[i] != ';' and s[i] != '\0': inc(i)
+    result[copy(s, keystart, keyend)] = copy(s, valstart, i-1)
+    if s[i] == '\0': break
+    inc(i) # skip ';'
+
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
index bff1b381d..cb36ea541 100755
--- a/lib/pure/httpserver.nim
+++ b/lib/pure/httpserver.nim
@@ -136,9 +136,12 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
   if meth == reqPost:
     # get from client and post to CGI program:
     var buf = alloc(contentLength)
-    if recv(client, buf, contentLength) != contentLength: OSError()
+    if recv(client, buf, contentLength) != contentLength: 
+      dealloc(buf)
+      OSError()
     var inp = process.inputStream
     inp.writeData(inp, buf, contentLength)
+    dealloc(buf)
 
   var outp = process.outputStream
   while running(process) or not outp.atEnd(outp):
@@ -153,10 +156,21 @@ proc acceptRequest(client: TSocket) =
   var query = ""
   var buf = ""
   discard recvLine(client, buf)
+  var path = ""
   var data = buf.split()
   var meth = reqGet
+
+  var q = find(data[1], '?')
+
+  # extract path
+  if q >= 0:
+    # strip "?..." from path, this may be found in both POST and GET
+    path = "." & data[1].copy(0, q-1)
+  else:
+    path = "." & data[1]
+  # path starts with "/", by adding "." in front of it we serve files from cwd
+  
   if cmpIgnoreCase(data[0], "GET") == 0:
-    var q = find(data[1], '?')
     if q >= 0:
       cgi = true
       query = data[1].copy(q+1)
@@ -166,7 +180,6 @@ proc acceptRequest(client: TSocket) =
   else:
     unimplemented(client)
 
-  var path = data[1]
   if path[path.len-1] == '/' or existsDir(path):
     path = path / "index.html"
 
@@ -221,7 +234,7 @@ proc next*(s: var TServer) =
   var buf = ""
   discard recvLine(s.client, buf)
   var data = buf.split()
-  if cmpIgnoreCase(data[0], "GET") == 0:
+  if cmpIgnoreCase(data[0], "GET") == 0 or cmpIgnoreCase(data[0], "POST") == 0:
     var q = find(data[1], '?')
     if q >= 0:
       s.query = data[1].copy(q+1)
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 0a373125d..175713a0c 100755
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -37,6 +37,11 @@ type
     pkAny,              ## any character (.)
     pkAnyRune,          ## any Unicode character (_)
     pkNewLine,          ## CR-LF, LF, CR
+    pkLetter,           ## Unicode letter
+    pkLower,            ## Unicode lower case letter
+    pkUpper,            ## Unicode upper case letter
+    pkTitle,            ## Unicode title character
+    pkWhitespace,       ## Unicode whitespace character
     pkTerminal,
     pkTerminalIgnoreCase,
     pkTerminalIgnoreStyle,
@@ -71,7 +76,7 @@ type
     rule: TNode                   ## the rule that the symbol refers to
   TNode {.final.} = object
     case kind: TPegKind
-    of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine: nil
+    of pkEmpty..pkWhitespace: nil
     of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
     of pkChar, pkGreedyRepChar: ch: char
     of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
@@ -196,6 +201,7 @@ proc `@`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
 
 proc `@@`*(a: TPeg): TPeg {.noSideEffect, rtl, 
                             extern: "npgegsCapturedSearch".} =
+  ## constructs a "captured search" for the PEG `a`
   result.kind = pkCapturedSearch
   result.sons = @[a]
   
@@ -237,6 +243,27 @@ proc newLine*: TPeg {.inline.} =
   ## constructs the PEG `newline`:idx: (``\n``)
   result.kind = pkNewline
 
+proc UnicodeLetter*: TPeg {.inline.} = 
+  ## constructs the PEG ``\letter`` which matches any Unicode letter.
+  result.kind = pkLetter
+  
+proc UnicodeLower*: TPeg {.inline.} = 
+  ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
+  result.kind = pkLower 
+
+proc UnicodeUpper*: TPeg {.inline.} = 
+  ## constructs the PEG ``\upper`` which matches any Unicode lowercase letter.
+  result.kind = pkUpper 
+  
+proc UnicodeTitle*: TPeg {.inline.} = 
+  ## constructs the PEG ``\title`` which matches any Unicode title letter.
+  result.kind = pkTitle
+
+proc UnicodeWhitespace*: TPeg {.inline.} = 
+  ## constructs the PEG ``\white`` which matches any Unicode 
+  ## whitespace character.
+  result.kind = pkWhitespace
+
 proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
   ## constructs a capture with the PEG `a`
   result.kind = pkCapture
@@ -267,8 +294,8 @@ proc spaceCost(n: TPeg): int =
   case n.kind
   of pkEmpty: nil
   of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
-     pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny, pkAnyRune,
-     pkNewLine, pkGreedyAny:
+     pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, 
+     pkAny..pkWhitespace, pkGreedyAny:
     result = 1
   of pkNonTerminal:
     # we cannot inline a rule with a non-terminal
@@ -379,6 +406,12 @@ proc toStrAux(r: TPeg, res: var string) =
   of pkEmpty: add(res, "()")
   of pkAny: add(res, '.')
   of pkAnyRune: add(res, '_')
+  of pkLetter: add(res, "\\letter")
+  of pkLower: add(res, "\\lower")
+  of pkUpper: add(res, "\\upper")
+  of pkTitle: add(res, "\\title")
+  of pkWhitespace: add(res, "\\white")
+
   of pkNewline: add(res, "\\n")
   of pkTerminal: add(res, singleQuoteEsc(r.term))
   of pkTerminalIgnoreCase:
@@ -460,10 +493,15 @@ proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
 # --------------------- core engine -------------------------------------------
 
 type
-  TMatchClosure {.final.} = object
+  TCaptures* {.final.} = object ## contains the captured substrings.
     matches: array[0..maxSubpatterns-1, tuple[first, last: int]]
     ml: int
 
+proc bounds*(c: TCaptures, 
+             i: range[0..maxSubpatterns-1]): tuple[first, last: int] = 
+  ## returns the bounds ``[first..last]`` of the `i`'th capture.
+  result = c.matches[i]
+
 when not useUnicode:
   type
     TRune = char
@@ -472,9 +510,17 @@ when not useUnicode:
     inc(i)
   template runeLenAt(s, i: expr): expr = 1
 
-proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
-  ## this implements a simple PEG interpreter. Thanks to superoperators it
-  ## has competitive performance nevertheless.
+  proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
+  proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
+  proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
+  proc isTitle(a: char): bool {.inline.} = return false
+  proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
+
+proc rawMatch*(s: string, p: TPeg, start: int, c: var TCaptures): int {.
+               nosideEffect, rtl, extern: "npegs$1".} =
+  ## low-level matching proc that implements the PEG interpreter. Use this 
+  ## for maximum efficiency (every other PEG operation ends up calling this
+  ## proc).
   ## Returns -1 if it does not match, else the length of the match
   case p.kind
   of pkEmpty: result = 0 # match of length 0
@@ -486,6 +532,51 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
       result = runeLenAt(s, start)
     else:
       result = -1
+  of pkLetter: 
+    if s[start] != '\0':
+      var a: TRune
+      result = start
+      fastRuneAt(s, result, a)
+      if isAlpha(a): dec(result, start)
+      else: result = -1
+    else:
+      result = -1
+  of pkLower: 
+    if s[start] != '\0':
+      var a: TRune
+      result = start
+      fastRuneAt(s, result, a)
+      if isLower(a): dec(result, start)
+      else: result = -1
+    else:
+      result = -1
+  of pkUpper: 
+    if s[start] != '\0':
+      var a: TRune
+      result = start
+      fastRuneAt(s, result, a)
+      if isUpper(a): dec(result, start)
+      else: result = -1
+    else:
+      result = -1
+  of pkTitle: 
+    if s[start] != '\0':
+      var a: TRune
+      result = start
+      fastRuneAt(s, result, a)
+      if isTitle(a): dec(result, start) 
+      else: result = -1
+    else:
+      result = -1
+  of pkWhitespace: 
+    if s[start] != '\0':
+      var a: TRune
+      result = start
+      fastRuneAt(s, result, a)
+      if isWhitespace(a): dec(result, start)
+      else: result = -1
+    else:
+      result = -1
   of pkGreedyAny:
     result = len(s) - start
   of pkNewLine:
@@ -537,14 +628,14 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
   of pkNonTerminal:
     var oldMl = c.ml
     when false: echo "enter: ", p.nt.name
-    result = m(s, p.nt.rule, start, c)
+    result = rawMatch(s, p.nt.rule, start, c)
     when false: echo "leave: ", p.nt.name
     if result < 0: c.ml = oldMl
   of pkSequence:
     var oldMl = c.ml  
     result = 0
     for i in 0..high(p.sons):
-      var x = m(s, p.sons[i], start+result, c)
+      var x = rawMatch(s, p.sons[i], start+result, c)
       if x < 0:
         c.ml = oldMl
         result = -1
@@ -553,14 +644,14 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
   of pkOrderedChoice:
     var oldMl = c.ml
     for i in 0..high(p.sons):
-      result = m(s, p.sons[i], start, c)
+      result = rawMatch(s, p.sons[i], start, c)
       if result >= 0: break
       c.ml = oldMl
   of pkSearch:
     var oldMl = c.ml
     result = 0
     while start+result < s.len:
-      var x = m(s, p.sons[0], start+result, c)
+      var x = rawMatch(s, p.sons[0], start+result, c)
       if x >= 0:
         inc(result, x)
         return
@@ -572,7 +663,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
     inc(c.ml)
     result = 0
     while start+result < s.len:
-      var x = m(s, p.sons[0], start+result, c)
+      var x = rawMatch(s, p.sons[0], start+result, c)
       if x >= 0:
         if idx < maxSubpatterns:
           c.matches[idx] = (start, start+result-1)
@@ -585,7 +676,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
   of pkGreedyRep:
     result = 0
     while true:
-      var x = m(s, p.sons[0], start+result, c)
+      var x = rawMatch(s, p.sons[0], start+result, c)
       # if x == 0, we have an endless loop; so the correct behaviour would be
       # not to break. But endless loops can be easily introduced:
       # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
@@ -600,15 +691,15 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
     result = 0
     while contains(p.charChoice^, s[start+result]): inc(result)
   of pkOption:
-    result = max(0, m(s, p.sons[0], start, c))
+    result = max(0, rawMatch(s, p.sons[0], start, c))
   of pkAndPredicate:
     var oldMl = c.ml
-    result = m(s, p.sons[0], start, c)
+    result = rawMatch(s, p.sons[0], start, c)
     if result >= 0: result = 0 # do not consume anything
     else: c.ml = oldMl
   of pkNotPredicate:
     var oldMl = c.ml
-    result = m(s, p.sons[0], start, c)
+    result = rawMatch(s, p.sons[0], start, c)
     if result < 0: result = 0
     else:
       c.ml = oldMl
@@ -616,7 +707,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
   of pkCapture:
     var idx = c.ml # reserve a slot for the subpattern
     inc(c.ml)
-    result = m(s, p.sons[0], start, c)
+    result = rawMatch(s, p.sons[0], start, c)
     if result >= 0:
       if idx < maxSubpatterns:
         c.matches[idx] = (start, start+result-1)
@@ -629,7 +720,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
     var n: TPeg
     n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef)) 
     n.term = s.copy(a, b)
-    result = m(s, n, start, c)
+    result = rawMatch(s, n, start, c)
   of pkRule, pkList: assert false
 
 proc match*(s: string, pattern: TPeg, matches: var openarray[string],
@@ -638,8 +729,8 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
   ## the captured substrings in the array ``matches``. If it does not
   ## match, nothing is written into ``matches`` and ``false`` is
   ## returned.
-  var c: TMatchClosure
-  result = m(s, pattern, start, c) == len(s) -start
+  var c: TCaptures
+  result = rawMatch(s, pattern, start, c) == len(s) -start
   if result:
     for i in 0..c.ml-1:
       matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
@@ -647,8 +738,8 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
 proc match*(s: string, pattern: TPeg, 
             start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
   ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
-  var c: TMatchClosure
-  result = m(s, pattern, start, c) == len(s)-start
+  var c: TCaptures
+  result = rawMatch(s, pattern, start, c) == len(s)-start
 
 proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
                start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
@@ -656,8 +747,8 @@ proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
   ## if there is no match, -1 is returned. Note that a match length
   ## of zero can happen. It's possible that a suffix of `s` remains
   ## that does not belong to the match.
-  var c: TMatchClosure
-  result = m(s, pattern, start, c)
+  var c: TCaptures
+  result = rawMatch(s, pattern, start, c)
   if result >= 0:
     for i in 0..c.ml-1:
       matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
@@ -668,8 +759,8 @@ proc matchLen*(s: string, pattern: TPeg,
   ## if there is no match, -1 is returned. Note that a match length
   ## of zero can happen. It's possible that a suffix of `s` remains
   ## that does not belong to the match.
-  var c: TMatchClosure
-  result = m(s, pattern, start, c)
+  var c: TCaptures
+  result = rawMatch(s, pattern, start, c)
 
 proc find*(s: string, pattern: TPeg, matches: var openarray[string],
            start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
@@ -681,6 +772,18 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
   return -1
   # could also use the pattern here: (!P .)* P
   
+proc findBounds*(s: string, pattern: TPeg, matches: var openarray[string],
+                 start = 0): tuple[first, last: int] {.
+                 nosideEffect, rtl, extern: "npegs$1Capture".} =
+  ## returns the starting position and end position of ``pattern`` in ``s`` 
+  ## and the captured
+  ## substrings in the array ``matches``. If it does not match, nothing
+  ## is written into ``matches`` and (-1,0) is returned.
+  for i in start .. s.len-1:
+    var L = matchLen(s, pattern, matches, i)
+    if L >= 0: return (i, i+L-1)
+  return (-1, 0)
+  
 proc find*(s: string, pattern: TPeg, 
            start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
   ## returns the starting position of ``pattern`` in ``s``. If it does not
@@ -1351,6 +1454,11 @@ proc primary(p: var TPegParser): TPeg =
     of "a": result = charset({'a'..'z', 'A'..'Z'})
     of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
     of "ident": result = pegs.ident
+    of "letter": result = UnicodeLetter()
+    of "upper": result = UnicodeUpper()
+    of "lower": result = UnicodeLower()
+    of "title": result = UnicodeTitle()
+    of "white": result = UnicodeWhitespace()
     else: pegError(p, "unknown built-in: " & p.tok.literal)
     getTok(p)
   of tkEscaped:
@@ -1439,9 +1547,12 @@ proc rawParse(p: var TPegParser): TPeg =
     elif ntUsed notin nt.flags and i > 0:
       pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
 
-proc parsePeg*(input: string, filename = "pattern", line = 1, col = 0): TPeg =
+proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): TPeg =
+  ## constructs a TPeg object from `pattern`. `filename`, `line`, `col` are
+  ## used for error messages, but they only provide start offsets. `parsePeg`
+  ## keeps track of line and column numbers within `pattern`.
   var p: TPegParser
-  init(TPegLexer(p), input, filename, line, col)
+  init(TPegLexer(p), pattern, filename, line, col)
   p.tok.kind = tkInvalid
   p.tok.modifier = modNone
   p.tok.literal = ""
@@ -1505,9 +1616,9 @@ when isMainModule:
   expr.rule = sequence(capture(ident), *sequence(
                 nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
   
-  var c: TMatchClosure
+  var c: TCaptures
   var s = "a+b +  c +d+e+f"
-  assert m(s, expr.rule, 0, c) == len(s)
+  assert rawMatch(s, expr.rule, 0, c) == len(s)
   var a = ""
   for i in 0..c.ml-1:
     a.add(copy(s, c.matches[i][0], c.matches[i][1]))
@@ -1559,4 +1670,10 @@ when isMainModule:
   else:
     assert false
   
-
+  assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
+  # ß is not a lower cased letter?!
+  assert match("eine übersicht und auerdem", peg"(\lower \white*)+")
+  assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
+  assert(not match("456678", peg"(\letter)+"))
+  
+  
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index 85628db78..add41afd6 100755
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -9,7 +9,7 @@
 
 ## This module implements a simple portable type-safe sockets layer.
 
-import os
+import os, parseutils
 
 when defined(Windows):
   import winlean
@@ -146,18 +146,66 @@ proc listen*(socket: TSocket, attempts = 5) =
   ## listens to socket.
   if listen(cint(socket), cint(attempts)) < 0'i32: OSError()
 
-proc bindAddr*(socket: TSocket, port = TPort(0)) =
-  ## binds a port number to a socket.
+proc invalidIp4(s: string) {.noreturn, noinline.} =
+  raise newException(EInvalidValue, "invalid ip4 address: " & s)
+
+proc parseIp4*(s: string): int32 = 
+  ## parses an IP version 4 in dotted decimal form like "a.b.c.d".
+  ## Raises EInvalidValue in case of an error.
+  var a, b, c, d: int
+  var i = 0
+  var j = parseInt(s, a, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, b, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, c, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] == '.': inc(i)
+  else: invalidIp4(s)
+  j = parseInt(s, d, i)
+  if j <= 0: invalidIp4(s)
+  inc(i, j)
+  if s[i] != '\0': invalidIp4(s)
+  result = int32(a shl 24 or b shl 16 or c shl 8 or d)
+
+proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
+  ## binds an address/port number to a socket.
+  ## Use address string in dotted decimal form like "a.b.c.d"
+  ## or leave "" for any address.
   var name: Tsockaddr_in
   when defined(Windows):
     name.sin_family = int16(ord(AF_INET))
   else:
     name.sin_family = posix.AF_INET
   name.sin_port = sockets.htons(int16(port))
-  name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
+  if address == "":
+    name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
+  else:
+    name.sin_addr.s_addr = parseIp4(address)
   if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)),
                 sizeof(name)) < 0'i32:
     OSError()
+
+when false:
+  proc bindAddr*(socket: TSocket, port = TPort(0)) =
+    ## binds a port number to a socket.
+    var name: Tsockaddr_in
+    when defined(Windows):
+      name.sin_family = int16(ord(AF_INET))
+    else:
+      name.sin_family = posix.AF_INET
+    name.sin_port = sockets.htons(int16(port))
+    name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
+    if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)),
+                  sizeof(name)) < 0'i32:
+      OSError()
   
 proc getSockName*(socket: TSocket): TPort = 
   ## returns the socket's associated port number.
@@ -410,6 +458,33 @@ proc send*(socket: TSocket, data: string) =
   if send(socket, cstring(data), data.len) != data.len: OSError()
 
 when defined(Windows):
+  const 
+    SOCKET_ERROR = -1
+    IOCPARM_MASK = 127
+    IOC_IN = int(-2147483648)
+    FIONBIO = int(IOC_IN or ((sizeof(int) and IOCPARM_MASK) shl 16) or 
+                             (102 shl 8) or 126)
+
+  proc ioctlsocket(s: TWinSocket, cmd: clong, 
+                   argptr: ptr clong): cint {.
+                   stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
+
+proc setBlocking*(s: TSocket, blocking: bool) =
+  ## sets blocking mode on socket
+  when defined(Windows):
+    var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
+    if SOCKET_ERROR == ioctlsocket(TWinSocket(s), FIONBIO, addr(mode)):
+      OSError()
+  else: # BSD sockets
+    var x: int = fcntl(cint(s), F_GETFL, 0)
+    if x == -1:
+      OSError()
+    else:
+      var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
+      if fcntl(cint(s), F_SETFL, mode) == -1:
+        OSError()
+
+when defined(Windows):
   var wsa: TWSADATA
   if WSAStartup(0x0101'i16, wsa) != 0: OSError()