Merge branch 'master' of github.com:Araq/Nimrod

This commit is contained in:
Araq 2013-06-27 01:05:19 +02:00
commit 3ff572ffcd
12 changed files with 585 additions and 485 deletions

View file

@ -167,7 +167,7 @@ proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
result = newAsyncSocket() result = newAsyncSocket()
result.socket = socket(domain, typ, protocol, buffered) result.socket = socket(domain, typ, protocol, buffered)
result.proto = protocol result.proto = protocol
if result.socket == InvalidSocket: OSError() if result.socket == InvalidSocket: OSError(OSLastError())
result.socket.setBlocking(false) result.socket.setBlocking(false)
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
@ -349,7 +349,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
client.sslNeedAccept = false client.sslNeedAccept = false
client.info = SockConnected client.info = SockConnected
if c == InvalidSocket: OSError() if c == InvalidSocket: SocketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested. c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``. # deleg.open is set in ``toDelegate``.
@ -423,6 +423,10 @@ proc isConnecting*(s: PAsyncSocket): bool =
proc isClosed*(s: PAsyncSocket): bool = proc isClosed*(s: PAsyncSocket): bool =
## Determines whether ``s`` has been closed. ## Determines whether ``s`` has been closed.
return s.info == SockClosed return s.info == SockClosed
proc isSendDataBuffered*(s: PAsyncSocket): bool =
## Determines whether ``s`` has data waiting to be sent, i.e. whether this
## socket's sendBuffer contains data.
return s.sendBuffer.len != 0
proc setHandleWrite*(s: PAsyncSocket, proc setHandleWrite*(s: PAsyncSocket,
handleWrite: proc (s: PAsyncSocket) {.closure.}) = handleWrite: proc (s: PAsyncSocket) {.closure.}) =
@ -638,8 +642,7 @@ when isMainModule:
proc testRead(s: PAsyncSocket, no: int) = proc testRead(s: PAsyncSocket, no: int) =
echo("Reading! " & $no) echo("Reading! " & $no)
var data = "" var data = ""
if not s.readLine(data): if not s.readLine(data): return
OSError()
if data == "": if data == "":
echo("Closing connection. " & $no) echo("Closing connection. " & $no)
s.close() s.close()

View file

@ -39,6 +39,8 @@ type
FWriteDir* = object of FWriteIO ## effect that denotes a write operation to FWriteDir* = object of FWriteIO ## effect that denotes a write operation to
## the directory structure ## the directory structure
TOSErrorCode* = distinct int32 ## Specifies an OS Error Code.
const const
doslike = defined(windows) or defined(OS2) or defined(DOS) doslike = defined(windows) or defined(OS2) or defined(DOS)
# DOS-like filesystem # DOS-like filesystem
@ -171,10 +173,13 @@ const
## The character which separates the base filename from the extension; ## The character which separates the base filename from the extension;
## for example, the '.' in ``os.nim``. ## 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``. ## Retrieves the operating system's error flag, ``errno``.
## On Windows ``GetLastError`` is checked before ``errno``. ## On Windows ``GetLastError`` is checked before ``errno``.
## Returns "" if no error occured. ## Returns "" if no error occured.
##
## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc.
result = "" result = ""
when defined(Windows): when defined(Windows):
var err = GetLastError() var err = GetLastError()
@ -194,17 +199,89 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
if errno != 0'i32: if errno != 0'i32:
result = $os.strerror(errno) 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``. ## raises an EOS exception with the given message ``msg``.
## If ``msg == ""``, the operating system's error flag ## If ``msg == ""``, the operating system's error flag
## (``errno``) is converted to a readable error message. On Windows ## (``errno``) is converted to a readable error message. On Windows
## ``GetLastError`` is checked before ``errno``. ## ``GetLastError`` is checked before ``errno``.
## If no error flag is set, the message ``unknown OS error`` is used. ## 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: if len(msg) == 0:
var m = OSErrorMsg() var m = OSErrorMsg()
raise newException(EOS, if m.len > 0: m else: "unknown OS error") raise newException(EOS, if m.len > 0: m else: "unknown OS error")
else: else:
raise newException(EOS, msg) 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 {. proc UnixToNativePath*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} = 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. ## Returns the `file`'s last modification time.
when defined(posix): when defined(posix):
var res: TStat var res: TStat
if stat(file, res) < 0'i32: OSError() if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_mtime return res.st_mtime
else: else:
var f: TWIN32_Find_Data var f: TWIN32_Find_Data
var h = findfirstFile(file, f) var h = findfirstFile(file, f)
if h == -1'i32: OSError() if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)) result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
findclose(h) 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. ## Returns the `file`'s last read or write access time.
when defined(posix): when defined(posix):
var res: TStat var res: TStat
if stat(file, res) < 0'i32: OSError() if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_atime return res.st_atime
else: else:
var f: TWIN32_Find_Data var f: TWIN32_Find_Data
var h = findfirstFile(file, f) var h = findfirstFile(file, f)
if h == -1'i32: OSError() if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)) result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
findclose(h) findclose(h)
@ -337,12 +414,12 @@ proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s creation time. ## Returns the `file`'s creation time.
when defined(posix): when defined(posix):
var res: TStat var res: TStat
if stat(file, res) < 0'i32: OSError() if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_ctime return res.st_ctime
else: else:
var f: TWIN32_Find_Data var f: TWIN32_Find_Data
var h = findfirstFile(file, f) var h = findfirstFile(file, f)
if h == -1'i32: OSError() if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime)) result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
findclose(h) findclose(h)
@ -358,30 +435,30 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
when useWinUnicode: when useWinUnicode:
var res = newWideCString("", bufsize) var res = newWideCString("", bufsize)
var L = GetCurrentDirectoryW(bufsize, res) var L = GetCurrentDirectoryW(bufsize, res)
if L == 0'i32: OSError() if L == 0'i32: OSError(OSLastError())
result = res$L result = res$L
else: else:
result = newString(bufsize) result = newString(bufsize)
var L = GetCurrentDirectoryA(bufsize, result) var L = GetCurrentDirectoryA(bufsize, result)
if L == 0'i32: OSError() if L == 0'i32: OSError(OSLastError())
setLen(result, L) setLen(result, L)
else: else:
result = newString(bufsize) result = newString(bufsize)
if getcwd(result, bufsize) != nil: if getcwd(result, bufsize) != nil:
setlen(result, c_strlen(result)) setlen(result, c_strlen(result))
else: else:
OSError() OSError(OSLastError())
proc setCurrentDir*(newDir: string) {.inline, tags: [].} = proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
## Sets the `current working directory`:idx:; `EOS` is raised if ## Sets the `current working directory`:idx:; `EOS` is raised if
## `newDir` cannot been set. ## `newDir` cannot been set.
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError() if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError())
else: else:
if SetCurrentDirectoryA(newDir) == 0'i32: OSError() if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
else: else:
if chdir(newDir) != 0'i32: OSError() if chdir(newDir) != 0'i32: OSError(OSLastError())
proc JoinPath*(head, tail: string): string {. proc JoinPath*(head, tail: string): string {.
noSideEffect, rtl, extern: "nos$1".} = 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 res = newWideCString("", bufsize div 2)
var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused) var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
if L <= 0'i32 or L >= bufsize: if L <= 0'i32 or L >= bufsize:
OSError() OSError(OSLastError())
result = res$L result = res$L
else: else:
var unused: cstring var unused: cstring
result = newString(bufsize) result = newString(bufsize)
var L = GetFullPathNameA(filename, bufsize, result, unused) 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) setLen(result, L)
elif defined(macosx) or defined(bsd): elif defined(macosx) or defined(bsd):
# On Mac OS X 10.5, realpath does not allocate the buffer on its own # On Mac OS X 10.5, realpath does not allocate the buffer on its own
var pathBuffer: cstring = newString(pathMax) var pathBuffer: cstring = newString(pathMax)
var resultBuffer = realpath(filename, pathBuffer) var resultBuffer = realpath(filename, pathBuffer)
if resultBuffer == nil: OSError() if resultBuffer == nil: OSError(OSLastError())
result = $resultBuffer result = $resultBuffer
else: else:
var res = realpath(filename, nil) var res = realpath(filename, nil)
if res == nil: OSError() if res == nil: OSError(OSLastError())
result = $res result = $res
c_free(res) c_free(res)
@ -677,6 +754,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
var f1 = OpenHandle(path1) var f1 = OpenHandle(path1)
var f2 = OpenHandle(path2) var f2 = OpenHandle(path2)
var lastErr: TOSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION 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 result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
fi1.nFileIndexHigh == fi2.nFileIndexHigh and fi1.nFileIndexHigh == fi2.nFileIndexHigh and
fi1.nFileIndexLow == fi2.nFileIndexLow fi1.nFileIndexLow == fi2.nFileIndexLow
else: success = false else:
else: success = false lastErr = OSLastError()
success = false
else:
lastErr = OSLastError()
success = false
discard CloseHandle(f1) discard CloseHandle(f1)
discard CloseHandle(f2) discard CloseHandle(f2)
if not success: OSError() if not success: OSError(lastErr)
else: else:
var a, b: TStat var a, b: TStat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
OSError() OSError(OSLastError())
else: else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino 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: when useWinUnicode:
let s = newWideCString(source) let s = newWideCString(source)
let d = newWideCString(dest) let d = newWideCString(dest)
if CopyFileW(s, d, 0'i32) == 0'i32: OSError() if CopyFileW(s, d, 0'i32) == 0'i32: OSError(OSLastError())
else: else:
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError() if CopyFileA(source, dest, 0'i32) == 0'i32: OSError(OSLastError())
else: else:
# generic version of copyFile which works for any platform: # generic version of copyFile which works for any platform:
const bufSize = 8000 # better for memory manager const bufSize = 8000 # better for memory manager
var d, s: TFile var d, s: TFile
if not open(s, source): OSError() if not open(s, source): OSError(OSLastError())
if not open(d, dest, fmWrite): if not open(d, dest, fmWrite):
close(s) close(s)
OSError() OSError(OSLastError())
var buf = alloc(bufsize) var buf = alloc(bufsize)
while True: while True:
var bytesread = readBuffer(s, buf, bufsize) var bytesread = readBuffer(s, buf, bufsize)
@ -758,7 +840,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
dealloc(buf) dealloc(buf)
close(s) close(s)
close(d) close(d)
OSError() OSError(OSLastError())
if bytesread != bufSize: break if bytesread != bufSize: break
dealloc(buf) dealloc(buf)
close(s) close(s)
@ -767,7 +849,8 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [FReadIO, FWriteIO].} = tags: [FReadIO, FWriteIO].} =
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised. ## 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): when not defined(ENOENT):
var ENOENT {.importc, header: "<errno.h>".}: cint 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].} = proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Removes the `file`. If this fails, `EOS` is raised. This does not fail ## Removes the `file`. If this fails, `EOS` is raised. This does not fail
## if the file never existed in the first place. ## 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", proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [FExecIO].} = tags: [FExecIO].} =
@ -907,14 +991,14 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
indx = high(environment) indx = high(environment)
when defined(unix): when defined(unix):
if cputenv(environment[indx]) != 0'i32: if cputenv(environment[indx]) != 0'i32:
OSError() OSError(OSLastError())
else: else:
when useWinUnicode: when useWinUnicode:
var k = newWideCString(key) var k = newWideCString(key)
var v = newWideCString(val) var v = newWideCString(val)
if SetEnvironmentVariableW(k, v) == 0'i32: OSError() if SetEnvironmentVariableW(k, v) == 0'i32: OSError(OSLastError())
else: else:
if SetEnvironmentVariableA(key, val) == 0'i32: OSError() if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError())
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} = iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
## Iterate over all `environments variables`:idx:. In the first component ## Iterate over all `environments variables`:idx:. In the first component
@ -1044,10 +1128,12 @@ proc rawRemoveDir(dir: string) =
wrapUnary(res, RemoveDirectoryW, dir) wrapUnary(res, RemoveDirectoryW, dir)
else: else:
var res = RemoveDirectoryA(dir) var res = RemoveDirectoryA(dir)
if res == 0'i32 and GetLastError() != 3'i32 and let lastError = OSLastError()
GetLastError() != 18'i32: OSError() if res == 0'i32 and lastError.int32 != 3'i32 and
lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
OSError(lastError)
else: 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: [ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
FWriteDir, FReadDir].} = FWriteDir, FReadDir].} =
@ -1065,14 +1151,14 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
proc rawCreateDir(dir: string) = proc rawCreateDir(dir: string) =
when defined(unix): when defined(unix):
if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST: if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
OSError() OSError(OSLastError())
else: else:
when useWinUnicode: when useWinUnicode:
wrapUnary(res, CreateDirectoryW, dir) wrapUnary(res, CreateDirectoryW, dir)
else: else:
var res = CreateDirectoryA(dir) var res = CreateDirectoryA(dir)
if res == 0'i32 and GetLastError() != 183'i32: if res == 0'i32 and GetLastError() != 183'i32:
OSError() OSError(OSLastError())
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Creates the `directory`:idx: `dir`. ## Creates the `directory`:idx: `dir`.
@ -1213,7 +1299,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
## permission is available in any case. ## permission is available in any case.
when defined(posix): when defined(posix):
var a: TStat var a: TStat
if stat(filename, a) < 0'i32: OSError() if stat(filename, a) < 0'i32: OSError(OSLastError())
result = {} result = {}
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead) if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite) 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) wrapUnary(res, GetFileAttributesW, filename)
else: else:
var res = GetFileAttributesA(filename) var res = GetFileAttributesA(filename)
if res == -1'i32: OSError() if res == -1'i32: OSError(OSLastError())
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
fpOthersExec, fpOthersRead} fpOthersExec, fpOthersRead}
@ -1257,13 +1343,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
if fpOthersWrite in permissions: p = p or S_IWOTH if fpOthersWrite in permissions: p = p or S_IWOTH
if fpOthersExec in permissions: p = p or S_IXOTH if fpOthersExec in permissions: p = p or S_IXOTH
if chmod(filename, p) != 0: OSError() if chmod(filename, p) != 0: OSError(OSLastError())
else: else:
when useWinUnicode: when useWinUnicode:
wrapUnary(res, GetFileAttributesW, filename) wrapUnary(res, GetFileAttributesW, filename)
else: else:
var res = GetFileAttributesA(filename) var res = GetFileAttributesA(filename)
if res == -1'i32: OSError() if res == -1'i32: OSError(OSLastError())
if fpUserWrite in permissions: if fpUserWrite in permissions:
res = res and not FILE_ATTRIBUTE_READONLY res = res and not FILE_ATTRIBUTE_READONLY
else: else:
@ -1272,7 +1358,7 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
wrapBinary(res2, SetFileAttributesW, filename, res) wrapBinary(res2, SetFileAttributesW, filename, res)
else: else:
var res2 = SetFileAttributesA(filename, res) var res2 = SetFileAttributesA(filename, res)
if res2 == - 1'i32: OSError() if res2 == - 1'i32: OSError(OSLastError())
proc inclFilePermissions*(filename: string, proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {. permissions: set[TFilePermission]) {.
@ -1368,6 +1454,9 @@ when defined(macosx):
proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
## Returns the filename of the application's executable. ## Returns the filename of the application's executable.
##
## This procedure will resolve symlinks.
##
## **Note**: This does not work reliably on BSD. ## **Note**: This does not work reliably on BSD.
# Linux: /proc/<pid>/exe # Linux: /proc/<pid>/exe
@ -1397,6 +1486,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
result = newString(int(size)) result = newString(int(size))
if getExecPath2(result, size): if getExecPath2(result, size):
result = "" # error! result = "" # error!
if result.len > 0:
result = result.expandFilename
else: else:
# little heuristic that may work on other POSIX-like systems: # little heuristic that may work on other POSIX-like systems:
result = string(getEnv("_")) result = string(getEnv("_"))
@ -1443,7 +1534,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
when defined(windows): when defined(windows):
var a: TWin32FindData var a: TWin32FindData
var resA = findfirstFile(file, a) var resA = findfirstFile(file, a)
if resA == -1: OSError() if resA == -1: OSError(OSLastError())
result = rdFileSize(a) result = rdFileSize(a)
findclose(resA) findclose(resA)
else: else:
@ -1451,7 +1542,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
if open(f, file): if open(f, file):
result = getFileSize(f) result = getFileSize(f)
close(f) close(f)
else: OSError() else: OSError(OSLastError())
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} = proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
## Searches for `exe` in the current working directory and then ## Searches for `exe` in the current working directory and then

View file

@ -264,7 +264,7 @@ when defined(Windows) and not defined(useNimRtl):
# TRUE and zero bytes returned (EOF). # TRUE and zero bytes returned (EOF).
# TRUE and n (>0) bytes returned (good data). # TRUE and n (>0) bytes returned (good data).
# FALSE and bytes returned undefined (system error). # FALSE and bytes returned undefined (system error).
if a == 0 and br != 0: OSError() if a == 0 and br != 0: OSError(OSLastError())
s.atTheEnd = br < bufLen s.atTheEnd = br < bufLen
result = br result = br
@ -272,7 +272,7 @@ when defined(Windows) and not defined(useNimRtl):
var s = PFileHandleStream(s) var s = PFileHandleStream(s)
var bytesWritten: int32 var bytesWritten: int32
var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil) var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil)
if a == 0: OSError() if a == 0: OSError(OSLastError())
proc newFileHandleStream(handle: THandle): PFileHandleStream = proc newFileHandleStream(handle: THandle): PFileHandleStream =
new(result) new(result)
@ -313,7 +313,7 @@ when defined(Windows) and not defined(useNimRtl):
piInheritablePipe.lpSecurityDescriptor = nil piInheritablePipe.lpSecurityDescriptor = nil
piInheritablePipe.Binherithandle = 1 piInheritablePipe.Binherithandle = 1
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32: if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
OSError() OSError(OSLastError())
proc fileClose(h: THandle) {.inline.} = proc fileClose(h: THandle) {.inline.} =
if h > 4: discard CloseHandle(h) if h > 4: discard CloseHandle(h)
@ -370,6 +370,7 @@ when defined(Windows) and not defined(useNimRtl):
else: else:
success = winlean.CreateProcessA(nil, success = winlean.CreateProcessA(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo) cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
let lastError = OSLastError()
if poParentStreams notin options: if poParentStreams notin options:
FileClose(si.hStdInput) FileClose(si.hStdInput)
@ -379,7 +380,7 @@ when defined(Windows) and not defined(useNimRtl):
if e != nil: dealloc(e) if e != nil: dealloc(e)
dealloc(cmdl) dealloc(cmdl)
if success == 0: OSError() if success == 0: OSError(lastError)
# Close the handle now so anyone waiting is woken: # Close the handle now so anyone waiting is woken:
discard closeHandle(procInfo.hThread) discard closeHandle(procInfo.hThread)
result.FProcessHandle = procInfo.hProcess result.FProcessHandle = procInfo.hProcess
@ -450,7 +451,7 @@ when defined(Windows) and not defined(useNimRtl):
var res = winlean.CreateProcessA(nil, command, nil, nil, 0, var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
if res == 0: if res == 0:
OSError() OSError(OSLastError())
else: else:
Process = ProcInfo.hProcess Process = ProcInfo.hProcess
discard CloseHandle(ProcInfo.hThread) discard CloseHandle(ProcInfo.hThread)
@ -473,7 +474,7 @@ when defined(Windows) and not defined(useNimRtl):
of WAIT_TIMEOUT: of WAIT_TIMEOUT:
return 0 return 0
of WAIT_FAILED: of WAIT_FAILED:
OSError() OSError(OSLastError())
else: else:
var i = ret - WAIT_OBJECT_0 var i = ret - WAIT_OBJECT_0
readfds.del(i) readfds.del(i)
@ -531,7 +532,7 @@ elif not defined(useNimRtl):
if poParentStreams notin options: if poParentStreams notin options:
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or
pipe(p_stderr) != 0'i32: pipe(p_stderr) != 0'i32:
OSError() OSError(OSLastError())
var pid: TPid var pid: TPid
when defined(posix_spawn) and not defined(useFork): when defined(posix_spawn) and not defined(useFork):
@ -539,7 +540,7 @@ elif not defined(useNimRtl):
var fops: Tposix_spawn_file_actions var fops: Tposix_spawn_file_actions
template chck(e: expr) = template chck(e: expr) =
if e != 0'i32: OSError() if e != 0'i32: OSError(OSLastError())
chck posix_spawn_file_actions_init(fops) chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr) chck posix_spawnattr_init(attr)
@ -584,20 +585,20 @@ elif not defined(useNimRtl):
else: else:
Pid = fork() Pid = fork()
if Pid < 0: OSError() if Pid < 0: OSError(OSLastError())
if pid == 0: if pid == 0:
## child process: ## child process:
if poParentStreams notin options: if poParentStreams notin options:
discard close(p_stdin[writeIdx]) discard close(p_stdin[writeIdx])
if dup2(p_stdin[readIdx], readIdx) < 0: OSError() if dup2(p_stdin[readIdx], readIdx) < 0: OSError(OSLastError())
discard close(p_stdout[readIdx]) discard close(p_stdout[readIdx])
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError() if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError(OSLastError())
discard close(p_stderr[readIdx]) discard close(p_stderr[readIdx])
if poStdErrToStdOut in options: if poStdErrToStdOut in options:
if dup2(p_stdout[writeIdx], 2) < 0: OSError() if dup2(p_stdout[writeIdx], 2) < 0: OSError(OSLastError())
else: else:
if dup2(p_stderr[writeIdx], 2) < 0: OSError() if dup2(p_stderr[writeIdx], 2) < 0: OSError(OSLastError())
# Create a new process group # Create a new process group
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
@ -652,10 +653,10 @@ elif not defined(useNimRtl):
discard close(p.errorHandle) discard close(p.errorHandle)
proc suspend(p: PProcess) = proc suspend(p: PProcess) =
if kill(-p.id, SIGSTOP) != 0'i32: OSError() if kill(-p.id, SIGSTOP) != 0'i32: OSError(OSLastError())
proc resume(p: PProcess) = proc resume(p: PProcess) =
if kill(-p.id, SIGCONT) != 0'i32: OSError() if kill(-p.id, SIGCONT) != 0'i32: OSError(OSLastError())
proc running(p: PProcess): bool = proc running(p: PProcess): bool =
var ret = waitPid(p.id, p.exitCode, WNOHANG) var ret = waitPid(p.id, p.exitCode, WNOHANG)
@ -665,8 +666,8 @@ elif not defined(useNimRtl):
proc terminate(p: PProcess) = proc terminate(p: PProcess) =
if kill(-p.id, SIGTERM) == 0'i32: if kill(-p.id, SIGTERM) == 0'i32:
if p.running(): if p.running():
if kill(-p.id, SIGKILL) != 0'i32: OSError() if kill(-p.id, SIGKILL) != 0'i32: OSError(OSLastError())
else: OSError() else: OSError(OSLastError())
proc waitForExit(p: PProcess, timeout: int = -1): int = proc waitForExit(p: PProcess, timeout: int = -1): int =
#if waitPid(p.id, p.exitCode, 0) == int(p.id): #if waitPid(p.id, p.exitCode, 0) == int(p.id):
@ -676,7 +677,7 @@ elif not defined(useNimRtl):
if p.exitCode != -3: return p.exitCode if p.exitCode != -3: return p.exitCode
if waitPid(p.id, p.exitCode, 0) < 0: if waitPid(p.id, p.exitCode, 0) < 0:
p.exitCode = -3 p.exitCode = -3
OSError() OSError(OSLastError())
result = int(p.exitCode) shr 8 result = int(p.exitCode) shr 8
proc peekExitCode(p: PProcess): int = proc peekExitCode(p: PProcess): int =
@ -690,7 +691,7 @@ elif not defined(useNimRtl):
proc createStream(stream: var PStream, handle: var TFileHandle, proc createStream(stream: var PStream, handle: var TFileHandle,
fileMode: TFileMode) = fileMode: TFileMode) =
var f: TFile var f: TFile
if not open(f, handle, fileMode): OSError() if not open(f, handle, fileMode): OSError(OSLastError())
stream = newFileStream(f) stream = newFileStream(f)
proc inputStream(p: PProcess): PStream = proc inputStream(p: PProcess): PStream =

View file

@ -175,6 +175,15 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
if ret == L: if ret == L:
return ReadFullLine return ReadFullLine
proc checkCloseSocket(client: PAsyncClient) =
if not client.c.isClosed:
if client.c.isSendDataBuffered:
client.c.setHandleWrite do (s: PAsyncSocket):
if not s.isClosed and not s.isSendDataBuffered:
s.close()
s.delHandleWrite()
else: client.c.close()
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) = proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case client.mode case client.mode
of ClientReadChar: of ClientReadChar:
@ -206,7 +215,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
client.mode = ClientReadContent client.mode = ClientReadContent
else: else:
s.handleRequest(client.c, client.input, client.headers) s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close() checkCloseSocket(client)
of ReadPartialLine, ReadDisconnected, ReadNone: return of ReadPartialLine, ReadDisconnected, ReadNone: return
of ClientReadContent: of ClientReadContent:
let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len
@ -215,11 +224,11 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case ret case ret
of ReadFullLine: of ReadFullLine:
s.handleRequest(client.c, client.input, client.headers) s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close() checkCloseSocket(client)
of ReadPartialLine, ReadDisconnected, ReadNone: return of ReadPartialLine, ReadDisconnected, ReadNone: return
else: else:
s.handleRequest(client.c, client.input, client.headers) s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close() checkCloseSocket(client)
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) = proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
var client: PAsyncSocket var client: PAsyncSocket

View file

@ -231,7 +231,7 @@ when defined(ssl):
if err == 0: if err == 0:
raise newException(ESSL, "No error reported.") raise newException(ESSL, "No error reported.")
if err == -1: if err == -1:
OSError() OSError(OSLastError())
var errStr = ErrErrorString(err, nil) var errStr = ErrErrorString(err, nil)
raise newException(ESSL, $errStr) raise newException(ESSL, $errStr)
@ -347,24 +347,23 @@ proc SocketError*(socket: TSocket, err: int = -1, async = false) =
else: SSLError("Unknown Error") else: SSLError("Unknown Error")
if err == -1 and not (when defined(ssl): socket.isSSL else: false): if err == -1 and not (when defined(ssl): socket.isSSL else: false):
let lastError = OSLastError()
if async: if async:
when defined(windows): when defined(windows):
# TODO: Test on Windows if lastError.int32 == WSAEWOULDBLOCK:
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
return return
else: OSError() else: OSError(lastError)
else: else:
if errno == EAGAIN or errno == EWOULDBLOCK: if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
return return
else: OSError() else: OSError(lastError)
else: OSError() else: OSError(lastError)
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## 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.} = proc invalidIp4(s: string) {.noreturn, noinline.} =
raise newException(EInvalidValue, "invalid ip4 address: " & s) 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) var gaiResult = getAddrInfo(a, $p, addr(h), list)
if gaiResult != 0'i32: if gaiResult != 0'i32:
when defined(windows): when defined(windows):
OSError() OSError(OSLastError())
else: else:
OSError($gai_strerror(gaiResult)) 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) name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)), if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSockLen) < 0'i32: sizeof(name).TSockLen) < 0'i32:
OSError() OSError(OSLastError())
else: else:
var hints: TAddrInfo var hints: TAddrInfo
var aiList: ptr TAddrInfo = nil var aiList: ptr TAddrInfo = nil
@ -432,21 +431,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
hints.ai_protocol = toInt(IPPROTO_TCP) hints.ai_protocol = toInt(IPPROTO_TCP)
gaiNim(address, port, hints, aiList) gaiNim(address, port, hints, aiList)
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 0'i32: if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 0'i32:
OSError() OSError(OSLastError())
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()
proc getSockName*(socket: TSocket): TPort = proc getSockName*(socket: TSocket): TPort =
## returns the socket's associated port number. ## returns the socket's associated port number.
@ -460,7 +445,7 @@ proc getSockName*(socket: TSocket): TPort =
var namelen = sizeof(name).TSockLen var namelen = sizeof(name).TSockLen
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)), if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
OSError() OSError(OSLastError())
result = TPort(sockets.ntohs(name.sin_port)) result = TPort(sockets.ntohs(name.sin_port))
template acceptAddrPlain(noClientRet, successRet: expr, template acceptAddrPlain(noClientRet, successRet: expr,
@ -472,26 +457,25 @@ template acceptAddrPlain(noClientRet, successRet: expr,
addr(addrLen)) addr(addrLen))
if sock < 0: if sock < 0:
# TODO: Test on Windows. let err = OSLastError()
when defined(windows): when defined(windows):
var err = WSAGetLastError() if err.int32 == WSAEINPROGRESS:
if err == WSAEINPROGRESS:
client = InvalidSocket client = InvalidSocket
address = "" address = ""
when noClientRet.int == -1: when noClientRet.int == -1:
return return
else: else:
return noClientRet return noClientRet
else: OSError() else: OSError(err)
else: else:
if errno == EAGAIN or errno == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
client = InvalidSocket client = InvalidSocket
address = "" address = ""
when noClientRet.int == -1: when noClientRet.int == -1:
return return
else: else:
return noClientRet return noClientRet
else: OSError() else: OSError(err)
else: else:
client.fd = sock client.fd = sock
client.isBuffered = server.isBuffered client.isBuffered = server.isBuffered
@ -644,7 +628,7 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
var s = winlean.getservbyname(name, proto) var s = winlean.getservbyname(name, proto)
else: else:
var s = posix.getservbyname(name, proto) var s = posix.getservbyname(name, proto)
if s == nil: OSError() if s == nil: OSError(OSLastError())
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port) 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) var s = winlean.getservbyport(ze(int16(port)).cint, proto)
else: else:
var s = posix.getservbyport(ze(int16(port)).cint, proto) var s = posix.getservbyport(ze(int16(port)).cint, proto)
if s == nil: OSError() if s == nil: OSError(OSLastError())
result.name = $s.s_name result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port) result.port = TPort(s.s_port)
@ -670,7 +654,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
when defined(windows): when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(sockets.AF_INET)) cint(sockets.AF_INET))
if s == nil: OSError() if s == nil: OSError(OSLastError())
else: else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen, var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen,
cint(posix.AF_INET)) cint(posix.AF_INET))
@ -687,7 +671,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
elif s.h_addrtype == posix.AF_INET6: elif s.h_addrtype == posix.AF_INET6:
result.addrType = AF_INET6 result.addrType = AF_INET6
else: else:
OSError("unknown h_addrtype") raise newException(EOS, "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
@ -697,7 +681,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
var s = winlean.gethostbyname(name) var s = winlean.gethostbyname(name)
else: else:
var s = posix.gethostbyname(name) var s = posix.gethostbyname(name)
if s == nil: OSError() if s == nil: OSError(OSLastError())
result.name = $s.h_name result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases) result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows): when defined(windows):
@ -708,7 +692,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
elif s.h_addrtype == posix.AF_INET6: elif s.h_addrtype == posix.AF_INET6:
result.addrType = AF_INET6 result.addrType = AF_INET6
else: else:
OSError("unknown h_addrtype") raise newException(EOS, "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
@ -719,7 +703,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
var size = sizeof(res).TSockLen var size = sizeof(res).TSockLen
if getsockopt(socket.fd, cint(level), cint(optname), if getsockopt(socket.fd, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
OSError() OSError(OSLastError())
result = int(res) result = int(res)
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
@ -728,7 +712,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
var value = cint(optval) var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value), if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
sizeof(value).TSockLen) < 0'i32: sizeof(value).TSockLen) < 0'i32:
OSError() OSError(OSLastError())
proc connect*(socket: TSocket, address: string, port = TPort(0), proc connect*(socket: TSocket, address: string, port = TPort(0),
af: TDomain = AF_INET) {.tags: [FReadIO].} = af: TDomain = AF_INET) {.tags: [FReadIO].} =
@ -746,15 +730,17 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
gaiNim(address, port, hints, aiList) gaiNim(address, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32: if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32:
success = true success = true
break break
else: lastError = OSLastError()
it = it.ai_next it = it.ai_next
freeaddrinfo(aiList) freeaddrinfo(aiList)
if not success: OSError() if not success: OSError(lastError)
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
@ -807,6 +793,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
gaiNim(name, port, hints, aiList) gaiNim(name, port, hints, aiList)
# try all possibilities: # try all possibilities:
var success = false var success = false
var lastError: TOSErrorCode
var it = aiList var it = aiList
while it != nil: while it != nil:
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) 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 success = true
break break
else: else:
# TODO: Test on Windows. lastError = OSLastError()
when defined(windows): when defined(windows):
var err = WSAGetLastError()
# Windows EINTR doesn't behave same as POSIX. # Windows EINTR doesn't behave same as POSIX.
if err == WSAEWOULDBLOCK: if lastError.int32 == WSAEWOULDBLOCK:
success = true success = true
break break
else: else:
if errno == EINTR or errno == EINPROGRESS: if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
success = true success = true
break break
it = it.ai_next it = it.ai_next
freeaddrinfo(aiList) freeaddrinfo(aiList)
if not success: OSError() if not success: OSError(lastError)
when defined(ssl): when defined(ssl):
if socket.isSSL: if socket.isSSL:
socket.sslNoHandshake = true socket.sslNoHandshake = true
@ -1110,7 +1096,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
var s = @[socket] var s = @[socket]
var startTime = epochTime() var startTime = epochTime()
let selRet = select(s, timeout - int(waited * 1000.0)) let selRet = select(s, timeout - int(waited * 1000.0))
if selRet < 0: OSError() if selRet < 0: OSError(OSLastError())
if selRet != 1: if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.") raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime) waited += (epochTime() - startTime)
@ -1259,14 +1245,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
var c: char var c: char
discard waitFor(socket, waited, timeout, 1, "readLine") discard waitFor(socket, waited, timeout, 1, "readLine")
var n = recv(socket, addr(c), 1) var n = recv(socket, addr(c), 1)
if n < 0: OSError() if n < 0: OSError(OSLastError())
elif n == 0: return elif n == 0: return
if c == '\r': if c == '\r':
discard waitFor(socket, waited, timeout, 1, "readLine") discard waitFor(socket, waited, timeout, 1, "readLine")
n = peekChar(socket, c) n = peekChar(socket, c)
if n > 0 and c == '\L': if n > 0 and c == '\L':
discard recv(socket, addr(c), 1) discard recv(socket, addr(c), 1)
elif n <= 0: OSError() elif n <= 0: OSError(OSLastError())
addNlIfEmpty() addNlIfEmpty()
return return
elif c == '\L': elif c == '\L':
@ -1326,6 +1312,7 @@ proc readLineAsync*(socket: TSocket,
while true: while true:
var c: char var c: char
var n = recv(socket, addr(c), 1) var n = recv(socket, addr(c), 1)
#echo(n)
if n < 0: if n < 0:
if line.len == 0: errorOrNone else: return ReadPartialLine if line.len == 0: errorOrNone else: return ReadPartialLine
elif n == 0: elif n == 0:
@ -1352,7 +1339,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
var pos = 0 var pos = 0
while true: while true:
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1) var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
if bytesRead == -1: OSError() if bytesRead == -1: OSError(OSLastError())
setLen(result.string, pos + bytesRead) setLen(result.string, pos + bytesRead)
if bytesRead != bufSize-1: break if bytesRead != bufSize-1: break
# increase capacity: # increase capacity:
@ -1364,7 +1351,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
while true: while true:
var bytesRead = recv(socket, cstring(buf), bufSize-1) var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error # Error
if bytesRead == -1: OSError() if bytesRead == -1: OSError(OSLastError())
buf[bytesRead] = '\0' # might not be necessary buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead) setLen(buf, bytesRead)
@ -1421,16 +1408,15 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
else: SSLError("Unknown Error") else: SSLError("Unknown Error")
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false): if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
let err = OSLastError()
when defined(windows): when defined(windows):
# TODO: Test on Windows if err.int32 == WSAEWOULDBLOCK:
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
return False return False
else: OSError() else: OSError(err)
else: else:
if errno == EAGAIN or errno == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return False return False
else: OSError() else: OSError(err)
setLen(s.string, pos + bytesRead) setLen(s.string, pos + bytesRead)
if bytesRead != bufSize-1: break if bytesRead != bufSize-1: break
@ -1475,16 +1461,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
result = true result = true
var callRes = recvFrom(socket, data, length, address, port, flags) var callRes = recvFrom(socket, data, length, address, port, flags)
if callRes < 0: if callRes < 0:
let err = OSLastError()
when defined(windows): when defined(windows):
# TODO: Test on Windows if err.int32 == WSAEWOULDBLOCK:
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
return False return False
else: OSError() else: OSError(err)
else: else:
if errno == EAGAIN or errno == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return False return False
else: OSError() else: OSError(err)
proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} = proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
## skips all the data that is pending for the socket ## 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: if socket.isSSL:
SSLError() SSLError()
OSError() OSError(OSLastError())
proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
## sends data to a non-blocking socket. ## sends data to a non-blocking socket.
@ -1561,16 +1546,15 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
else: else:
return return
if result == -1: if result == -1:
let err = OSLastError()
when defined(windows): when defined(windows):
var err = WSAGetLastError() if err.int32 == WSAEINPROGRESS:
# TODO: Test on windows.
if err == WSAEINPROGRESS:
return 0 return 0
else: OSError() else: OSError(err)
else: else:
if errno == EAGAIN or errno == EWOULDBLOCK: if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return 0 return 0
else: OSError() else: OSError(err)
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
@ -1626,15 +1610,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
when defined(Windows): when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1: if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
OSError() OSError(OSLastError())
else: # BSD sockets else: # BSD sockets
var x: int = fcntl(s.fd, F_GETFL, 0) var x: int = fcntl(s.fd, F_GETFL, 0)
if x == -1: if x == -1:
OSError() OSError(OSLastError())
else: else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s.fd, F_SETFL, mode) == -1: if fcntl(s.fd, F_SETFL, mode) == -1:
OSError() OSError(OSLastError())
s.nonblocking = not blocking s.nonblocking = not blocking
proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, 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): when defined(Windows):
var wsa: TWSADATA var wsa: TWSADATA
if WSAStartup(0x0101'i16, wsa) != 0: OSError() if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View file

@ -1,312 +1,310 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2012 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module contains a few procedures to control the *terminal* ## This module contains a few procedures to control the *terminal*
## (also called *console*). On UNIX, the implementation simply uses ANSI escape ## (also called *console*). On UNIX, the implementation simply uses ANSI escape
## sequences and does not depend on any other module, on Windows it uses the ## sequences and does not depend on any other module, on Windows it uses the
## Windows API. ## Windows API.
## Changing the style is permanent even after program termination! Use the ## Changing the style is permanent even after program termination! Use the
## code ``system.addQuitProc(resetAttributes)`` to restore the defaults. ## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
import macros import macros
when defined(windows): when defined(windows):
import windows, os import windows, os
var var
conHandle: THandle conHandle: THandle
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0) # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
block: block:
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE) var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0: addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
OSError() OSError(OSLastError())
proc getCursorPos(): tuple [x,y: int] = proc getCursorPos(): tuple [x,y: int] =
var c: TCONSOLE_SCREEN_BUFFER_INFO var c: TCONSOLE_SCREEN_BUFFER_INFO
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError() if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError(OSLastError())
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
proc getAttributes(): int16 = proc getAttributes(): int16 =
var c: TCONSOLE_SCREEN_BUFFER_INFO var c: TCONSOLE_SCREEN_BUFFER_INFO
# workaround Windows bugs: try several times # workaround Windows bugs: try several times
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0: if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
return c.wAttributes return c.wAttributes
else: return 0x70'i16 # ERROR: return white background, black text
OSError()
return 0x70'i16 # ERROR: return white background, black text var
oldAttr = getAttributes()
var
oldAttr = getAttributes() proc setCursorPos*(x, y: int) =
## sets the terminal's cursor to the (x,y) position. (0,0) is the
proc setCursorPos*(x, y: int) = ## upper left of the screen.
## sets the terminal's cursor to the (x,y) position. (0,0) is the when defined(windows):
## upper left of the screen. var c: TCoord
when defined(windows): c.x = int16(x)
var c: TCoord c.y = int16(y)
c.x = int16(x) if SetConsoleCursorPosition(conHandle, c) == 0: OSError(OSLastError())
c.y = int16(y) else:
if SetConsoleCursorPosition(conHandle, c) == 0: OSError() stdout.write("\e[" & $y & ';' & $x & 'f')
else:
stdout.write("\e[" & $y & ';' & $x & 'f') proc setCursorXPos*(x: int) =
## sets the terminal's cursor to the x position. The y position is
proc setCursorXPos*(x: int) = ## not changed.
## sets the terminal's cursor to the x position. The y position is when defined(windows):
## not changed. var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
when defined(windows): var hStdout = conHandle
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var hStdout = conHandle var origin = scrbuf.dwCursorPosition
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() origin.x = int16(x)
var origin = scrbuf.dwCursorPosition if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
origin.x = int16(x) else:
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() stdout.write("\e[" & $x & 'G')
else:
stdout.write("\e[" & $x & 'G') when defined(windows):
proc setCursorYPos*(y: int) =
when defined(windows): ## sets the terminal's cursor to the y position. The x position is
proc setCursorYPos*(y: int) = ## not changed. **Warning**: This is not supported on UNIX!
## sets the terminal's cursor to the y position. The x position is when defined(windows):
## not changed. **Warning**: This is not supported on UNIX! var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
when defined(windows): var hStdout = conHandle
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var hStdout = conHandle var origin = scrbuf.dwCursorPosition
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() origin.y = int16(y)
var origin = scrbuf.dwCursorPosition if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
origin.y = int16(y) else:
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() nil
else:
nil proc CursorUp*(count=1) =
## Moves the cursor up by `count` rows.
proc CursorUp*(count=1) = when defined(windows):
## Moves the cursor up by `count` rows. var p = getCursorPos()
when defined(windows): dec(p.y, count)
var p = getCursorPos() setCursorPos(p.x, p.y)
dec(p.y, count) else:
setCursorPos(p.x, p.y) stdout.write("\e[" & $count & 'A')
else:
stdout.write("\e[" & $count & 'A') proc CursorDown*(count=1) =
## Moves the cursor down by `count` rows.
proc CursorDown*(count=1) = when defined(windows):
## Moves the cursor down by `count` rows. var p = getCursorPos()
when defined(windows): inc(p.y, count)
var p = getCursorPos() setCursorPos(p.x, p.y)
inc(p.y, count) else:
setCursorPos(p.x, p.y) stdout.write("\e[" & $count & 'B')
else:
stdout.write("\e[" & $count & 'B') proc CursorForward*(count=1) =
## Moves the cursor forward by `count` columns.
proc CursorForward*(count=1) = when defined(windows):
## Moves the cursor forward by `count` columns. var p = getCursorPos()
when defined(windows): inc(p.x, count)
var p = getCursorPos() setCursorPos(p.x, p.y)
inc(p.x, count) else:
setCursorPos(p.x, p.y) stdout.write("\e[" & $count & 'C')
else:
stdout.write("\e[" & $count & 'C') proc CursorBackward*(count=1) =
## Moves the cursor backward by `count` columns.
proc CursorBackward*(count=1) = when defined(windows):
## Moves the cursor backward by `count` columns. var p = getCursorPos()
when defined(windows): dec(p.x, count)
var p = getCursorPos() setCursorPos(p.x, p.y)
dec(p.x, count) else:
setCursorPos(p.x, p.y) stdout.write("\e[" & $count & 'D')
else:
stdout.write("\e[" & $count & 'D') when true:
nil
when true: else:
nil proc EraseLineEnd* =
else: ## Erases from the current cursor position to the end of the current line.
proc EraseLineEnd* = when defined(windows):
## Erases from the current cursor position to the end of the current line. nil
when defined(windows): else:
nil stdout.write("\e[K")
else:
stdout.write("\e[K") proc EraseLineStart* =
## Erases from the current cursor position to the start of the current line.
proc EraseLineStart* = when defined(windows):
## Erases from the current cursor position to the start of the current line. nil
when defined(windows): else:
nil stdout.write("\e[1K")
else:
stdout.write("\e[1K") proc EraseDown* =
## Erases the screen from the current line down to the bottom of the screen.
proc EraseDown* = when defined(windows):
## Erases the screen from the current line down to the bottom of the screen. nil
when defined(windows): else:
nil stdout.write("\e[J")
else:
stdout.write("\e[J") proc EraseUp* =
## Erases the screen from the current line up to the top of the screen.
proc EraseUp* = when defined(windows):
## Erases the screen from the current line up to the top of the screen. nil
when defined(windows): else:
nil stdout.write("\e[1J")
else:
stdout.write("\e[1J") proc EraseLine* =
## Erases the entire current line.
proc EraseLine* = when defined(windows):
## Erases the entire current line. var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
when defined(windows): var numwrote: DWORD
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO var hStdout = conHandle
var numwrote: DWORD if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var hStdout = conHandle var origin = scrbuf.dwCursorPosition
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() origin.x = 0'i16
var origin = scrbuf.dwCursorPosition if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
origin.x = 0'i16 var ht = scrbuf.dwSize.Y - origin.Y
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError() var wt = scrbuf.dwSize.X - origin.X
var ht = scrbuf.dwSize.Y - origin.Y if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
var wt = scrbuf.dwSize.X - origin.X origin, addr(numwrote)) == 0:
if FillConsoleOutputCharacter(hStdout,' ', ht*wt, OSError(OSLastError())
origin, addr(numwrote)) == 0: if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
OSError() scrbuf.dwCursorPosition, addr(numwrote)) == 0:
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt, OSError(OSLastError())
scrbuf.dwCursorPosition, addr(numwrote)) == 0: else:
OSError() stdout.write("\e[2K")
else: setCursorXPos(0)
stdout.write("\e[2K")
setCursorXPos(0) proc EraseScreen* =
## Erases the screen with the background colour and moves the cursor to home.
proc EraseScreen* = when defined(windows):
## Erases the screen with the background colour and moves the cursor to home. var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
when defined(windows): var numwrote: DWORD
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO var origin: TCoord # is inititalized to 0, 0
var numwrote: DWORD var hStdout = conHandle
var origin: TCoord # is inititalized to 0, 0 if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var hStdout = conHandle if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError() origin, addr(numwrote)) == 0:
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y, OSError(OSLastError())
origin, addr(numwrote)) == 0: if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
OSError() scrbuf.dwSize.X * scrbuf.dwSize.Y,
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, origin, addr(numwrote)) == 0:
scrbuf.dwSize.X * scrbuf.dwSize.Y, OSError(OSLastError())
origin, addr(numwrote)) == 0: setCursorXPos(0)
OSError() else:
setCursorXPos(0) stdout.write("\e[2J")
else:
stdout.write("\e[2J") proc ResetAttributes* {.noconv.} =
## resets all attributes; it is advisable to register this as a quit proc
proc ResetAttributes* {.noconv.} = ## with ``system.addQuitProc(resetAttributes)``.
## resets all attributes; it is advisable to register this as a quit proc when defined(windows):
## with ``system.addQuitProc(resetAttributes)``. discard SetConsoleTextAttribute(conHandle, oldAttr)
when defined(windows): else:
discard SetConsoleTextAttribute(conHandle, oldAttr) stdout.write("\e[0m")
else:
stdout.write("\e[0m") type
TStyle* = enum ## different styles for text output
type styleBright = 1, ## bright text
TStyle* = enum ## different styles for text output styleDim, ## dim text
styleBright = 1, ## bright text styleUnknown, ## unknown
styleDim, ## dim text styleUnderscore = 4, ## underscored text
styleUnknown, ## unknown styleBlink, ## blinking/bold text
styleUnderscore = 4, ## underscored text styleReverse = 7, ## unknown
styleBlink, ## blinking/bold text styleHidden ## hidden text
styleReverse = 7, ## unknown
styleHidden ## hidden text when not defined(windows):
var
when not defined(windows): # XXX: These better be thread-local
var gFG = 0
# XXX: These better be thread-local gBG = 0
gFG = 0
gBG = 0 proc setStyle*(style: set[TStyle]) =
## sets the terminal style
proc setStyle*(style: set[TStyle]) = when defined(windows):
## sets the terminal style var a = 0'i16
when defined(windows): if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
var a = 0'i16 if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY)
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY) if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY) if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO discard SetConsoleTextAttribute(conHandle, a)
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE else:
discard SetConsoleTextAttribute(conHandle, a) for s in items(style):
else: stdout.write("\e[" & $ord(s) & 'm')
for s in items(style):
stdout.write("\e[" & $ord(s) & 'm') proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) =
## writes the text `txt` in a given `style`.
proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) = when defined(windows):
## writes the text `txt` in a given `style`. var old = getAttributes()
when defined(windows): setStyle(style)
var old = getAttributes() stdout.write(txt)
setStyle(style) discard SetConsoleTextAttribute(conHandle, old)
stdout.write(txt) else:
discard SetConsoleTextAttribute(conHandle, old) setStyle(style)
else: stdout.write(txt)
setStyle(style) resetAttributes()
stdout.write(txt) if gFG != 0:
resetAttributes() stdout.write("\e[" & $ord(gFG) & 'm')
if gFG != 0: if gBG != 0:
stdout.write("\e[" & $ord(gFG) & 'm') stdout.write("\e[" & $ord(gBG) & 'm')
if gBG != 0:
stdout.write("\e[" & $ord(gBG) & 'm') type
TForegroundColor* = enum ## terminal's foreground colors
type fgBlack = 30, ## black
TForegroundColor* = enum ## terminal's foreground colors fgRed, ## red
fgBlack = 30, ## black fgGreen, ## green
fgRed, ## red fgYellow, ## yellow
fgGreen, ## green fgBlue, ## blue
fgYellow, ## yellow fgMagenta, ## magenta
fgBlue, ## blue fgCyan, ## cyan
fgMagenta, ## magenta fgWhite ## white
fgCyan, ## cyan
fgWhite ## white TBackgroundColor* = enum ## terminal's background colors
bgBlack = 40, ## black
TBackgroundColor* = enum ## terminal's background colors bgRed, ## red
bgBlack = 40, ## black bgGreen, ## green
bgRed, ## red bgYellow, ## yellow
bgGreen, ## green bgBlue, ## blue
bgYellow, ## yellow bgMagenta, ## magenta
bgBlue, ## blue bgCyan, ## cyan
bgMagenta, ## magenta bgWhite ## white
bgCyan, ## cyan
bgWhite ## white proc setForegroundColor*(fg: TForegroundColor, bright=false) =
## sets the terminal's foreground color
proc setForegroundColor*(fg: TForegroundColor, bright=false) = when defined(windows):
## sets the terminal's foreground color var old = getAttributes() and not 0x0007
when defined(windows): if bright:
var old = getAttributes() and not 0x0007 old = old or FOREGROUND_INTENSITY
if bright: const lookup: array [TForegroundColor, int] = [
old = old or FOREGROUND_INTENSITY 0,
const lookup: array [TForegroundColor, int] = [ (FOREGROUND_RED),
0, (FOREGROUND_GREEN),
(FOREGROUND_RED), (FOREGROUND_RED or FOREGROUND_GREEN),
(FOREGROUND_GREEN), (FOREGROUND_BLUE),
(FOREGROUND_RED or FOREGROUND_GREEN), (FOREGROUND_RED or FOREGROUND_BLUE),
(FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN),
(FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
(FOREGROUND_BLUE or FOREGROUND_GREEN), discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg]))
(FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] else:
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg])) gFG = ord(fg)
else: if bright: inc(gFG, 60)
gFG = ord(fg) stdout.write("\e[" & $gFG & 'm')
if bright: inc(gFG, 60)
stdout.write("\e[" & $gFG & 'm') proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
## sets the terminal's background color
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) = when defined(windows):
## sets the terminal's background color var old = getAttributes() and not 0x0070
when defined(windows): if bright:
var old = getAttributes() and not 0x0070 old = old or BACKGROUND_INTENSITY
if bright: const lookup: array [TBackgroundColor, int] = [
old = old or BACKGROUND_INTENSITY 0,
const lookup: array [TBackgroundColor, int] = [ (BACKGROUND_RED),
0, (BACKGROUND_GREEN),
(BACKGROUND_RED), (BACKGROUND_RED or BACKGROUND_GREEN),
(BACKGROUND_GREEN), (BACKGROUND_BLUE),
(BACKGROUND_RED or BACKGROUND_GREEN), (BACKGROUND_RED or BACKGROUND_BLUE),
(BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN),
(BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
(BACKGROUND_BLUE or BACKGROUND_GREEN), discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg]))
(BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] else:
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg])) gBG = ord(bg)
else: if bright: inc(gBG, 60)
gBG = ord(bg) stdout.write("\e[" & $gBG & 'm')
if bright: inc(gBG, 60)
stdout.write("\e[" & $gBG & 'm')
proc isatty*(f: TFile): bool = proc isatty*(f: TFile): bool =
## returns true if `f` is associated with a terminal device. ## returns true if `f` is associated with a terminal device.
@ -318,33 +316,33 @@ proc isatty*(f: TFile): bool =
importc: "_isatty", header: "<io.h>".} importc: "_isatty", header: "<io.h>".}
result = isatty(fileHandle(f)) != 0'i32 result = isatty(fileHandle(f)) != 0'i32
proc styledEchoProcessArg(s: string) = write stdout, s proc styledEchoProcessArg(s: string) = write stdout, s
proc styledEchoProcessArg(style: TStyle) = setStyle({style}) proc styledEchoProcessArg(style: TStyle) = setStyle({style})
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
macro styledEcho*(m: varargs[expr]): stmt = macro styledEcho*(m: varargs[expr]): stmt =
## to be documented. ## to be documented.
let m = callsite() let m = callsite()
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
for i in countup(1, m.len - 1): for i in countup(1, m.len - 1):
result.add(newCall(bindSym"styledEchoProcessArg", m[i])) result.add(newCall(bindSym"styledEchoProcessArg", m[i]))
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n"))) result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
result.add(newCall(bindSym"resetAttributes")) result.add(newCall(bindSym"resetAttributes"))
when isMainModule: when isMainModule:
system.addQuitProc(resetAttributes) system.addQuitProc(resetAttributes)
write(stdout, "never mind") write(stdout, "never mind")
eraseLine() eraseLine()
#setCursorPos(2, 2) #setCursorPos(2, 2)
writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore}) writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore})
setBackGroundColor(bgCyan, true) setBackGroundColor(bgCyan, true)
setForeGroundColor(fgBlue) setForeGroundColor(fgBlue)
writeln(stdout, "ordinary text") writeln(stdout, "ordinary text")
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore}) styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})

View file

@ -489,7 +489,7 @@ proc FD_SET*(Socket: TWinSocket, FDSet: var TFDSet) =
proc FD_ZERO*(FDSet: var TFDSet) = proc FD_ZERO*(FDSet: var TFDSet) =
FDSet.fd_count = 0 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.} stdcall, importc: "WSAStartup", dynlib: ws2dll.}
proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo, proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,

View file

@ -27,7 +27,7 @@ type
gshort* = cshort gshort* = cshort
glong* = clong glong* = clong
gint* = cint gint* = cint
gboolean* = bool gboolean* = distinct gint
guchar* = char guchar* = char
gushort* = int16 gushort* = int16
gulong* = int gulong* = int
@ -173,6 +173,12 @@ type
TGBoxedFreeFunc* = proc (boxed: gpointer){.cdecl.} TGBoxedFreeFunc* = proc (boxed: gpointer){.cdecl.}
PGsource = pointer # I don't know and don't care PGsource = pointer # I don't know and don't care
converter gbool*(nimbool: bool): gboolean =
return ord(nimbool).gboolean
converter toBool*(gbool: gboolean): bool =
return int(gbool) == 1
const const
G_TYPE_FUNDAMENTAL_SHIFT* = 2 G_TYPE_FUNDAMENTAL_SHIFT* = 2
G_TYPE_FUNDAMENTAL_MAX* = 255 shl G_TYPE_FUNDAMENTAL_SHIFT G_TYPE_FUNDAMENTAL_MAX* = 255 shl G_TYPE_FUNDAMENTAL_SHIFT

View file

@ -2,6 +2,8 @@
import import
glib2, atk, pango, gdk2pixbuf, gdk2 glib2, atk, pango, gdk2pixbuf, gdk2
export gbool, toBool
when defined(win32): when defined(win32):
const const
lib = "libgtk-win32-2.0-0.dll" lib = "libgtk-win32-2.0-0.dll"

View file

@ -196,7 +196,7 @@ const
proc SSL_library_init*(): cInt{.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_library_init*(): cInt{.cdecl, dynlib: DLLSSLName, importc, discardable.}
proc SSL_load_error_strings*(){.cdecl, dynlib: DLLSSLName, importc.} proc SSL_load_error_strings*(){.cdecl, dynlib: DLLSSLName, importc.}
proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLSSLName, importc.} proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
proc SSLv23_client_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_client_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv23_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
@ -262,14 +262,15 @@ proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl,
proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLSSLName, importc: "OPENSSL_add_all_algorithms_conf".} proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSSL_add_all_algorithms_conf".}
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.} proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_malloc_init*() = proc CRYPTO_malloc_init*() =
CRYPTO_set_mem_functions(alloc, realloc, dealloc) when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True: when True:
nil nil

View file

@ -32,6 +32,7 @@ Options:
--ignoreCase, -i be case insensitive --ignoreCase, -i be case insensitive
--ignoreStyle, -y be style insensitive --ignoreStyle, -y be style insensitive
--ext:EX1|EX2|... only search the files with the given extension(s) --ext:EX1|EX2|... only search the files with the given extension(s)
--nocolor output will be given without any colours.
--verbose be verbose: list every processed file --verbose be verbose: list every processed file
--help, -h shows this help --help, -h shows this help
--version, -v shows the version --version, -v shows the version
@ -291,6 +292,7 @@ for kind, key, val in getopt():
of "ignorecase", "i": incl(options, optIgnoreCase) of "ignorecase", "i": incl(options, optIgnoreCase)
of "ignorestyle", "y": incl(options, optIgnoreStyle) of "ignorestyle", "y": incl(options, optIgnoreStyle)
of "ext": extensions = val.split('|') of "ext": extensions = val.split('|')
of "nocolor": useWriteStyled = false
of "verbose": incl(options, optVerbose) of "verbose": incl(options, optVerbose)
of "help", "h": writeHelp() of "help", "h": writeHelp()
of "version", "v": writeVersion() of "version", "v": writeVersion()

View file

@ -20,7 +20,10 @@ Changes affecting backwards compatibility
- The scoping rules for the ``if`` statement changed for better interaction - The scoping rules for the ``if`` statement changed for better interaction
with the new syntactic construct ``(;)``. with the new syntactic construct ``(;)``.
- ``OSError`` family of procedures has been deprecated. Procedures with the same
name but which take different parameters have been introduced. These procs now
require an error code to be passed to them. This error code can be retrieved
using the new ``OSLastError`` proc.
Compiler Additions Compiler Additions