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: "".}: 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,