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

This commit is contained in:
Araq
2013-06-27 01:05:19 +02:00
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.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``.
@@ -423,6 +423,10 @@ proc isConnecting*(s: PAsyncSocket): bool =
proc isClosed*(s: PAsyncSocket): bool =
## Determines whether ``s`` has been closed.
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,
handleWrite: proc (s: PAsyncSocket) {.closure.}) =
@@ -638,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()

View File

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

View File

@@ -264,7 +264,7 @@ when defined(Windows) and not defined(useNimRtl):
# TRUE and zero bytes returned (EOF).
# TRUE and n (>0) bytes returned (good data).
# 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
result = br
@@ -272,7 +272,7 @@ when defined(Windows) and not defined(useNimRtl):
var s = PFileHandleStream(s)
var bytesWritten: int32
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 =
new(result)
@@ -313,7 +313,7 @@ when defined(Windows) and not defined(useNimRtl):
piInheritablePipe.lpSecurityDescriptor = nil
piInheritablePipe.Binherithandle = 1
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
OSError()
OSError(OSLastError())
proc fileClose(h: THandle) {.inline.} =
if h > 4: discard CloseHandle(h)
@@ -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)
@@ -473,7 +474,7 @@ when defined(Windows) and not defined(useNimRtl):
of WAIT_TIMEOUT:
return 0
of WAIT_FAILED:
OSError()
OSError(OSLastError())
else:
var i = ret - WAIT_OBJECT_0
readfds.del(i)
@@ -531,7 +532,7 @@ elif not defined(useNimRtl):
if poParentStreams notin options:
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or
pipe(p_stderr) != 0'i32:
OSError()
OSError(OSLastError())
var pid: TPid
when defined(posix_spawn) and not defined(useFork):
@@ -539,7 +540,7 @@ elif not defined(useNimRtl):
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
if e != 0'i32: OSError()
if e != 0'i32: OSError(OSLastError())
chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr)
@@ -584,20 +585,20 @@ elif not defined(useNimRtl):
else:
Pid = fork()
if Pid < 0: OSError()
if Pid < 0: OSError(OSLastError())
if pid == 0:
## child process:
if poParentStreams notin options:
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])
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError()
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError(OSLastError())
discard close(p_stderr[readIdx])
if poStdErrToStdOut in options:
if dup2(p_stdout[writeIdx], 2) < 0: OSError()
if dup2(p_stdout[writeIdx], 2) < 0: OSError(OSLastError())
else:
if dup2(p_stderr[writeIdx], 2) < 0: OSError()
if dup2(p_stderr[writeIdx], 2) < 0: OSError(OSLastError())
# Create a new process group
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
@@ -652,10 +653,10 @@ elif not defined(useNimRtl):
discard close(p.errorHandle)
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) =
if kill(-p.id, SIGCONT) != 0'i32: OSError()
if kill(-p.id, SIGCONT) != 0'i32: OSError(OSLastError())
proc running(p: PProcess): bool =
var ret = waitPid(p.id, p.exitCode, WNOHANG)
@@ -665,8 +666,8 @@ elif not defined(useNimRtl):
proc terminate(p: PProcess) =
if kill(-p.id, SIGTERM) == 0'i32:
if p.running():
if kill(-p.id, SIGKILL) != 0'i32: OSError()
else: OSError()
if kill(-p.id, SIGKILL) != 0'i32: OSError(OSLastError())
else: OSError(OSLastError())
proc waitForExit(p: PProcess, timeout: int = -1): int =
#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 waitPid(p.id, p.exitCode, 0) < 0:
p.exitCode = -3
OSError()
OSError(OSLastError())
result = int(p.exitCode) shr 8
proc peekExitCode(p: PProcess): int =
@@ -690,7 +691,7 @@ elif not defined(useNimRtl):
proc createStream(stream: var PStream, handle: var TFileHandle,
fileMode: TFileMode) =
var f: TFile
if not open(f, handle, fileMode): OSError()
if not open(f, handle, fileMode): OSError(OSLastError())
stream = newFileStream(f)
proc inputStream(p: PProcess): PStream =

View File

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

View File

@@ -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())

View File

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

View File

@@ -27,7 +27,7 @@ type
gshort* = cshort
glong* = clong
gint* = cint
gboolean* = bool
gboolean* = distinct gint
guchar* = char
gushort* = int16
gulong* = int
@@ -173,6 +173,12 @@ type
TGBoxedFreeFunc* = proc (boxed: gpointer){.cdecl.}
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
G_TYPE_FUNDAMENTAL_SHIFT* = 2
G_TYPE_FUNDAMENTAL_MAX* = 255 shl G_TYPE_FUNDAMENTAL_SHIFT

View File

@@ -2,6 +2,8 @@
import
glib2, atk, pango, gdk2pixbuf, gdk2
export gbool, toBool
when defined(win32):
const
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_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_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_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 CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_malloc_init*() =
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True:
nil