summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@googlemail.com>2013-06-25 20:10:28 +0100
committerDominik Picheta <dominikpicheta@googlemail.com>2013-06-25 20:10:28 +0100
commitbacb20a379a7ce607bc53f9c917ac675579569a8 (patch)
tree404f0aca1a1b1150261cd28b8fbebbf6283a04c9
parent9540a93d9542d9d1fed4f5a149156ba43da9be80 (diff)
downloadNim-bacb20a379a7ce607bc53f9c917ac675579569a8.tar.gz
Deprecated OSError, and modified it to require an explicit OS error code.
The deprecated functions include the OSError and OSErrorMsg, the name did not change however the signature of the new functions did. They now require a TOSErrorCode value be passed to them. This value can be retrieved using OSLastError. The reason this was done is because on Windows any win api call can reset the last error code to 0, this change allows the user to immediately grab the error code and worry about the string representation later if needs be.
-rw-r--r--lib/pure/asyncio.nim7
-rw-r--r--lib/pure/os.nim174
-rw-r--r--lib/pure/osproc.nim5
-rw-r--r--lib/pure/sockets.nim134
-rw-r--r--lib/windows/winlean.nim2
5 files changed, 196 insertions, 126 deletions
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index a1ce75af3..4ff6e0ced 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -167,7 +167,7 @@ proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
   result = newAsyncSocket()
   result.socket = socket(domain, typ, protocol, buffered)
   result.proto = protocol
-  if result.socket == InvalidSocket: OSError()
+  if result.socket == InvalidSocket: OSError(OSLastError())
   result.socket.setBlocking(false)
 
 proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
@@ -349,7 +349,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
     client.sslNeedAccept = false
     client.info = SockConnected
 
-  if c == InvalidSocket: OSError()
+  if c == InvalidSocket: SocketError(server.socket)
   c.setBlocking(false) # TODO: Needs to be tested.
   
   # deleg.open is set in ``toDelegate``.
@@ -642,8 +642,7 @@ when isMainModule:
   proc testRead(s: PAsyncSocket, no: int) =
     echo("Reading! " & $no)
     var data = ""
-    if not s.readLine(data):
-      OSError()
+    if not s.readLine(data): return
     if data == "":
       echo("Closing connection. " & $no)
       s.close()
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index de4893f8c..eaa22d351 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -39,6 +39,8 @@ type
   FWriteDir* = object of FWriteIO ## effect that denotes a write operation to
                                   ## the directory structure
 
+  TOSErrorCode* = distinct int32 ## Specifies an OS Error Code.
+
 const
   doslike = defined(windows) or defined(OS2) or defined(DOS)
     # DOS-like filesystem
@@ -171,10 +173,13 @@ const
     ## The character which separates the base filename from the extension;
     ## for example, the '.' in ``os.nim``.
 
-proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
+proc OSErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
   ## Retrieves the operating system's error flag, ``errno``.
   ## On Windows ``GetLastError`` is checked before ``errno``.
   ## Returns "" if no error occured.
+  ##
+  ## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc.
+  
   result = ""
   when defined(Windows):
     var err = GetLastError()
@@ -194,17 +199,89 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
   if errno != 0'i32:
     result = $os.strerror(errno)
 
-proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
+{.push warning[deprecated]: off.}
+proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
   ## raises an EOS exception with the given message ``msg``.
   ## If ``msg == ""``, the operating system's error flag
   ## (``errno``) is converted to a readable error message. On Windows
   ## ``GetLastError`` is checked before ``errno``.
   ## If no error flag is set, the message ``unknown OS error`` is used.
+  ##
+  ## **Deprecated since version 0.9.4**: use the other ``OSError`` proc.
   if len(msg) == 0:
     var m = OSErrorMsg()
     raise newException(EOS, if m.len > 0: m else: "unknown OS error")
   else:
     raise newException(EOS, msg)
+{.pop.}
+
+proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
+proc `$`*(err: TOSErrorCode): string {.borrow.}
+
+proc OSErrorMsg*(errorCode: TOSErrorCode): string =
+  ## Converts an OS error code into a human readable string.
+  ##
+  ## The error code can be retrieved using the ``OSLastError`` proc.
+  ##
+  ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
+  ## returned.
+  ##
+  ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
+  ## make this procedure use the non-unicode Win API calls to retrieve the
+  ## message.
+  result = ""
+  when defined(Windows):
+    if errorCode != TOSErrorCode(0'i32):
+      when useWinUnicode:
+        var msgbuf: widecstring
+        if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: LocalFree(cast[pointer](msgbuf))
+      else:
+        var msgbuf: cstring
+        if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
+                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: LocalFree(msgbuf)
+  else:
+    if errorCode != TOSErrorCode(0'i32):
+      result = $os.strerror(errorCode.int32)
+
+proc OSError*(errorCode: TOSErrorCode) =
+  ## Raises an ``EOS`` exception. The ``errorCode`` will determine the
+  ## message, ``OSErrorMsg`` will be used to get this message.
+  ##
+  ## The error code can be retrieved using the ``OSLastError`` proc.
+  ##
+  ## If the error code is ``0`` or an error message could not be retrieved,
+  ## the message ``unknown OS error`` will be used.
+  let msg = OSErrorMsg(errorCode)
+  if msg == "":
+    raise newException(EOS, "unknown OS error")
+  else:
+    raise newException(EOS, msg)
+
+{.push stackTrace:off.}
+proc OSLastError*(): TOSErrorCode =
+  ## Retrieves the last operating system error code.
+  ##
+  ## This procedure is useful in the event when an OS call fails. In that case
+  ## this procedure will return the error code describing the reason why the
+  ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
+  ## this code into a string.
+  ##
+  ## **Warning**:
+  ## The behaviour of this procedure varies between Windows and POSIX systems.
+  ## On Windows some OS calls can reset the error code to ``0`` causing this
+  ## procedure to return ``0``. It is therefore advised to call this procedure
+  ## immediately after an OS call fails. On POSIX systems this is not a problem.
+  
+  when defined(windows):
+    result = TOSErrorCode(GetLastError())
+  else:
+    result = TOSErrorCode(errno)
+{.pop.}
 
 proc UnixToNativePath*(path: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
@@ -311,12 +388,12 @@ proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last modification time.
   when defined(posix):
     var res: TStat
-    if stat(file, res) < 0'i32: OSError()
+    if stat(file, res) < 0'i32: OSError(OSLastError())
     return res.st_mtime
   else:
     var f: TWIN32_Find_Data
     var h = findfirstFile(file, f)
-    if h == -1'i32: OSError()
+    if h == -1'i32: OSError(OSLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
     findclose(h)
 
@@ -324,12 +401,12 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last read or write access time.
   when defined(posix):
     var res: TStat
-    if stat(file, res) < 0'i32: OSError()
+    if stat(file, res) < 0'i32: OSError(OSLastError())
     return res.st_atime
   else:
     var f: TWIN32_Find_Data
     var h = findfirstFile(file, f)
-    if h == -1'i32: OSError()
+    if h == -1'i32: OSError(OSLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
     findclose(h)
 
@@ -337,12 +414,12 @@ proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s creation time.
   when defined(posix):
     var res: TStat
-    if stat(file, res) < 0'i32: OSError()
+    if stat(file, res) < 0'i32: OSError(OSLastError())
     return res.st_ctime
   else:
     var f: TWIN32_Find_Data
     var h = findfirstFile(file, f)
-    if h == -1'i32: OSError()
+    if h == -1'i32: OSError(OSLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
     findclose(h)
 
@@ -358,30 +435,30 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
     when useWinUnicode:
       var res = newWideCString("", bufsize)
       var L = GetCurrentDirectoryW(bufsize, res)
-      if L == 0'i32: OSError()
+      if L == 0'i32: OSError(OSLastError())
       result = res$L
     else:
       result = newString(bufsize)
       var L = GetCurrentDirectoryA(bufsize, result)
-      if L == 0'i32: OSError()
+      if L == 0'i32: OSError(OSLastError())
       setLen(result, L)
   else:
     result = newString(bufsize)
     if getcwd(result, bufsize) != nil:
       setlen(result, c_strlen(result))
     else:
-      OSError()
+      OSError(OSLastError())
 
 proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
   ## Sets the `current working directory`:idx:; `EOS` is raised if
   ## `newDir` cannot been set.
   when defined(Windows):
     when useWinUnicode:
-      if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError()
+      if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError())
     else:
-      if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
+      if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
   else:
-    if chdir(newDir) != 0'i32: OSError()
+    if chdir(newDir) != 0'i32: OSError(OSLastError())
 
 proc JoinPath*(head, tail: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
@@ -571,23 +648,23 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
       var res = newWideCString("", bufsize div 2)
       var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
       if L <= 0'i32 or L >= bufsize: 
-        OSError()
+        OSError(OSLastError())
       result = res$L
     else:
       var unused: cstring
       result = newString(bufsize)
       var L = GetFullPathNameA(filename, bufsize, result, unused)
-      if L <= 0'i32 or L >= bufsize: OSError()
+      if L <= 0'i32 or L >= bufsize: OSError(OSLastError())
       setLen(result, L)
   elif defined(macosx) or defined(bsd):
     # On Mac OS X 10.5, realpath does not allocate the buffer on its own
     var pathBuffer: cstring = newString(pathMax)
     var resultBuffer = realpath(filename, pathBuffer)
-    if resultBuffer == nil: OSError()
+    if resultBuffer == nil: OSError(OSLastError())
     result = $resultBuffer
   else:
     var res = realpath(filename, nil)
-    if res == nil: OSError()
+    if res == nil: OSError(OSLastError())
     result = $res
     c_free(res)
  
@@ -677,6 +754,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
       var f1 = OpenHandle(path1)
       var f2 = OpenHandle(path2)
 
+    var lastErr: TOSErrorCode
     if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
       var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
 
@@ -685,17 +763,21 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
         result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
                  fi1.nFileIndexHigh == fi2.nFileIndexHigh and
                  fi1.nFileIndexLow == fi2.nFileIndexLow
-      else: success = false
-    else: success = false
+      else:
+        lastErr = OSLastError()
+        success = false
+    else:
+      lastErr = OSLastError()
+      success = false
 
     discard CloseHandle(f1)
     discard CloseHandle(f2)
 
-    if not success: OSError()
+    if not success: OSError(lastErr)
   else:
     var a, b: TStat
     if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
-      OSError()
+      OSError(OSLastError())
     else:
       result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
 
@@ -738,17 +820,17 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
     when useWinUnicode:
       let s = newWideCString(source)
       let d = newWideCString(dest)
-      if CopyFileW(s, d, 0'i32) == 0'i32: OSError()
+      if CopyFileW(s, d, 0'i32) == 0'i32: OSError(OSLastError())
     else:
-      if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
+      if CopyFileA(source, dest, 0'i32) == 0'i32: OSError(OSLastError())
   else:
     # generic version of copyFile which works for any platform:
     const bufSize = 8000 # better for memory manager
     var d, s: TFile
-    if not open(s, source): OSError()
+    if not open(s, source): OSError(OSLastError())
     if not open(d, dest, fmWrite):
       close(s)
-      OSError()
+      OSError(OSLastError())
     var buf = alloc(bufsize)
     while True:
       var bytesread = readBuffer(s, buf, bufsize)
@@ -758,7 +840,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
           dealloc(buf)
           close(s)
           close(d)
-          OSError()
+          OSError(OSLastError())
       if bytesread != bufSize: break
     dealloc(buf)
     close(s)
@@ -767,7 +849,8 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
 proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", 
   tags: [FReadIO, FWriteIO].} =
   ## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
-  if crename(source, dest) != 0'i32: OSError()
+  if crename(source, dest) != 0'i32:
+    raise newException(EOS, $strerror(errno))
 
 when not defined(ENOENT):
   var ENOENT {.importc, header: "<errno.h>".}: cint
@@ -775,7 +858,8 @@ when not defined(ENOENT):
 proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
   ## Removes the `file`. If this fails, `EOS` is raised. This does not fail
   ## if the file never existed in the first place.
-  if cremove(file) != 0'i32 and errno != ENOENT: OSError()
+  if cremove(file) != 0'i32 and errno != ENOENT:
+    raise newException(EOS, $strerror(errno))
 
 proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", 
   tags: [FExecIO].} =
@@ -907,14 +991,14 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
     indx = high(environment)
   when defined(unix):
     if cputenv(environment[indx]) != 0'i32:
-      OSError()
+      OSError(OSLastError())
   else:
     when useWinUnicode:
       var k = newWideCString(key)
       var v = newWideCString(val)
-      if SetEnvironmentVariableW(k, v) == 0'i32: OSError()
+      if SetEnvironmentVariableW(k, v) == 0'i32: OSError(OSLastError())
     else:
-      if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
+      if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError())
 
 iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
   ## Iterate over all `environments variables`:idx:. In the first component 
@@ -1044,10 +1128,12 @@ proc rawRemoveDir(dir: string) =
       wrapUnary(res, RemoveDirectoryW, dir)
     else:
       var res = RemoveDirectoryA(dir)
-    if res == 0'i32 and GetLastError() != 3'i32 and 
-        GetLastError() != 18'i32: OSError()
+    let lastError = OSLastError()
+    if res == 0'i32 and lastError.int32 != 3'i32 and 
+        lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
+      OSError(lastError)
   else:
-    if rmdir(dir) != 0'i32 and errno != ENOENT: OSError()
+    if rmdir(dir) != 0'i32 and errno != ENOENT: OSError(OSLastError())
 
 proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
   FWriteDir, FReadDir].} =
@@ -1065,14 +1151,14 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
 proc rawCreateDir(dir: string) =
   when defined(unix):
     if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
-      OSError()
+      OSError(OSLastError())
   else:
     when useWinUnicode:
       wrapUnary(res, CreateDirectoryW, dir)
     else:
       var res = CreateDirectoryA(dir)
     if res == 0'i32 and GetLastError() != 183'i32:
-      OSError()
+      OSError(OSLastError())
 
 proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
   ## Creates the `directory`:idx: `dir`.
@@ -1213,7 +1299,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
   ## permission is available in any case.
   when defined(posix):
     var a: TStat
-    if stat(filename, a) < 0'i32: OSError()
+    if stat(filename, a) < 0'i32: OSError(OSLastError())
     result = {}
     if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
     if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite)
@@ -1231,7 +1317,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
       wrapUnary(res, GetFileAttributesW, filename)
     else:
       var res = GetFileAttributesA(filename)
-    if res == -1'i32: OSError()
+    if res == -1'i32: OSError(OSLastError())
     if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
       result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, 
                 fpOthersExec, fpOthersRead}
@@ -1257,13 +1343,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
     if fpOthersWrite in permissions: p = p or S_IWOTH
     if fpOthersExec in permissions: p = p or S_IXOTH
     
-    if chmod(filename, p) != 0: OSError()
+    if chmod(filename, p) != 0: OSError(OSLastError())
   else:
     when useWinUnicode:
       wrapUnary(res, GetFileAttributesW, filename)
     else:
       var res = GetFileAttributesA(filename)
-    if res == -1'i32: OSError()
+    if res == -1'i32: OSError(OSLastError())
     if fpUserWrite in permissions: 
       res = res and not FILE_ATTRIBUTE_READONLY
     else:
@@ -1272,7 +1358,7 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
       wrapBinary(res2, SetFileAttributesW, filename, res)
     else:
       var res2 = SetFileAttributesA(filename, res)
-    if res2 == - 1'i32: OSError()
+    if res2 == - 1'i32: OSError(OSLastError())
   
 proc inclFilePermissions*(filename: string, 
                           permissions: set[TFilePermission]) {.
@@ -1448,7 +1534,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
   when defined(windows):
     var a: TWin32FindData
     var resA = findfirstFile(file, a)
-    if resA == -1: OSError()
+    if resA == -1: OSError(OSLastError())
     result = rdFileSize(a)
     findclose(resA)
   else:
@@ -1456,7 +1542,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
     if open(f, file): 
       result = getFileSize(f)
       close(f)
-    else: OSError()
+    else: OSError(OSLastError())
 
 proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} = 
   ## Searches for `exe` in the current working directory and then
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index aa4fbe32d..48a559c4e 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -370,6 +370,7 @@ when defined(Windows) and not defined(useNimRtl):
     else:
       success = winlean.CreateProcessA(nil,
         cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
+    let lastError = OSLastError()
 
     if poParentStreams notin options:
       FileClose(si.hStdInput)
@@ -379,7 +380,7 @@ when defined(Windows) and not defined(useNimRtl):
 
     if e != nil: dealloc(e)
     dealloc(cmdl)
-    if success == 0: OSError()
+    if success == 0: OSError(lastError)
     # Close the handle now so anyone waiting is woken:
     discard closeHandle(procInfo.hThread)
     result.FProcessHandle = procInfo.hProcess
@@ -450,7 +451,7 @@ when defined(Windows) and not defined(useNimRtl):
       var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
         NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
     if res == 0:
-      OSError()
+      OSError(OSLastError())
     else:
       Process = ProcInfo.hProcess
       discard CloseHandle(ProcInfo.hThread)
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index 603fd612b..33df72d38 100644
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -231,7 +231,7 @@ when defined(ssl):
     if err == 0:
       raise newException(ESSL, "No error reported.")
     if err == -1:
-      OSError()
+      OSError(OSLastError())
     var errStr = ErrErrorString(err, nil)
     raise newException(ESSL, $errStr)
 
@@ -347,24 +347,23 @@ proc SocketError*(socket: TSocket, err: int = -1, async = false) =
         else: SSLError("Unknown Error")
   
   if err == -1 and not (when defined(ssl): socket.isSSL else: false):
+    let lastError = OSLastError()
     if async:
       when defined(windows):
-        # TODO: Test on Windows
-        var err = WSAGetLastError()
-        if err == WSAEWOULDBLOCK:
+        if lastError.int32 == WSAEWOULDBLOCK:
           return
-        else: OSError()
+        else: OSError(lastError)
       else:
-        if errno == EAGAIN or errno == EWOULDBLOCK:
+        if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
           return
-        else: OSError()
-    else: OSError()
+        else: OSError(lastError)
+    else: OSError(lastError)
 
 proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
   ## Marks ``socket`` as accepting connections. 
   ## ``Backlog`` specifies the maximum length of the 
   ## queue of pending connections.
-  if listen(socket.fd, cint(backlog)) < 0'i32: OSError()
+  if listen(socket.fd, cint(backlog)) < 0'i32: OSError(OSLastError())
 
 proc invalidIp4(s: string) {.noreturn, noinline.} =
   raise newException(EInvalidValue, "invalid ip4 address: " & s)
@@ -403,7 +402,7 @@ template gaiNim(a, p, h, list: expr): stmt =
     var gaiResult = getAddrInfo(a, $p, addr(h), list)
     if gaiResult != 0'i32:
       when defined(windows):
-        OSError()
+        OSError(OSLastError())
       else:
         OSError($gai_strerror(gaiResult))
 
@@ -423,7 +422,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
     name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
     if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
                   sizeof(name).TSockLen) < 0'i32:
-      OSError()
+      OSError(OSLastError())
   else:
     var hints: TAddrInfo
     var aiList: ptr TAddrInfo = nil
@@ -432,21 +431,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
     hints.ai_protocol = toInt(IPPROTO_TCP)
     gaiNim(address, port, hints, aiList)
     if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 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).TSockLen) < 0'i32:
-      OSError()
+      OSError(OSLastError())
   
 proc getSockName*(socket: TSocket): TPort = 
   ## returns the socket's associated port number.
@@ -460,7 +445,7 @@ proc getSockName*(socket: TSocket): TPort =
   var namelen = sizeof(name).TSockLen
   if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
                  addr(namelen)) == -1'i32:
-    OSError()
+    OSError(OSLastError())
   result = TPort(sockets.ntohs(name.sin_port))
 
 template acceptAddrPlain(noClientRet, successRet: expr, 
@@ -472,26 +457,25 @@ template acceptAddrPlain(noClientRet, successRet: expr,
                     addr(addrLen))
   
   if sock < 0:
-    # TODO: Test on Windows.
+    let err = OSLastError()
     when defined(windows):
-      var err = WSAGetLastError()
-      if err == WSAEINPROGRESS:
+      if err.int32 == WSAEINPROGRESS:
         client = InvalidSocket
         address = ""
         when noClientRet.int == -1:
           return
         else:
           return noClientRet
-      else: OSError()
+      else: OSError(err)
     else:
-      if errno == EAGAIN or errno == EWOULDBLOCK:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
         client = InvalidSocket
         address = ""
         when noClientRet.int == -1:
           return
         else:
           return noClientRet
-      else: OSError()
+      else: OSError(err)
   else:
     client.fd = sock
     client.isBuffered = server.isBuffered
@@ -644,7 +628,7 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
     var s = winlean.getservbyname(name, proto)
   else:
     var s = posix.getservbyname(name, proto)
-  if s == nil: OSError()
+  if s == nil: OSError(OSLastError())
   result.name = $s.s_name
   result.aliases = cstringArrayToSeq(s.s_aliases)
   result.port = TPort(s.s_port)
@@ -656,7 +640,7 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
     var s = winlean.getservbyport(ze(int16(port)).cint, proto)
   else:
     var s = posix.getservbyport(ze(int16(port)).cint, proto)
-  if s == nil: OSError()
+  if s == nil: OSError(OSLastError())
   result.name = $s.s_name
   result.aliases = cstringArrayToSeq(s.s_aliases)
   result.port = TPort(s.s_port)
@@ -670,7 +654,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
   when defined(windows):
     var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
                                   cint(sockets.AF_INET))
-    if s == nil: OSError()
+    if s == nil: OSError(OSLastError())
   else:
     var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen, 
                                 cint(posix.AF_INET))
@@ -687,7 +671,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
     elif s.h_addrtype == posix.AF_INET6:
       result.addrType = AF_INET6
     else:
-      OSError("unknown h_addrtype")
+      raise newException(EOS, "unknown h_addrtype")
   result.addrList = cstringArrayToSeq(s.h_addr_list)
   result.length = int(s.h_length)
 
@@ -697,7 +681,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
     var s = winlean.gethostbyname(name)
   else:
     var s = posix.gethostbyname(name)
-  if s == nil: OSError()
+  if s == nil: OSError(OSLastError())
   result.name = $s.h_name
   result.aliases = cstringArrayToSeq(s.h_aliases)
   when defined(windows): 
@@ -708,7 +692,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
     elif s.h_addrtype == posix.AF_INET6:
       result.addrType = AF_INET6
     else:
-      OSError("unknown h_addrtype")
+      raise newException(EOS, "unknown h_addrtype")
   result.addrList = cstringArrayToSeq(s.h_addr_list)
   result.length = int(s.h_length)
 
@@ -719,7 +703,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
   var size = sizeof(res).TSockLen
   if getsockopt(socket.fd, cint(level), cint(optname), 
                 addr(res), addr(size)) < 0'i32:
-    OSError()
+    OSError(OSLastError())
   result = int(res)
 
 proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
@@ -728,7 +712,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
   var value = cint(optval)
   if setsockopt(socket.fd, cint(level), cint(optname), addr(value),  
                 sizeof(value).TSockLen) < 0'i32:
-    OSError()
+    OSError(OSLastError())
 
 proc connect*(socket: TSocket, address: string, port = TPort(0), 
               af: TDomain = AF_INET) {.tags: [FReadIO].} =
@@ -746,15 +730,17 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
   gaiNim(address, port, hints, aiList)
   # try all possibilities:
   var success = false
+  var lastError: TOSErrorCode
   var it = aiList
   while it != nil:
     if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32:
       success = true
       break
+    else: lastError = OSLastError()
     it = it.ai_next
 
   freeaddrinfo(aiList)
-  if not success: OSError()
+  if not success: OSError(lastError)
   
   when defined(ssl):
     if socket.isSSL:
@@ -807,6 +793,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
   gaiNim(name, port, hints, aiList)
   # try all possibilities:
   var success = false
+  var lastError: TOSErrorCode
   var it = aiList
   while it != nil:
     var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen)
@@ -814,22 +801,21 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
       success = true
       break
     else:
-      # TODO: Test on Windows.
+      lastError = OSLastError()
       when defined(windows):
-        var err = WSAGetLastError()
         # Windows EINTR doesn't behave same as POSIX.
-        if err == WSAEWOULDBLOCK:
+        if lastError.int32 == WSAEWOULDBLOCK:
           success = true
           break
       else:
-        if errno == EINTR or errno == EINPROGRESS:
+        if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
           success = true
           break
         
     it = it.ai_next
 
   freeaddrinfo(aiList)
-  if not success: OSError()
+  if not success: OSError(lastError)
   when defined(ssl):
     if socket.isSSL:
       socket.sslNoHandshake = true
@@ -1110,7 +1096,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
     var s = @[socket]
     var startTime = epochTime()
     let selRet = select(s, timeout - int(waited * 1000.0))
-    if selRet < 0: OSError()
+    if selRet < 0: OSError(OSLastError())
     if selRet != 1:
       raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
     waited += (epochTime() - startTime)
@@ -1259,14 +1245,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
     var c: char
     discard waitFor(socket, waited, timeout, 1, "readLine")
     var n = recv(socket, addr(c), 1)
-    if n < 0: OSError()
+    if n < 0: OSError(OSLastError())
     elif n == 0: return
     if c == '\r':
       discard waitFor(socket, waited, timeout, 1, "readLine")
       n = peekChar(socket, c)
       if n > 0 and c == '\L':
         discard recv(socket, addr(c), 1)
-      elif n <= 0: OSError()
+      elif n <= 0: OSError(OSLastError())
       addNlIfEmpty()
       return
     elif c == '\L': 
@@ -1326,6 +1312,7 @@ proc readLineAsync*(socket: TSocket,
   while true:
     var c: char
     var n = recv(socket, addr(c), 1)
+    #echo(n)
     if n < 0:
       if line.len == 0: errorOrNone else: return ReadPartialLine
     elif n == 0: 
@@ -1352,7 +1339,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
   var pos = 0
   while true:
     var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
-    if bytesRead == -1: OSError()
+    if bytesRead == -1: OSError(OSLastError())
     setLen(result.string, pos + bytesRead)
     if bytesRead != bufSize-1: break
     # increase capacity:
@@ -1364,7 +1351,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
     while true:
       var bytesRead = recv(socket, cstring(buf), bufSize-1)
       # Error
-      if bytesRead == -1: OSError()
+      if bytesRead == -1: OSError(OSLastError())
       
       buf[bytesRead] = '\0' # might not be necessary
       setLen(buf, bytesRead)
@@ -1421,16 +1408,15 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
           else: SSLError("Unknown Error")
           
     if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
+      let err = OSLastError()
       when defined(windows):
-        # TODO: Test on Windows
-        var err = WSAGetLastError()
-        if err == WSAEWOULDBLOCK:
+        if err.int32 == WSAEWOULDBLOCK:
           return False
-        else: OSError()
+        else: OSError(err)
       else:
-        if errno == EAGAIN or errno == EWOULDBLOCK:
+        if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
           return False
-        else: OSError()
+        else: OSError(err)
 
     setLen(s.string, pos + bytesRead)
     if bytesRead != bufSize-1: break
@@ -1475,16 +1461,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
   result = true
   var callRes = recvFrom(socket, data, length, address, port, flags)
   if callRes < 0:
+    let err = OSLastError()
     when defined(windows):
-      # TODO: Test on Windows
-      var err = WSAGetLastError()
-      if err == WSAEWOULDBLOCK:
+      if err.int32 == WSAEWOULDBLOCK:
         return False
-      else: OSError()
+      else: OSError(err)
     else:
-      if errno == EAGAIN or errno == EWOULDBLOCK:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
         return False
-      else: OSError()
+      else: OSError(err)
 
 proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
   ## skips all the data that is pending for the socket
@@ -1531,7 +1516,7 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
       if socket.isSSL:
         SSLError()
     
-    OSError()
+    OSError(OSLastError())
 
 proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
   ## sends data to a non-blocking socket.
@@ -1561,16 +1546,15 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
       else:
         return
   if result == -1:
+    let err = OSLastError()
     when defined(windows):
-      var err = WSAGetLastError()
-      # TODO: Test on windows.
-      if err == WSAEINPROGRESS:
+      if err.int32 == WSAEINPROGRESS:
         return 0
-      else: OSError()
+      else: OSError(err)
     else:
-      if errno == EAGAIN or errno == EWOULDBLOCK:
+      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
         return 0
-      else: OSError()
+      else: OSError(err)
   
 
 proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
@@ -1626,15 +1610,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
   when defined(Windows):
     var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
     if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
-      OSError()
+      OSError(OSLastError())
   else: # BSD sockets
     var x: int = fcntl(s.fd, F_GETFL, 0)
     if x == -1:
-      OSError()
+      OSError(OSLastError())
     else:
       var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
       if fcntl(s.fd, F_SETFL, mode) == -1:
-        OSError()
+        OSError(OSLastError())
   s.nonblocking = not blocking
   
 proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int,
@@ -1665,6 +1649,6 @@ proc getFD*(socket: TSocket): cint = return socket.fd
 
 when defined(Windows):
   var wsa: TWSADATA
-  if WSAStartup(0x0101'i16, wsa) != 0: OSError()
+  if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
 
 
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index fa4925ee6..d448d2b10 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -489,7 +489,7 @@ proc FD_SET*(Socket: TWinSocket, FDSet: var TFDSet) =
 proc FD_ZERO*(FDSet: var TFDSet) =
   FDSet.fd_count = 0
 
-proc WSAStartup*(wVersionRequired: int16, WSData: var TWSAData): cint {.
+proc WSAStartup*(wVersionRequired: int16, WSData: ptr TWSAData): cint {.
   stdcall, importc: "WSAStartup", dynlib: ws2dll.}
 
 proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,