annotated effects of modules: os, sockets, times

This commit is contained in:
Araq
2012-11-18 13:34:48 +01:00
parent 1c17d3e841
commit ec9b1f78e1
8 changed files with 151 additions and 104 deletions

View File

@@ -28,6 +28,17 @@ else:
include "system/ansi_c"
type
FReadEnv* = object of FReadIO ## effect that denotes a read
## from an environment variable
FWriteEnv* = object of FWriteIO ## effect that denotes a write
## to an environment variable
FReadDir* = object of FReadIO ## effect that denotes a write operation to
## the directory structure
FWriteDir* = object of FWriteIO ## effect that denotes a write operation to
## the directory structure
const
doslike = defined(windows) or defined(OS2) or defined(DOS)
# DOS-like filesystem
@@ -275,7 +286,8 @@ when defined(windows):
template getFilename(f: expr): expr = $f.cFilename
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
## Returns true if the file exists, false otherwise.
when defined(windows):
when useWinUnicode:
@@ -288,7 +300,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
var res: TStat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1".} =
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
## Returns true iff the directory `dir` exists. If `dir` is a file, false
## is returned.
when defined(windows):
@@ -346,7 +358,7 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
## modification time is later than `b`'s.
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
## Returns the `current working directory`:idx:.
const bufsize = 512 # should be enough
when defined(windows):
@@ -368,7 +380,7 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
else:
OSError()
proc setCurrentDir*(newDir: string) {.inline.} =
proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
## Sets the `current working directory`:idx:; `EOS` is raised if
## `newDir` cannot been set.
when defined(Windows):
@@ -559,7 +571,8 @@ proc extractFilename*(path: string): string {.
else:
result = splitPath(path).tail
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
tags: [FReadDir].} =
## Returns the full path of `filename`, raises EOS in case of an error.
when defined(windows):
const bufsize = 3072'i32
@@ -647,7 +660,8 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
elif defined(posix):
result = path[0] == '/'
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
## Returns True if both pathname arguments refer to the same physical
## file or directory. Raises an exception if any of the files does not
## exist or information about it can not be obtained.
@@ -702,7 +716,8 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
tags: [FReadIO].} =
## Returns True if both pathname arguments refer to files with identical
## binary content.
const
@@ -732,7 +747,8 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
close(a)
close(b)
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1".} =
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [FReadIO, FWriteIO].} =
## Copies a file from `source` to `dest`. If this fails,
## `EOS` is raised.
when defined(Windows):
@@ -768,19 +784,21 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1".} =
close(s)
close(d)
proc moveFile*(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()
when not defined(ENOENT):
var ENOENT* {.importc, header: "<errno.h>".}: cint
var ENOENT {.importc, header: "<errno.h>".}: cint
proc removeFile*(file: string) {.rtl, extern: "nos$1".} =
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()
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [FExecIO].} =
## Executes a `shell command`:idx:.
##
## Command has the form 'program args' where args are the command
@@ -873,7 +891,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i
return -1
proc getEnv*(key: string): TaintedString =
proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
## Returns the value of the `environment variable`:idx: named `key`.
##
## If the variable does not exist, "" is returned. To distinguish
@@ -887,13 +905,13 @@ proc getEnv*(key: string): TaintedString =
if env == nil: return TaintedString("")
result = TaintedString($env)
proc existsEnv*(key: string): bool =
proc existsEnv*(key: string): bool {.tags: [FReadEnv].} =
## Checks whether the environment variable named `key` exists.
## Returns true if it exists, false otherwise.
if cgetenv(key) != nil: return true
else: return findEnvVar(key) >= 0
proc putEnv*(key, val: string) =
proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
## Sets the value of the `environment variable`:idx: named `key` to `val`.
## If an error occurs, `EInvalidEnvVar` is raised.
@@ -921,7 +939,7 @@ proc putEnv*(key, val: string) =
else:
if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
iterator envPairs*(): tuple[key, value: TaintedString] =
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
## Iterate over all `environments variables`:idx:. In the first component
## of the tuple is the name of the current variable stored, in the second
## its value.
@@ -931,7 +949,7 @@ iterator envPairs*(): tuple[key, value: TaintedString] =
yield (TaintedString(substr(environment[i], 0, p-1)),
TaintedString(substr(environment[i], p+1)))
iterator walkFiles*(pattern: string): string =
iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
## Iterate over all the files that match the `pattern`. On POSIX this uses
## the `glob`:idx: call.
##
@@ -969,7 +987,8 @@ type
pcDir, ## path refers to a directory
pcLinkToDir ## path refers to a symbolic link to a directory
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
tags: [FReadDir].} =
## walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item is returned.
## Walking is not recursive.
@@ -1019,7 +1038,8 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
yield (k, y)
discard closeDir(d)
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
tags: [FReadDir].} =
## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned.
## Walking is recursive. `filter` controls the behaviour of the iterator:
@@ -1052,7 +1072,8 @@ proc rawRemoveDir(dir: string) =
else:
if rmdir(dir) != 0'i32 and errno != ENOENT: OSError()
proc removeDir*(dir: string) {.rtl, extern: "nos$1".} =
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
FWriteDir, FReadDir].} =
## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively). If this fails, `EOS` is raised. This does not fail
## if the directory never existed in the first place.
@@ -1074,7 +1095,7 @@ proc rawCreateDir(dir: string) =
if res == 0'i32 and GetLastError() != 183'i32:
OSError()
proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Creates the `directory`:idx: `dir`.
##
## The directory may contain several subdirectories that do not exist yet.
@@ -1085,7 +1106,8 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
if dir[i] in {dirsep, altsep}: rawCreateDir(substr(dir, 0, i-1))
rawCreateDir(dir)
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1".} =
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
tags: [FWriteIO, FReadIO].} =
## Copies a directory from `source` to `dest`. If this fails, `EOS` is raised.
createDir(dest)
for kind, path in walkDir(source):
@@ -1199,7 +1221,7 @@ type
fpOthersRead ## read access for others
proc getFilePermissions*(filename: string): set[TFilePermission] {.
rtl, extern: "nos$1".} =
rtl, extern: "nos$1", tags: [FReadDir].} =
## retrieves file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case.
@@ -1231,7 +1253,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
result = {fpUserExec..fpOthersRead}
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
rtl, extern: "nos$1", tags: [FWriteDir].} =
## sets the file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is changed, depending on
## ``fpUserWrite``.
@@ -1268,7 +1290,7 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
## a convenience procedure for:
##
## .. code-block:: nimrod
@@ -1277,24 +1299,24 @@ proc inclFilePermissions*(filename: string,
proc exclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
## a convenience procedure for:
##
## .. code-block:: nimrod
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions)
proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
## Returns the home directory of the current user.
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
else: return string(getEnv("HOME")) & "/"
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
## Returns the config directory of the current user for applications.
when defined(windows): return string(getEnv("APPDATA")) & "\\"
else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1".} =
proc getTempDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
## Returns the temporary directory of the current user for applications to
## save temporary files in.
when defined(windows): return string(getEnv("TEMP")) & "\\"
@@ -1309,13 +1331,14 @@ when defined(windows):
var
ownArgv: seq[string]
proc paramCount*(): int {.rtl, extern: "nos$1".} =
proc paramCount*(): int {.rtl, extern: "nos$1", tags: [FReadIO].} =
## Returns the number of `command line arguments`:idx: given to the
## application.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
result = ownArgv.len-1
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1".} =
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1",
tags: [FReadIO].} =
## Returns the `i`-th `command line argument`:idx: given to the
## application.
##
@@ -1330,11 +1353,11 @@ elif not defined(createNimRtl):
cmdCount {.importc: "cmdCount".}: cint
cmdLine {.importc: "cmdLine".}: cstringArray
proc paramStr*(i: int): TaintedString =
proc paramStr*(i: int): TaintedString {.tags: [FReadIO].} =
if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
raise newException(EInvalidIndex, "invalid index")
proc paramCount*(): int = return cmdCount-1
proc paramCount*(): int {.tags: [FReadIO].} = return cmdCount-1
when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
proc getApplAux(procPath: string): string =
@@ -1357,7 +1380,7 @@ when defined(macosx):
proc getExecPath2(c: cstring, size: var cuint32): bool {.
importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}
proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
## Returns the filename of the application's executable.
## **Note**: This does not work reliably on BSD.
@@ -1413,12 +1436,12 @@ proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} =
## instead.
result = splitFile(getAppFilename()).dir
proc getAppDir*(): string {.rtl, extern: "nos$1".} =
proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
## Returns the directory of the application's executable.
## **Note**: This does not work reliably on BSD.
result = splitFile(getAppFilename()).dir
proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} =
## sleeps `milsecs` milliseconds.
when defined(windows):
winlean.sleep(int32(milsecs))
@@ -1428,7 +1451,8 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
a.tv_nsec = (milsecs mod 1000) * 1000
discard posix.nanosleep(a, b)
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
tags: [FReadIO].} =
## returns the file size of `file`. Can raise ``EOS``.
when defined(windows):
var a: TWin32FindData
@@ -1443,7 +1467,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
close(f)
else: OSError()
proc findExe*(exe: string): string =
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
## Searches for `exe` in the current working directory and then
## in directories listed in the ``PATH`` environment variable.
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`

View File

@@ -58,7 +58,7 @@ proc startProcess*(command: string,
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}):
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO].}
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].}
## Starts a process. `Command` is the executable file, `workingDir` is the
## process's working directory. If ``workingDir == ""`` the current directory
## is used. `args` are the command line arguments that are passed to the
@@ -74,7 +74,8 @@ proc startProcess*(command: string,
## but ``EOS`` is raised in case of an error.
proc startCmd*(command: string, options: set[TProcessOption] = {
poStdErrToStdOut, poUseShell}): PProcess {.tags: [FExecIO].} =
poStdErrToStdOut, poUseShell}): PProcess {.
tags: [FExecIO, FReadEnv].} =
## a simpler version of `startProcess` that parses the command line into
## program and arguments and then calls `startProcess` with the empty string
## for `workingDir` and the nil string table for `env`.
@@ -159,7 +160,7 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int {.rtl, extern: "nosp$1",
tags: [FExecIO].} =
tags: [FExecIO, FTime, FReadEnv].} =
## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes
## is returned.

View File

@@ -242,9 +242,10 @@ when defined(ssl):
## are available with the addition of ``ProtSSLv23`` which allows for
## compatibility with all of them.
##
## There are currently only two options for verify mode; one is ``CVerifyNone``
## and with it certificates will not be verified the other is ``CVerifyPeer``
## and certificates will be verified for it, ``CVerifyPeer`` is the safest choice.
## There are currently only two options for verify mode;
## one is ``CVerifyNone`` and with it certificates will not be verified
## the other is ``CVerifyPeer`` and certificates will be verified for
## it, ``CVerifyPeer`` is the safest choice.
##
## The last two parameters specify the certificate file path and the key file
## path, a server socket will most likely not work without these.
@@ -291,7 +292,7 @@ when defined(ssl):
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
SSLError()
proc listen*(socket: TSocket, backlog = SOMAXCONN) =
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
@@ -335,7 +336,8 @@ template gaiNim(a, p, h, list: expr): stmt =
else:
OSError($gai_strerror(gaiResult))
proc bindAddr*(socket: TSocket, port = TPort(0), address = "") =
proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
tags: [FReadIO].} =
## binds an address/port number to a socket.
## Use address string in dotted decimal form like "a.b.c.d"
## or leave "" for any address.
@@ -390,15 +392,6 @@ proc getSockName*(socket: TSocket): TPort =
OSError()
result = TPort(sockets.ntohs(name.sin_port))
proc selectWrite*(writefds: var seq[TSocket], timeout = 500): int
## When a socket in ``writefds`` is ready to be written to then a non-zero
## value will be returned specifying the count of the sockets which can be
## written to. The sockets which can be written to will also be removed
## from ``writefds``.
##
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
## an unlimited time.
template acceptAddrPlain(noClientRet, successRet: expr,
sslImplementation: stmt): stmt {.immediate.} =
assert(client != nil)
@@ -439,7 +432,8 @@ template acceptAddrPlain(noClientRet, successRet: expr,
else:
return successRet
proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) =
proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {.
tags: [FReadIO].} =
## Blocks until a connection is being made from a client. When a connection
## is made sets ``client`` to the client socket and ``address`` to the address
## of the connecting client.
@@ -482,7 +476,8 @@ proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) =
proc setBlocking*(s: TSocket, blocking: bool)
when defined(ssl):
proc acceptAddrSSL*(server: TSocket, client: var TSocket,
address: var string): TSSLAcceptResult =
address: var string): TSSLAcceptResult {.
tags: [FReadIO].} =
## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values:
##
@@ -531,7 +526,7 @@ when defined(ssl):
acceptAddrPlain(AcceptNoClient, AcceptSuccess):
doHandshake()
proc accept*(server: TSocket, client: var TSocket) =
proc accept*(server: TSocket, client: var TSocket) {.tags: [FReadIO].} =
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
## socket.
##
@@ -541,7 +536,8 @@ proc accept*(server: TSocket, client: var TSocket) =
var addrDummy = ""
acceptAddr(server, client, addrDummy)
proc acceptAddr*(server: TSocket): tuple[client: TSocket, address: string] {.deprecated.} =
proc acceptAddr*(server: TSocket): tuple[client: TSocket, address: string] {.
deprecated, tags: [FReadIO].} =
## Slightly different version of ``acceptAddr``.
##
## **Deprecated since version 0.9.0:** Please use the function above.
@@ -551,7 +547,7 @@ proc acceptAddr*(server: TSocket): tuple[client: TSocket, address: string] {.dep
acceptAddr(server, client, address)
return (client, address)
proc accept*(server: TSocket): TSocket {.deprecated.} =
proc accept*(server: TSocket): TSocket {.deprecated, tags: [FReadIO].} =
## **Deprecated since version 0.9.0:** Please use the function above.
new(result)
var address = ""
@@ -568,7 +564,7 @@ proc close*(socket: TSocket) =
if socket.isSSL:
discard SSLShutdown(socket.sslHandle)
proc getServByName*(name, proto: string): TServent =
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
## well-known getservbyname proc.
when defined(Windows):
var s = winlean.getservbyname(name, proto)
@@ -580,7 +576,7 @@ proc getServByName*(name, proto: string): TServent =
result.port = TPort(s.s_port)
result.proto = $s.s_proto
proc getServByPort*(port: TPort, proto: string): TServent =
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
## well-known getservbyport proc.
when defined(Windows):
var s = winlean.getservbyport(ze(int16(port)).cint, proto)
@@ -592,7 +588,7 @@ proc getServByPort*(port: TPort, proto: string): TServent =
result.port = TPort(s.s_port)
result.proto = $s.s_proto
proc getHostByAddr*(ip: string): THostEnt =
proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
## This function will lookup the hostname of an IP Address.
var myaddr: TInAddr
myaddr.s_addr = inet_addr(ip)
@@ -621,7 +617,7 @@ proc getHostByAddr*(ip: string): THostEnt =
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getHostByName*(name: string): THostEnt =
proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
## well-known gethostbyname proc.
when defined(Windows):
var s = winlean.gethostbyname(name)
@@ -642,7 +638,8 @@ proc getHostByName*(name: string): THostEnt =
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
proc getSockOptInt*(socket: TSocket, level, optname: int): int =
proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
tags: [FReadIO].} =
## getsockopt for integer options.
var res: cint
var size = sizeof(res).TSockLen
@@ -651,7 +648,8 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int =
OSError()
result = int(res)
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) =
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
tags: [FWriteIO].} =
## setsockopt for integer options.
var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
@@ -659,7 +657,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) =
OSError()
proc connect*(socket: TSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) =
af: TDomain = AF_INET) {.tags: [FReadIO].} =
## Connects socket to ``name``:``port``. ``Name`` can be an IP address or a
## host name. If ``name`` is a host name, this function will try each IP
## of that host name. ``htons`` is already performed on ``port`` so you must
@@ -719,7 +717,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
OSError()
proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
af: TDomain = AF_INET) =
af: TDomain = AF_INET) {.tags: [FReadIO].} =
## A variant of ``connect`` for non-blocking sockets.
##
## This procedure will immediatelly return, it will not block until a connection
@@ -764,7 +762,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
socket.sslNoHandshake = true
when defined(ssl):
proc handshake*(socket: TSocket): bool =
proc handshake*(socket: TSocket): bool {.tags: [FReadIO, RWriteIO].} =
## This proc needs to be called on a socket after it connects. This is
## only applicable when using ``connectAsync``.
## This proc performs the SSL handshake.
@@ -854,7 +852,7 @@ proc checkBuffer(readfds: var seq[TSocket]): int =
readfds = res
proc select*(readfds, writefds, exceptfds: var seq[TSocket],
timeout = 500): int =
timeout = 500): int {.tags: [FReadIO].} =
## Traditional select function. This function will return the number of
## sockets that are ready to be read from, written to, or which have errors
## if there are none; 0 is returned.
@@ -884,7 +882,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
pruneSocketSet(exceptfds, (ex))
proc select*(readfds, writefds: var seq[TSocket],
timeout = 500): int =
timeout = 500): int {.tags: [FReadIO].} =
## variant of select with only a read and write list.
let buffersFilled = checkBuffer(readfds)
if buffersFilled > 0:
@@ -905,7 +903,14 @@ proc select*(readfds, writefds: var seq[TSocket],
pruneSocketSet(writefds, (wr))
proc selectWrite*(writefds: var seq[TSocket],
timeout = 500): int =
timeout = 500): int {.tags: [FReadIO].} =
## When a socket in ``writefds`` is ready to be written to then a non-zero
## value will be returned specifying the count of the sockets which can be
## written to. The sockets which can be written to will also be removed
## from ``writefds``.
##
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
## an unlimited time.
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
var wr: TFdSet
@@ -961,7 +966,7 @@ template retRead(flags, readBytes: int) =
else:
return res
proc recv*(socket: TSocket, data: pointer, size: int): int =
proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} =
## receives data from a socket
if socket.isBuffered:
if socket.bufLen == 0:
@@ -999,7 +1004,8 @@ proc recv*(socket: TSocket, data: pointer, size: int): int =
else:
result = recv(socket.fd, data, size.cint, 0'i32)
proc waitFor(socket: TSocket, waited: var float, timeout: int): int =
proc waitFor(socket: TSocket, waited: var float, timeout: int): int {.
tags: [FTime].} =
## returns the number of characters available to be read. In unbuffered
## sockets this is always 1, otherwise this may as big as the buffer, currently
## 4000.
@@ -1015,7 +1021,8 @@ proc waitFor(socket: TSocket, waited: var float, timeout: int): int =
raise newException(ETimeout, "Call to recv() timed out.")
waited += (epochTime() - startTime)
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int =
proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
tags: [FReadIO, FTime].} =
## overload with a ``timeout`` parameter in miliseconds.
var waited = 0.0 # number of seconds already waited
@@ -1031,7 +1038,7 @@ proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int =
result = read
proc peekChar(socket: TSocket, c: var char): int =
proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} =
if socket.isBuffered:
result = 1
if socket.bufLen == 0 or socket.currPos > socket.bufLen-1:
@@ -1047,7 +1054,8 @@ proc peekChar(socket: TSocket, c: var char): int =
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
proc recvLine*(socket: TSocket, line: var TaintedString): bool =
proc recvLine*(socket: TSocket, line: var TaintedString): bool {.
tags: [FReadIO].} =
## retrieves a line from ``socket``. If a full line is received ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is received then ``line``
## will be set to it.
@@ -1082,7 +1090,8 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool =
return true
add(line.string, c)
proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool =
proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool {.
tags: [FReadIO, FTime].} =
## variant with a ``timeout`` parameter, the timeout parameter specifies
## how many miliseconds to wait for data.
template addNLIfEmpty(): stmt =
@@ -1111,7 +1120,8 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool =
return true
add(line.string, c)
proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult =
proc recvLineAsync*(socket: TSocket,
line: var TaintedString): TRecvLineResult {.tags: [FReadIO].} =
## similar to ``recvLine`` but for non-blocking sockets.
## The values of the returned enum should be pretty self explanatory:
## If a full line has been retrieved; ``RecvFullLine`` is returned.
@@ -1136,7 +1146,7 @@ proc recvLineAsync*(socket: TSocket, line: var TaintedString): TRecvLineResult =
elif c == '\L': return RecvFullLine
add(line.string, c)
proc recv*(socket: TSocket): TaintedString =
proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO].} =
## receives all the available data from the socket.
## Socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
@@ -1165,7 +1175,8 @@ proc recv*(socket: TSocket): TaintedString =
add(result.string, buf)
if bytesRead != bufSize-1: break
proc recvTimeout*(socket: TSocket, timeout: int): TaintedString =
proc recvTimeout*(socket: TSocket, timeout: int): TaintedString {.
tags: [FReadIO].} =
## overloaded variant to support a ``timeout`` parameter, the ``timeout``
## parameter specifies the amount of miliseconds to wait for data on the
## socket.
@@ -1176,7 +1187,8 @@ proc recvTimeout*(socket: TSocket, timeout: int): TaintedString =
return socket.recv
proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
tags: [FReadIO].} =
## receives all the data from a non-blocking socket. If socket is non-blocking
## and there are no messages available, `False` will be returned.
## Other socket errors will result in an ``EOS`` error.
@@ -1226,7 +1238,8 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
result = True
proc recvFrom*(socket: TSocket, data: var string, length: int,
address: var string, port: var TPort, flags = 0'i32): int =
address: var string, port: var TPort, flags = 0'i32): int {.
tags: [FReadIO].} =
## Receives data from ``socket``. This function should normally be used with
## connection-less sockets (UDP sockets).
##
@@ -1248,7 +1261,8 @@ proc recvFrom*(socket: TSocket, data: var string, length: int,
port = ntohs(sockAddress.sin_port).TPort
proc recvFromAsync*(socket: TSocket, data: var String, length: int,
address: var string, port: var TPort, flags = 0'i32): bool =
address: var string, port: var TPort,
flags = 0'i32): bool {.tags: [FReadIO].} =
## Similar to ``recvFrom`` but raises an EOS error when an error occurs and
## is also meant for non-blocking sockets.
## Returns False if no messages could be received from ``socket``.
@@ -1266,14 +1280,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
return False
else: OSError()
proc skip*(socket: TSocket) =
proc skip*(socket: TSocket) {.tags: [FReadIO].} =
## skips all the data that is pending for the socket
const bufSize = 1000
var buf = alloc(bufSize)
while recv(socket, buf, bufSize) == bufSize: nil
dealloc(buf)
proc send*(socket: TSocket, data: pointer, size: int): int =
proc send*(socket: TSocket, data: pointer, size: int): int {.
tags: [FWriteIO].} =
## sends data to a socket.
when defined(ssl):
if socket.isSSL:
@@ -1284,7 +1299,7 @@ proc send*(socket: TSocket, data: pointer, size: int): int =
else:
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
proc send*(socket: TSocket, data: string) =
proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
## sends data to a socket.
if send(socket, cstring(data), data.len) != data.len:
when defined(ssl):
@@ -1293,7 +1308,7 @@ proc send*(socket: TSocket, data: string) =
OSError()
proc sendAsync*(socket: TSocket, data: string): bool =
proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
## sends data to a non-blocking socket. Returns whether ``data`` was sent.
result = true
var bytesSent = send(socket, cstring(data), data.len)
@@ -1327,13 +1342,14 @@ proc sendAsync*(socket: TSocket, data: string): bool =
return false
else: OSError()
proc trySend*(socket: TSocket, data: string): bool =
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
## and instead returns ``false`` on failure.
result = send(socket, cstring(data), data.len) == data.len
proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer,
size: int, af: TDomain = AF_INET, flags = 0'i32): int =
size: int, af: TDomain = AF_INET, flags = 0'i32): int {.
tags: [FWriteIO].} =
## low-level sendTo proc. This proc sends ``data`` to the specified ``address``,
## which may be an IP address or a hostname, if a hostname is specified
## this function will try each IP of that hostname.
@@ -1359,7 +1375,8 @@ proc sendTo*(socket: TSocket, address: string, port: TPort, data: pointer,
freeaddrinfo(aiList)
proc sendTo*(socket: TSocket, address: string, port: TPort, data: string): int =
proc sendTo*(socket: TSocket, address: string, port: TPort,
data: string): int {.tags: [FWriteIO].} =
## Friendlier version of the low-level ``sendTo``.
result = socket.sendTo(address, port, cstring(data), data.len)
@@ -1391,7 +1408,7 @@ proc setBlocking*(s: TSocket, blocking: bool) =
OSError()
proc connect*(socket: TSocket, timeout: int, name: string, port = TPort(0),
af: TDomain = AF_INET) =
af: TDomain = AF_INET) {.tags: [FReadIO].} =
## Overload for ``connect`` to support timeouts. The ``timeout`` parameter
## specifies the time in miliseconds of how long to wait for a connection
## to be made.

View File

@@ -131,7 +131,7 @@ type
months*: int ## The number of months
years*: int ## The number of years
proc getTime*(): TTime ## gets the current calendar time
proc getTime*(): TTime {.tags: [FTime].} ## gets the current calendar time
proc getLocalTime*(t: TTime): TTimeInfo
## converts the calendar time `t` to broken-time representation,
## expressed relative to the user's specified time zone.
@@ -164,14 +164,14 @@ proc `<=` * (a, b: TTime): bool {.
## returns true iff ``a <= b``.
result = a - b <= 0
proc getTzname*(): tuple[nonDST, DST: string]
proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime].}
## returns the local timezone; ``nonDST`` is the name of the local non-DST
## timezone, ``DST`` is the name of the local DST timezone.
proc getTimezone*(): int
proc getTimezone*(): int {.tags: [FTime].}
## returns the offset of the local (non-DST) timezone in seconds west of UTC.
proc getStartMilsecs*(): int {.deprecated.}
proc getStartMilsecs*(): int {.deprecated, tags: [FTime].}
## get the miliseconds from the start of the program. **Deprecated since
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
@@ -247,12 +247,12 @@ proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo =
result = getLocalTime(TTime(float(t) - secs))
when not defined(ECMAScript):
proc epochTime*(): float {.rtl, extern: "nt$1".}
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [FTime].}
## gets time after the UNIX epoch (1970) in seconds. It is a float
## because sub-second resolution is likely to be supported (depending
## on the hardware/OS).
proc cpuTime*(): float {.rtl, extern: "nt$1".}
proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [FTime].}
## gets time spent that the CPU spent to run the current process in
## seconds. This may be more useful for benchmarking than ``epochTime``.
## However, it may measure the real time instead (depending on the OS).
@@ -478,13 +478,13 @@ else:
return int(getTime() - startMilsecs)
proc getDateStr*(): string {.rtl, extern: "nt$1".} =
proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} =
## gets the current date as a string of the format ``YYYY-MM-DD``.
var ti = getLocalTime(getTime())
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
'-' & intToStr(ti.monthDay, 2)
proc getClockStr*(): string {.rtl, extern: "nt$1".} =
proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} =
## gets the current clock time as a string of the format ``HH:MM:SS``.
var ti = getLocalTime(getTime())
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &

View File

@@ -1,5 +1,5 @@
discard """
line: 47
line: 49
file: "sysio.nim"
errormsg: "can raise an unlisted exception: ref EIO"
"""

View File

@@ -11,7 +11,7 @@ type
EIO2 = ref object of EIO
proc raiser(): int {.tags: [TObj].} =
proc raiser(): int {.tags: [TObj, FWriteIO].} =
writeln stdout, "arg"
var o: TObjB

View File

@@ -1,7 +1,11 @@
version 0.9.2
=============
- make the stdlib aware of tags: sockets, os
- improve 'doc2':
* respect output directory
* write only 1 file unless requested 'whole project'
* make it work for the 'system' module
* the documentation system should default to 'doc2'
- test&finish first class iterators:
* allow return in first class iterators
* nested iterators

View File

@@ -8,9 +8,10 @@ Please choose your platform:
(includes GCC and everything else you need)
The source-based installation has been tested on these systems:
* Linux: i386, AMD64, PowerPC
* Linux: i386, AMD64, PowerPC, ARM
* BSD: AMD64
* Mac OS X: i386, AMD64
* Solaris: AMD64
Other UNIX-based operating systems may work.