Merge branch 'devel' of git://github.com/Araq/Nimrod

This commit is contained in:
Miguel
2014-02-10 01:20:42 +04:00
96 changed files with 3070 additions and 1912 deletions

View File

@@ -294,19 +294,6 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## if not `ex`:
## echo `info` & ": Check failed: " & `expString`
when not defined(booting):
template emit*(e: static[string]): stmt =
## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program
## Example:
##
## .. code-block:: nimrod
## emit("echo " & '"' & "hello world".toUpper & '"')
##
macro payload: stmt {.gensym.} =
result = e.parseStmt
payload()
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
@@ -421,7 +408,8 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"')
of nnkSym, nnkNone: assert false
of nnkSym: add(result, $n.symbol)
of nnkNone: assert false
else:
add(result, lispRepr(n[0]))
for j in 1..n.len-1:
@@ -745,3 +733,15 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} =
else: discard
dest.add(ident(ident))
when not defined(booting):
template emit*(e: static[string]): stmt =
## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program
## Example:
##
## .. code-block:: nimrod
## emit("echo " & '"' & "hello world".toUpper & '"')
##
macro payload: stmt {.gensym.} =
result = parseStmt(e)
payload()

View File

@@ -58,7 +58,7 @@ proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
## be established.
init(result)
let x = connect(result, host, port.cint)
let x = client(result, host, port.cint)
if x != 0'i32:
dbError(result, "cannot open: " & host)
@@ -119,7 +119,7 @@ proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
## the generated OID for the ``_id`` field.
result = genOid()
var x = jsonToBSon(data, result)
insert(db, namespace, x)
insert(db, namespace, x, nil)
destroy(x)
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.

View File

@@ -148,7 +148,8 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
@@ -157,7 +158,8 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
if step(stmt) == SQLITE_DONE:
result = last_insert_rowid(db)
if finalize(stmt) != SQLITE_OK: dbError(db)
if finalize(stmt) != SQLITE_OK:
result = -1
proc insertID*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =

View File

@@ -19,7 +19,7 @@ type
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
gtOperator, gtPunctation, gtComment, gtLongComment, gtRegularExpression,
gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression,
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
gtReference, gtOther
@@ -39,7 +39,7 @@ const
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
"EscapeSequence", "Operator", "Punctation", "Comment", "LongComment",
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
"Label", "Reference", "Other"]
@@ -258,7 +258,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
else: inc(pos)
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
inc(pos)
g.kind = gtPunctation
g.kind = gtPunctuation
of '\0':
g.kind = gtEof
else:
@@ -473,7 +473,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
else: inc(pos)
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
inc(pos)
g.kind = gtPunctation
g.kind = gtPunctuation
of '\0':
g.kind = gtEof
else:

88
lib/posix/epoll.nim Normal file
View File

@@ -0,0 +1,88 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
const
EPOLLIN* = 0x00000001
EPOLLPRI* = 0x00000002
EPOLLOUT* = 0x00000004
EPOLLERR* = 0x00000008
EPOLLHUP* = 0x00000010
EPOLLRDNORM* = 0x00000040
EPOLLRDBAND* = 0x00000080
EPOLLWRNORM* = 0x00000100
EPOLLWRBAND* = 0x00000200
EPOLLMSG* = 0x00000400
EPOLLRDHUP* = 0x00002000
EPOLLWAKEUP* = 1 shl 29
EPOLLONESHOT* = 1 shl 30
EPOLLET* = 1 shl 31
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
type
epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
thePtr* {.importc: "ptr".}: pointer # \
#fd*: cint
#u32*: uint32
#u64*: uint64
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events
data*: epoll_data # User data variable
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
header: "<sys/epoll.h>".}
## Creates an epoll instance. Returns an fd for the new instance.
## The "size" parameter is a hint specifying the number of file
## descriptors to be associated with the new instance. The fd
## returned by epoll_create() should be closed with close().
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
header: "<sys/epoll.h>".}
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
## constants defined above. The "fd" parameter is the target of the
## operation. The "event" parameter describes which events the caller
## is interested in and any associated user data.
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait",
header: "<sys/epoll.h>".}
## Wait for events on an epoll instance "epfd". Returns the number of
## triggered events returned in "events" buffer. Or -1 in case of
## error with the "errno" variable set to the specific error code. The
## "events" parameter is a buffer that will contain triggered
## events. The "maxevents" is the maximum number of events to be
## returned ( usually size of "events" ). The "timeout" parameter
## specifies the maximum wait time in milliseconds (-1 == infinite).
##
## This function is a cancellation point and therefore not marked with
## __THROW.
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
# timeout: cint; ss: ptr sigset_t): cint {.
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
# Same as epoll_wait, but the thread's signal mask is temporarily
# and atomically replaced with the one provided as parameter.
#
# This function is a cancellation point and therefore not marked with
# __THROW.

File diff suppressed because it is too large Load Diff

View File

@@ -131,3 +131,36 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2)
s = s*2
proc product*[T](x: openarray[seq[T]]): seq[seq[T]] =
## produces the Cartesian product of the array. Warning: complexity
## may explode.
result = @[]
if x.len == 0:
return
if x.len == 1:
result = @x
return
var
indexes = newSeq[int](x.len)
initial = newSeq[int](x.len)
index = 0
# replace with newSeq as soon as #853 is fixed
var next: seq[T] = @[]
next.setLen(x.len)
for i in 0..(x.len-1):
if len(x[i]) == 0: return
initial[i] = len(x[i])-1
indexes = initial
while true:
while indexes[index] == -1:
indexes[index] = initial[index]
index +=1
if index == x.len: return
indexes[index] -=1
for ni, i in indexes:
next[ni] = x[ni][i]
var res: seq[T]
shallowCopy(res, next)
result.add(res)
index = 0
indexes[index] -=1

View File

@@ -689,5 +689,5 @@ when isMainModule:
server.listen()
d.register(server)
while d.poll(-1): nil
while d.poll(-1): discard

485
lib/pure/asyncio2.nim Normal file
View File

@@ -0,0 +1,485 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import os, oids, tables, strutils
import winlean
import sockets2, net
## Asyncio2
## --------
##
## This module implements a brand new asyncio module based on Futures.
## IOCP is used under the hood on Windows and the selectors module is used for
## other operating systems.
# -- Futures
type
PFutureVoid* = ref object of PObject
cbVoid: proc () {.closure.}
finished: bool
PFuture*[T] = ref object of PFutureVoid
value: T
error: ref EBase
cb: proc (future: PFuture[T]) {.closure.}
proc newFuture*[T](): PFuture[T] =
## Creates a new future.
new(result)
result.finished = false
proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``.
assert(not future.finished)
assert(future.error == nil)
future.value = val
future.finished = true
if future.cb != nil:
future.cb(future)
if future.cbVoid != nil:
future.cbVoid()
proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``.
assert(not future.finished)
future.finished = true
future.error = error
if future.cb != nil:
future.cb(future)
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.cb = cb
if future.finished:
future.cb(future)
proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
## Sets the **void** callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: This is used for the ``await`` functionality, you most likely
## want to use ``callback``.
future.cbVoid = cb
if future.finished:
future.cbVoid()
proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``EInvalidValue`` exception.
##
## If the result of the future is an error then that error will be raised.
if future.finished:
if future.error != nil: raise future.error
return future.value
else:
# TODO: Make a custom exception type for this?
raise newException(EInvalidValue, "Future still in progress.")
proc finished*[T](future: PFuture[T]): bool =
## Determines whether ``future`` has completed.
##
## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
future.finished
proc failed*[T](future: PFuture[T]): bool =
## Determines whether ``future`` completed with an error.
future.error != nil
when defined(windows):
type
TCompletionKey = dword
TCompletionData* = object
sock: TSocketHandle
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object
ioPort: THandle
TCustomOverlapped = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
data*: TCompletionData
PCustomOverlapped = ptr TCustomOverlapped
proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance.
new result
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
proc register*(p: PDispatcher, sock: TSocketHandle) =
## Registers ``sock`` with the dispatcher ``p``.
if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError())
proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them.
let llTimeout =
if timeout == -1: winlean.INFINITE
else: timeout.int32
var lpNumberOfBytesTransferred: DWORD
var lpCompletionKey: ULONG
var lpOverlapped: POverlapped
let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
# http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
dealloc(customOverlapped)
else:
let errCode = OSLastError()
if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
dealloc(customOverlapped)
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
discard
else: OSError(errCode)
var connectExPtr: pointer = nil
var acceptExPtr: pointer = nil
var getAcceptExSockAddrsPtr: pointer = nil
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWord
func = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAll() =
let dummySock = socket()
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
OSError(OSLastError())
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
OSError(OSLastError())
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
OSError(OSLastError())
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
let func =
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
lpOverlapped)
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool =
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
let func =
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped)
proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
if getAcceptExSockAddrsPtr.isNil:
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
let func =
cast[proc (lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength)
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
## Connects ``socket`` to server at ``address:port``.
##
## Returns a ``PFuture`` which will complete when the connection succeeds
## or an error occurs.
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
# Apparently ``ConnectEx`` expects the socket to be initially bound:
var saddr: Tsockaddr_in
saddr.sin_family = int16(toInt(af))
saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
sizeof(saddr).TSockLen) < 0'i32:
OSError(OSLastError())
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
# "the OVERLAPPED structure must remain valid until the I/O completes"
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
nil, 0, nil, cast[POverlapped](ol))
if ret:
# Request to connect completed immediately.
success = true
retFuture.complete(0)
dealloc(ol)
break
else:
lastError = OSLastError()
if lastError.int32 == ERROR_IO_PENDING:
# In this case ``ol`` will be deallocated in ``poll``.
success = true
break
else:
dealloc(ol)
success = false
it = it.ai_next
dealloc(aiList)
if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once
## all of the requested data is read.
var retFuture = newFuture[string]()
var dataBuf: TWSABuf
dataBuf.buf = newString(size)
dataBuf.len = size
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
addr flags, cast[POverlapped](ol), nil)
if ret == -1:
let err = OSLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
# Request to read completed immediately.
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
dealloc(ol)
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent.
var retFuture = newFuture[int]()
var dataBuf: TWSABuf
dataBuf.buf = data
dataBuf.len = data.len
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
flags, cast[POverlapped](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
retFuture.complete(0)
dealloc(ol)
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
var clientSock = socket()
if clientSock == OSInvalidSocket: osError(osLastError())
const lpOutputLen = 1024
var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: DWORD
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
template completeAccept(): stmt {.immediate, dirty.} =
var listenSock = socket
let setoptRet = setsockopt(clientSock, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
sizeof(listenSock).TSockLen)
if setoptRet != 0: osError(osLastError())
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
var localLen, remoteLen: int32
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength,
addr LocalSockaddr, addr localLen,
addr RemoteSockaddr, addr remoteLen)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete(
(address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
client: clientSock)
)
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
completeAccept()
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
addr dwBytesReceived, cast[POverlapped](ol))
if not ret:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
completeAccept()
dealloc(ol)
return retFuture
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection.
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TSocketHandle]()
var fut = p.acceptAddr(socket)
fut.callback =
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
assert future.finished
if future.failed:
retFut.fail(future.error)
else:
retFut.complete(future.read.client)
return retFut
initAll()
else:
# TODO: Selectors.
when isMainModule:
var p = newDispatcher()
var sock = socket()
#sock.setBlocking false
p.register(sock)
when true:
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
f.callback =
proc (future: PFuture[int]) =
echo("Connected in future!")
echo(future.read)
for i in 0 .. 50:
var recvF = p.recv(sock, 10)
recvF.callback =
proc (future: PFuture[string]) =
echo("Read: ", future.read)
else:
sock.bindAddr(TPort(6667))
sock.listen()
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) =
echo(future.read)
var f = p.accept(sock)
f.callback = onAccept
var f = p.accept(sock)
f.callback = onAccept
while true:
p.poll()
echo "polled"

View File

@@ -276,6 +276,38 @@ template foldr*(sequence, operation: expr): expr =
result = operation
result
template mapIt*(seq1, typ, pred: expr): expr =
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. You also need to pass as `typ` the type of the expression,
## since the new returned sequence can have a different type than the
## original. Example:
##
## .. code-block:: nimrod
## let
## nums = @[1, 2, 3, 4]
## strings = nums.mapIt(string, $(4 * it))
var result {.gensym.}: seq[typ] = @[]
for it {.inject.} in items(seq1):
result.add(pred)
result
template mapIt*(varSeq, pred: expr) =
## Convenience template around the mutable ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. The expression has to return the same type as the sequence you
## are mutating. Example:
##
## .. code-block:: nimrod
## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3)
## assert nums[0] + nums[3] == 15
for i in 0 .. <len(varSeq):
let it {.inject.} = varSeq[i]
varSeq[i] = pred
when isMainModule:
import strutils
block: # concat test
@@ -381,4 +413,11 @@ when isMainModule:
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
block: # mapIt tests
var
nums = @[1, 2, 3, 4]
strings = nums.mapIt(string, $(4 * it))
nums.mapIt(it * 3)
assert nums[0] + nums[3] == 15
echo "Finished doc tests"

View File

@@ -224,3 +224,20 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
proc `$`*[A](s: TOrderedSet[A]): string =
## The `$` operator for ordered hash sets.
dollarImpl()
proc `<`*[A](s, t: TSet[A]): bool =
## Is s a strict subset of t?
s.counter != t.counter and s <= t
proc `<=`*[A](s, t: TSet[A]): bool =
## Is s a subset of t?
result = false
if s.counter > t.counter: return
result = true
for item in s:
if not(t.contains(item)):
result = false
return
proc `==`*[A](s, t: TSet[A]): bool =
s.counter == t.counter and s <= t

View File

@@ -189,6 +189,16 @@ template dollarImpl(): stmt {.dirty.} =
proc `$`*[A, B](t: TTable[A, B]): string =
## The `$` operator for hash tables.
dollarImpl()
proc `==`*[A, B](s, t: TTable[A, B]): bool =
s.counter == t.counter and s.data == t.data
proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] =
## Index the collection with the proc provided.
# TODO: As soon as supported, change collection: A to collection: A[B]
result = initTable[C, B]()
for item in collection:
result[index(item)] = item
# ------------------------------ ordered table ------------------------------

View File

@@ -14,15 +14,15 @@
type
TLibHandle* = pointer ## a handle to a dynamically loaded library
proc LoadLib*(path: string): TLibHandle
proc loadLib*(path: string): TLibHandle
## loads a library from `path`. Returns nil if the library could not
## be loaded.
proc LoadLib*(): TLibHandle
proc loadLib*(): TLibHandle
## gets the handle from the current executable. Returns nil if the
## library could not be loaded.
proc UnloadLib*(lib: TLibHandle)
proc unloadLib*(lib: TLibHandle)
## unloads the library `lib`
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
@@ -60,9 +60,9 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".}
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc UnloadLib(lib: TLibHandle) = dlclose(lib)
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer =
return dlsym(lib, name)
@@ -78,14 +78,14 @@ elif defined(windows) or defined(dos):
proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
proc winLoadLibrary(path: cstring): THINSTANCE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
proc GetProcAddress(lib: THINSTANCE, name: cstring): pointer {.
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc LoadLib(path: string): TLibHandle =
proc loadLib(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
proc LoadLib(): TLibHandle =
proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil))
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
proc symAddr(lib: TLibHandle, name: cstring): pointer =
result = GetProcAddress(cast[THINSTANCE](lib), name)

View File

@@ -480,7 +480,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
errors.add(expected(x, result))
break
else: nil
else: discard
result.addNode(parse(x, errors))
of xmlElementEnd:
if cmpIgnoreCase(x.elemName, result.tag) == 0:
@@ -547,7 +547,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
var u = entityToUtf8(x.rawData)
if u.len != 0: result = newText(u)
next(x)
of xmlEof: nil
of xmlEof: discard
proc parseHtml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode =

View File

@@ -476,12 +476,12 @@ when isMainModule:
var client = irc("amber.tenthbit.net", nick="TestBot1234",
joinChans = @["#flood"])
client.connect()
while True:
while true:
var event: TIRCEvent
if client.poll(event):
case event.typ
of EvConnected:
nil
discard
of EvDisconnected:
break
of EvMsg:

267
lib/pure/logging.nim Normal file
View File

@@ -0,0 +1,267 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a simple logger. It has been designed to be as simple
## as possible to avoid bloat, if this library does not fullfill your needs,
## write your own.
##
## Format strings support the following variables which must be prefixed with
## the dollar operator (``$``):
##
## ============ =======================
## Operator Output
## ============ =======================
## $date Current date
## $time Current time
## $app ``os.getAppFilename()``
## ============ =======================
##
##
## The following example demonstrates logging to three different handlers
## simultaneously:
##
## .. code-block:: nimrod
##
## var L = newConsoleLogger()
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
## handlers.add(L)
## handlers.add(fL)
## handlers.add(rL)
## info("920410:52 accepted")
## warn("4 8 15 16 23 4-- Error")
## error("922044:16 SYSTEM FAILURE")
## fatal("SYSTEM FAILURE SYSTEM FAILURE")
import strutils, os, times
type
TLevel* = enum ## logging level
lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active
lvlWarn, ## warn level (and any above) active
lvlError, ## error level (and any above) active
lvlFatal, ## fatal level (and any above) active
lvlNone ## no levels active
const
LevelNames*: array [TLevel, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
]
defaultFmtStr* = "" ## default string between log level and message per logger
verboseFmtStr* = "$date $time "
type
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold
## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
## console
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
f: TFile
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
## messages to a file and
## performs log rotation
maxLines: int # maximum number of lines
curLine : int
baseName: string # initial filename
baseMode: TFileMode # initial file mode
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
proc substituteLog(frmt: string): string =
## converts $date to the current date
## converts $time to the current time
## converts $app to getAppFilename()
## converts
result = newStringOfCap(frmt.len + 20)
var i = 0
while i < frmt.len:
if frmt[i] != '$':
result.add(frmt[i])
inc(i)
else:
inc(i)
var v = ""
var app = getAppFilename()
while frmt[i] in IdentChars:
v.add(toLower(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
of "time": result.add(getClockStr())
of "app": result.add(app)
of "appdir": result.add(app.splitFile.dir)
of "appname": result.add(app.splitFile.name)
method log*(logger: PLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
## Override this method in custom loggers. Default implementation does
## nothing.
discard
method log*(logger: PConsoleLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to the console using ``logger`` only.
if level >= logger.levelThreshold:
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
frmt % args)
method log*(logger: PFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using ``logger`` only.
if level >= logger.levelThreshold:
writeln(logger.f, LevelNames[level], " ",
substituteLog(logger.fmtStr), frmt % args)
proc defaultFilename*(): string =
## Returns the default filename for a logger.
var (path, name, ext) = splitFile(getAppFilename())
result = changeFileExt(path / name, "log")
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
## Creates a new console logger. This logger logs to the console.
new result
result.fmtStr = fmtStr
result.levelThreshold = levelThreshold
proc newFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmAppend,
levelThreshold = lvlAll,
fmtStr = defaultFmtStr): PFileLogger =
## Creates a new file logger. This logger logs to a file.
new(result)
result.levelThreshold = levelThreshold
result.f = open(filename, mode)
result.fmtStr = fmtStr
# ------
proc countLogLines(logger: PRollingFileLogger): int =
result = 0
for line in logger.f.lines():
result.inc()
proc countFiles(filename: string): int =
# Example: file.log.1
result = 0
let (dir, name, ext) = splitFile(filename)
for kind, path in walkDir(dir):
if kind == pcFile:
let llfn = name & ext & ExtSep
if path.extractFilename.startsWith(llfn):
let numS = path.extractFilename[llfn.len .. -1]
try:
let num = parseInt(numS)
if num > result:
result = num
except EInvalidValue: discard
proc newRollingFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmReadWrite,
levelThreshold = lvlAll,
fmtStr = defaultFmtStr,
maxLines = 1000): PRollingFileLogger =
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
## a new log file will be started and the old will be renamed.
new(result)
result.levelThreshold = levelThreshold
result.fmtStr = defaultFmtStr
result.maxLines = maxLines
result.f = open(filename, mode)
result.curLine = 0
result.baseName = filename
result.baseMode = mode
result.logFiles = countFiles(filename)
if mode == fmAppend:
# We need to get a line count because we will be appending to the file.
result.curLine = countLogLines(result)
proc rotate(logger: PRollingFileLogger) =
let (dir, name, ext) = splitFile(logger.baseName)
for i in countdown(logger.logFiles, 0):
let srcSuff = if i != 0: ExtSep & $i else: ""
moveFile(dir / (name & ext & srcSuff),
dir / (name & ext & ExtSep & $(i+1)))
method log*(logger: PRollingFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using rolling ``logger`` only.
if level >= logger.levelThreshold:
if logger.curLine >= logger.maxLines:
logger.f.close()
rotate(logger)
logger.logFiles.inc
logger.curLine = 0
logger.f = open(logger.baseName, logger.baseMode)
writeln(logger.f, LevelNames[level], " ", frmt % args)
logger.curLine.inc
# --------
var
level* = lvlAll ## global log filter
handlers*: seq[PLogger] = @[] ## handlers with their own log levels
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
for logger in items(handlers):
if level >= logger.levelThreshold:
log(logger, level, frmt, args)
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
## Logs a message to all registered handlers at the given level.
bind logLoop
bind `%`
bind logging.Level
if level >= logging.Level:
logLoop(level, frmt, args)
template debug*(frmt: string, args: varargs[string, `$`]) =
## Logs a debug message to all registered handlers.
log(lvlDebug, frmt, args)
template info*(frmt: string, args: varargs[string, `$`]) =
## Logs an info message to all registered handlers.
log(lvlInfo, frmt, args)
template warn*(frmt: string, args: varargs[string, `$`]) =
## Logs a warning message to all registered handlers.
log(lvlWarn, frmt, args)
template error*(frmt: string, args: varargs[string, `$`]) =
## Logs an error message to all registered handlers.
log(lvlError, frmt, args)
template fatal*(frmt: string, args: varargs[string, `$`]) =
## Logs a fatal error message to all registered handlers.
log(lvlFatal, frmt, args)
# --------------
when isMainModule:
var L = newConsoleLogger()
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
handlers.add(L)
handlers.add(fL)
handlers.add(rL)
for i in 0 .. 25:
info("hello" & $i, [])

View File

@@ -54,7 +54,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
try:
discard parseutils.parseInt(s, x, 0)
except EOverflow:
nil
discard
if x in validRange: value = x
when isMainModule:

40
lib/pure/net.nim Normal file
View File

@@ -0,0 +1,40 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a high-level cross-platform sockets interface.
import sockets2, os
type
TSocket* = TSocketHandle
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.
if address == "":
var name: TSockaddr_in
when defined(windows):
name.sin_family = toInt(AF_INET).int16
else:
name.sin_family = toInt(AF_INET)
name.sin_port = htons(int16(port))
name.sin_addr.s_addr = htonl(INADDR_ANY)
if bindAddr(socket, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32:
osError(osLastError())
else:
var aiList = getAddrInfo(address, port, AF_INET)
if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList)
osError(osLastError())
dealloc(aiList)

View File

@@ -28,7 +28,7 @@ proc hexbyte*(hex: char): int =
of '0'..'9': result = (ord(hex) - ord('0'))
of 'a'..'f': result = (ord(hex) - ord('a') + 10)
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
else: nil
else: discard
proc parseOid*(str: cstring): TOid =
## parses an OID.

View File

@@ -287,12 +287,18 @@ proc osLastError*(): TOSErrorCode =
result = TOSErrorCode(errno)
{.pop.}
proc unixToNativePath*(path: string): string {.
proc unixToNativePath*(path: string, drive=""): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one.
##
## On an UNIX system this does nothing. Else it converts
## '/', '.', '..' to the appropriate things.
##
## On systems with a concept of "drives", `drive` is used to determine
## which drive label to use during absolute path conversion.
## `drive` defaults to the drive of the current working directory, and is
## ignored on systems that do not have a concept of "drives".
when defined(unix):
result = path
else:
@@ -300,7 +306,10 @@ proc unixToNativePath*(path: string): string {.
if path[0] == '/':
# an absolute path
when doslike:
result = r"C:\"
if drive != "":
result = drive & ":" & DirSep
else:
result = $DirSep
elif defined(macos):
result = "" # must not start with ':'
else:
@@ -387,6 +396,21 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
var res: TStat
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
## Returns true iff the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file.
when defined(windows):
when useWinUnicode:
wrapUnary(a, getFileAttributesW, link)
else:
var a = getFileAttributesA(link)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
else:
var res: TStat
return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
proc fileExists*(filename: string): bool {.inline.} =
## Synonym for existsFile
existsFile(filename)
@@ -1013,7 +1037,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc`
## module.
result = c_system(command)
result = c_system(command) shr 8
# Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``.
@@ -1054,9 +1078,9 @@ when defined(windows):
while true:
var eend = strEnd(e)
add(environment, $e)
e = cast[CString](cast[TAddress](eend)+1)
e = cast[cstring](cast[TAddress](eend)+1)
if eend[1] == '\0': break
discard FreeEnvironmentStringsA(env)
discard freeEnvironmentStringsA(env)
envComputed = true
else:
@@ -1221,6 +1245,8 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
k = succ(k)
yield (k, dir / extractFilename(getFilename(f)))
if findNextFile(h, f) == 0'i32: break
findClose(h)
@@ -1245,6 +1271,10 @@ 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.
## **Warning**:
## Modifying the directory structure while the iterator
## is traversing may result in undefined behavior!
##
## Walking is recursive. `filter` controls the behaviour of the iterator:
##
## --------------------- ---------------------------------------------
@@ -1336,6 +1366,46 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
copyDir(path, dest / noSource)
else: discard
proc createSymlink*(src, dest: string) =
## Create a symbolic link at `dest` which points to the item specified
## by `src`. On most operating systems, will fail if a lonk
##
## **Warning**:
## Some OS's (such as Microsoft Windows) restrict the creation
## of symlinks to root users (administrators).
when defined(Windows):
let flag = dirExists(src).int32
when useWinUnicode:
var wSrc = newWideCString(src)
var wDst = newWideCString(dest)
if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
osError(osLastError())
else:
if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
osError(osLastError())
else:
if symlink(src, dest) != 0:
osError(osLastError())
proc createHardlink*(src, dest: string) =
## Create a hard link at `dest` which points to the item specified
## by `src`.
##
## **Warning**: Most OS's restrict the creation of hard links to
## root users (administrators) .
when defined(Windows):
when useWinUnicode:
var wSrc = newWideCString(src)
var wDst = newWideCString(dest)
if createHardLinkW(wDst, wSrc, nil) == 0:
osError(osLastError())
else:
if createHardLinkA(dest, src, nil) == 0:
osError(osLastError())
else:
if link(src, dest) != 0:
osError(osLastError())
proc parseCmdLine*(c: string): seq[string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a command line into several components;

View File

@@ -13,7 +13,7 @@
include "system/inclrtl"
import
strutils, os, strtabs, streams
strutils, os, strtabs, streams, sequtils
when defined(windows):
import winlean
@@ -36,11 +36,17 @@ type
TProcessOption* = enum ## options that can be passed `startProcess`
poEchoCmd, ## echo the command before execution
poUseShell, ## use the shell to execute the command; NOTE: This
## often creates a security hole!
poUsePath, ## Asks system to search for executable using PATH environment
## variable.
## On Windows, this is the default.
poEvalCommand, ## Pass `command` directly to the shell, without quoting.
## Use it only if `command` comes from trused source.
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams
template poUseShell*: TProcessOption {.deprecated.} = poUsePath
## Deprecated alias for poUsePath.
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to Windows API.
## Based on Python's subprocess.list2cmdline
@@ -94,12 +100,17 @@ proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
{.error:"quoteShell is not supported on your system".}
proc execProcess*(command: string,
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): TaintedString {.
poUsePath,
poEvalCommand}): TaintedString {.
rtl, extern: "nosp$1",
tags: [FExecIO, FReadIO].}
## A convenience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string.
## WARNING: this function uses poEvalCommand by default for backward compatibility.
## Make sure to pass options explicitly.
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
## Executes ``command`` and returns its error code. Standard input, output,
@@ -109,8 +120,8 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
proc startProcess*(command: string,
workingDir: string = "",
args: openArray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}):
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}):
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
@@ -127,16 +138,10 @@ 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, 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`.
var c = parseCmdLine(command)
var a: seq[string]
newSeq(a, c.len-1) # avoid slicing for now (still unstable)
for i in 1 .. c.len-1: a[i-1] = c[i]
result = startProcess(command=c[0], args=a, options=options)
poStdErrToStdOut, poUsePath}): PProcess {.
tags: [FExecIO, FReadEnv], deprecated.} =
## Deprecated - use `startProcess` directly.
result = startProcess(command=command, options=options + {poEvalCommand})
proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
## When the process has finished executing, cleanup related handles
@@ -157,7 +162,7 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
## returns `p`'s process ID.
return p.id
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
extern: "nosp$1", tags: [].}
## waits for the process to finish and returns `p`'s error code.
@@ -167,19 +172,19 @@ proc peekExitCode*(p: PProcess): int {.tags: [].}
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s input stream for writing to.
##
## **Warning**: The returned `PStream` should not be closed manually as it
## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``.
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s output stream for reading from.
##
## **Warning**: The returned `PStream` should not be closed manually as it
## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``.
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s error stream for reading from.
##
## **Warning**: The returned `PStream` should not be closed manually as it
## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``.
proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
@@ -245,15 +250,15 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int {.rtl, extern: "nosp$1",
tags: [FExecIO, FTime, FReadEnv].} =
n = countProcessors()): int {.rtl, extern: "nosp$1",
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.
when defined(posix):
# poParentStreams causes problems on Posix, so we simply disable it:
var options = options - {poParentStreams}
assert n > 0
if n > 1:
var q: seq[PProcess]
@@ -307,13 +312,17 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int
when not defined(useNimRtl):
proc execProcess(command: string,
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): TaintedString =
var p = startCmd(command, options=options)
poUsePath,
poEvalCommand}): TaintedString =
var p = startProcess(command, args=args, env=env, options=options)
var outp = outputStream(p)
result = TaintedString""
var line = newStringOfCap(120).TaintedString
while true:
# FIXME: converts CR-LF to LF.
if outp.readLine(line):
result.string.add(line.string)
result.string.add("\n")
@@ -427,8 +436,9 @@ when defined(Windows) and not defined(useNimRtl):
result.errHandle = TFileHandle(si.hStdError)
var cmdl: cstring
when false: # poUseShell in options:
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
if poEvalCommand in options:
cmdl = command
assert args.len == 0
else:
cmdl = buildCommandLine(command, args)
var wd: cstring = nil
@@ -441,11 +451,11 @@ when defined(Windows) and not defined(useNimRtl):
var ee = newWideCString(e)
var wwd = newWideCString(wd)
success = winlean.createProcessW(nil,
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
ee, wwd, si, procInfo)
else:
success = winlean.createProcessA(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
let lastError = osLastError()
if poParentStreams notin options:
@@ -455,7 +465,6 @@ when defined(Windows) and not defined(useNimRtl):
fileClose(si.hStdError)
if e != nil: dealloc(e)
dealloc(cmdl)
if success == 0: osError(lastError)
# Close the handle now so anyone waiting is woken:
discard closeHandle(procInfo.hThread)
@@ -495,7 +504,7 @@ when defined(Windows) and not defined(useNimRtl):
proc peekExitCode(p: PProcess): int =
var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT
if b: result = -1
else:
else:
var res: int32
discard getExitCodeProcess(p.fProcessHandle, res)
return res
@@ -525,7 +534,7 @@ when defined(Windows) and not defined(useNimRtl):
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
else:
var res = winlean.createProcessA(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
if res == 0:
osError(osLastError())
else:
@@ -538,13 +547,13 @@ when defined(Windows) and not defined(useNimRtl):
result = -1
discard closeHandle(process)
proc select(readfds: var seq[PProcess], timeout = 500): int =
proc select(readfds: var seq[PProcess], timeout = 500): int =
assert readfds.len <= MAXIMUM_WAIT_OBJECTS
var rfds: TWOHandleArray
for i in 0..readfds.len()-1:
rfds[i] = readfds[i].fProcessHandle
var ret = waitForMultipleObjects(readfds.len.int32,
var ret = waitForMultipleObjects(readfds.len.int32,
addr(rfds), 0'i32, timeout.int32)
case ret
of WAIT_TIMEOUT:
@@ -561,22 +570,7 @@ elif not defined(useNimRtl):
readIdx = 0
writeIdx = 1
proc addCmdArgs(command: string, args: openArray[string]): string =
result = quoteShell(command)
for i in 0 .. high(args):
add(result, " ")
add(result, quoteShell(args[i]))
proc toCStringArray(b, a: openArray[string]): cstringArray =
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
for i in 0..high(b):
result[i] = cast[cstring](alloc(b[i].len+1))
copyMem(result[i], cstring(b[i]), b[i].len+1)
for i in 0..high(a):
result[i+b.len] = cast[cstring](alloc(a[i].len+1))
copyMem(result[i+b.len], cstring(a[i]), a[i].len+1)
proc toCStringArray(t: PStringTable): cstringArray =
proc envToCStringArray(t: PStringTable): cstringArray =
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
var i = 0
for key, val in pairs(t):
@@ -595,7 +589,7 @@ elif not defined(useNimRtl):
result[i] = cast[cstring](alloc(x.len+1))
copyMem(result[i], addr(x[0]), x.len+1)
inc(i)
proc startProcess(command: string,
workingDir: string = "",
args: openArray[string] = [],
@@ -609,23 +603,38 @@ elif not defined(useNimRtl):
if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
pipe(pStderr) != 0'i32:
osError(osLastError())
var sys_command: string
var sys_args_raw: seq[string]
if poEvalCommand in options:
sys_command = "/bin/sh"
sys_args_raw = @[sys_command, "-c", command]
assert args.len == 0
else:
sys_command = command
sys_args_raw = @[command]
for arg in args.items:
sys_args_raw.add arg
var sys_args = allocCStringArray(sys_args_raw)
finally: deallocCStringArray(sys_args)
var pid: TPid
when defined(posix_spawn) and not defined(useFork):
var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
template chck(e: expr) =
if e != 0'i32: osError(osLastError())
chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr)
var mask: Tsigset
chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask)
chck posix_spawnattr_setpgroup(attr, 0'i32)
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK or
POSIX_SPAWN_SETPGROUP)
@@ -639,27 +648,22 @@ elif not defined(useNimRtl):
if poStdErrToStdOut in options:
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
else:
chck posix_spawn_file_actions_adddup2(fops, pStderr[writeIdx], 2)
var e = if env == nil: envToCStringArray() else: toCStringArray(env)
var a: cstringArray
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
var res: cint
# This is incorrect!
if workingDir.len > 0: os.setCurrentDir(workingDir)
if poUseShell notin options:
a = toCStringArray([extractFilename(command)], args)
res = posix_spawn(pid, command, fops, attr, a, e)
if poUsePath in options:
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
else:
var x = addCmdArgs(command, args)
a = toCStringArray(["sh", "-c"], [x])
res = posix_spawn(pid, "/bin/sh", fops, attr, a, e)
deallocCStringArray(a)
deallocCStringArray(e)
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
deallocCStringArray(sys_env)
discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr)
chck res
else:
pid = fork()
if pid < 0: osError(osLastError())
if pid == 0:
@@ -680,19 +684,18 @@ elif not defined(useNimRtl):
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
if workingDir.len > 0: os.setCurrentDir(workingDir)
if poUseShell notin options:
var a = toCStringArray([extractFilename(command)], args)
if env == nil:
discard execv(command, a)
if env == nil:
if poUsePath in options:
discard execvp(sys_command, sys_args)
else:
discard execve(command, a, toCStringArray(env))
discard execv(sys_command, sys_args)
else:
var x = addCmdArgs(command, args)
var a = toCStringArray(["sh", "-c"], [x])
if env == nil:
discard execv("/bin/sh", a)
var c_env = envToCStringArray(env)
if poUsePath in options:
discard execvpe(sys_command, sys_args, c_env)
else:
discard execve("/bin/sh", a, toCStringArray(env))
discard execve(sys_command, sys_args, c_env)
# too risky to raise an exception here:
quit("execve call failed: " & $strerror(errno))
# Parent process. Copy process information.
@@ -788,15 +791,15 @@ elif not defined(useNimRtl):
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc execCmd(command: string): int =
result = csystem(command)
result = csystem(command) shr 8
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd)
for i in items(s):
for i in items(s):
m = max(m, int(i.outHandle))
FD_SET(cint(i.outHandle), fd)
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
var i = 0
var L = s.len
while i < L:
@@ -807,26 +810,26 @@ elif not defined(useNimRtl):
inc(i)
setLen(s, L)
proc select(readfds: var seq[PProcess], timeout = 500): int =
var tv: Ttimeval
proc select(readfds: var seq[PProcess], timeout = 500): int =
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
var rd: TFdSet
var m = 0
createFdSet((rd), readfds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
pruneProcessSet(readfds, (rd))
proc execCmdEx*(command: string, options: set[TProcessOption] = {
poStdErrToStdOut, poUseShell}): tuple[
output: TaintedString,
poStdErrToStdOut, poUsePath}): tuple[
output: TaintedString,
exitCode: int] {.tags: [FExecIO, FReadIO].} =
## a convenience proc that runs the `command`, grabs all its output and
## exit code and returns both.

View File

@@ -149,7 +149,7 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
of '\c': my.bufpos = handleCR(my, my.bufpos)
of '\l': my.bufpos = handleLF(my, my.bufpos)
else: break
of '\0': nil
of '\0': discard
else: error(my, my.bufpos, my.sep & " expected")
break

View File

@@ -79,7 +79,7 @@ proc handleHexChar(c: var TSqlLexer, xi: var int) =
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos)
else:
nil
discard
proc handleOctChar(c: var TSqlLexer, xi: var int) =
if c.buf[c.bufpos] in {'0'..'7'}:
@@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) =
of '+':
if not trailingPlusMinus and buf[pos+1] notin operators and
tok.literal.len > 0: break
of '*', '<', '>', '=': nil
of '*', '<', '>', '=': discard
else: break
add(tok.literal, buf[pos])
inc(pos)
@@ -1120,7 +1120,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
proc ra(n: PSqlNode, s: var string, indent: int) =
if n == nil: return
case n.kind
of nkNone: nil
of nkNone: discard
of nkIdent:
if allCharsInSet(n.strVal, {'\33'..'\127'}):
s.add(n.strVal)

249
lib/pure/selectors.nim Normal file
View File

@@ -0,0 +1,249 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# TODO: Docs.
import tables, os, unsigned
when defined(windows):
import winlean
else:
import posix
type
TEvent* = enum
EvRead, EvWrite
TSelectorKey* = object
fd: cint
events: set[TEvent]
data: PObject
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
PSelector* = ref object of PObject ## Selector interface.
fds*: TTable[cint, TSelectorKey]
registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
closeImpl*: proc (s: PSelector) {.nimcall.}
template initSelector(r: expr) =
new r
r.fds = initTable[cint, TSelectorKey]()
proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
TSelectorKey =
if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
proc unregister*(s: PSelector, fd: cint): TSelectorKey =
##
## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
## will only have the ``fd`` field set. This is an optimisation and may
## change in the future if a viable use case is presented.
if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
##
## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``.
if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
proc close*(s: PSelector) =
if not s.closeImpl.isNil: s.closeImpl(s)
# ---- Select() ----------------------------------------------------------------
type
PSelectSelector* = ref object of PSelector ## Implementation of select()
proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "FD already exists in selector.")
var sk = TSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
result = s.fds[fd]
s.fds.del(fd)
proc ssClose(s: PSelector) = nil
proc timeValFromMilliseconds(timeout: int): TTimeVal =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
m: var int) =
FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds):
if EvRead in v.events:
m = max(m, int(k))
FD_SET(k, rd)
if EvWrite in v.events:
m = max(m, int(k))
FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
seq[TReadyInfo] =
result = @[]
for k, v in pairs(fds):
var events: set[TEvent] = {}
if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite}
result.add((v, events))
proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
seq[TReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var m = 0
createFdSet(rd, wr, fds, m)
var retCode = 0
if timeout != -1:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0:
OSError(OSLastError())
elif retCode == 0:
return @[]
else:
return getReadyFDs(rd, wr, fds)
proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
result = select(s.fds, timeout)
proc newSelectSelector*(): PSelectSelector =
initSelector(result)
result.registerImpl = ssRegister
result.unregisterImpl = ssUnregister
result.selectImpl = ssSelect
result.closeImpl = ssClose
# ---- Epoll -------------------------------------------------------------------
when defined(linux):
import epoll
type
PEpollSelector* = ref object of PSelector
epollFD: cint
events: array[64, ptr epoll_event]
TDataWrapper = object
fd: cint
boundEvents: set[TEvent] ## The events which ``fd`` listens for.
data: PObject ## User object.
proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey =
var es = PEpollSelector(s)
var event: epoll_event
if EvRead in events:
event.events = EPOLLIN
if EvWrite in events:
event.events = event.events or EPOLLOUT
var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
dw.fd = fd
dw.boundEvents = events
dw.data = data
event.data.thePtr = dw
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
result = TSelectorKey(fd: fd, events: events, data: data)
proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
# We cannot find out the information about this ``fd`` from the epoll
# context. As such I will simply return an almost empty TSelectorKey.
var es = PEpollSelector(s)
if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError())
# We could fill in the ``fds`` TTable to get the info, but that wouldn't
# be nice for our memory.
result = TSelectorKey(fd: fd, events: {}, data: nil)
proc esClose(s: PSelector) =
var es = PEpollSelector(s)
if es.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr es.events) # TODO: Test this
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
result = @[]
var es = PEpollSelector(s)
let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[]
for i in 0 .. <evNum:
var evSet: set[TEvent] = {}
if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents,
data: dw.data)
result.add((selectorKey, evSet))
proc newEpollSelector*(): PEpollSelector =
new result
result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
if result.epollFD < 0:
OSError(OSLastError())
result.registerImpl = esRegister
result.unregisterImpl = esUnregister
result.closeImpl = esClose
result.selectImpl = esSelect
when isMainModule:
# Select()
import sockets
type
PSockWrapper = ref object of PObject
sock: TSocket
var sock = socket()
sock.connect("irc.freenode.net", TPort(6667))
var selector = newEpollSelector()
var data = PSockWrapper(sock: sock)
let key = selector.register(sock.getFD.cint, {EvRead}, data)
var i = 0
while true:
let ready = selector.select(1000)
echo ready.len
if ready.len > 0: echo ready[0].events
i.inc
if i == 6:
selector.close()
break

View File

@@ -66,13 +66,15 @@ proc checkReply(smtp: var TSMTP, reply: string) =
if not line.string.startswith(reply):
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
const compiledWithSsl = defined(ssl)
proc connect*(address: string, port = 25,
ssl = false, debug = false): TSMTP =
## Establishes a connection with a SMTP server.
## May fail with EInvalidReply or with a socket error.
result.sock = socket()
if ssl:
when defined(ssl):
when compiledWithSsl:
let ctx = newContext(verifyMode = CVerifyNone)
ctx.wrapSocket(result.sock)
else:

View File

@@ -311,7 +311,8 @@ when defined(ssl):
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
if newCTX == nil:
SSLError()
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
newCTX.loadCertificates(certFile, keyFile)
return PSSLContext(newCTX)
@@ -1291,14 +1292,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(osLastError())
if n < 0: socket.socketError()
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(osLastError())
elif n <= 0: socket.socketError()
addNLIfEmpty()
return
elif c == '\L':

202
lib/pure/sockets2.nim Normal file
View File

@@ -0,0 +1,202 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a low-level cross-platform sockets interface. Look
## at the ``net`` module for the higher-level version.
import unsigned, os
when hostos == "solaris":
{.passl: "-lsocket -lnsl".}
when defined(Windows):
import winlean
else:
import posix
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
inet_ntoa
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service
name*: string
aliases*: seq[string]
port*: TPort
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
length*: int
addrList*: seq[string]
when defined(windows):
let
OSInvalidSocket* = winlean.INVALID_SOCKET
else:
let
OSInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
## returns the port number as a string
proc toInt*(domain: TDomain): cint
## Converts the TDomain enum to a platform-dependent ``cint``.
proc toInt*(typ: TType): cint
## Converts the TType enum to a platform-dependent ``cint``.
proc toInt*(p: TProtocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``.
when defined(posix):
proc toInt(domain: TDomain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: nil
proc toInt(typ: TType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW
else: nil
proc toInt(p: TProtocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
of IPPROTO_IP: result = posix.IPPROTO_IP
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: nil
else:
proc toInt(domain: TDomain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
# TODO: The function which will use this will raise EOS.
socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) =
## closes a socket.
when defined(windows):
discard winlean.closeSocket(socket)
else:
discard posix.close(socket)
# TODO: These values should not be discarded. An EOS should be raised.
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
result = bindSocket(socket, name, namelen)
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
when defined(windows):
if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
else:
if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
##
##
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
var hints: TAddrInfo
result = nil
hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ)
hints.ai_protocol = toInt(prot)
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
if gaiResult != 0'i32:
when defined(windows):
OSError(OSLastError())
else:
raise newException(EOS, $gai_strerror(gaiResult))
proc dealloc*(ai: ptr TAddrInfo) =
freeaddrinfo(ai)
proc ntohl*(x: int32): int32 =
## Converts 32-bit integers from network to host byte order.
## On machines where the host byte order is the same as network byte order,
## this is a no-op; otherwise, it performs a 4-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 24'i32) or
(x shr 8'i32 and 0xff00'i32) or
(x shl 8'i32 and 0xff0000'i32) or
(x shl 24'i32)
proc ntohs*(x: int16): int16 =
## Converts 16-bit integers from network to host byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 2-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 8'i16) or (x shl 8'i16)
proc htonl*(x: int32): int32 =
## Converts 32-bit integers from host to network byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 4-byte swap operation.
result = sockets2.ntohl(x)
proc htons*(x: int16): int16 =
## Converts 16-bit positive integers from host to network byte order.
## On machines where the host byte order is the same as network byte
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = sockets2.ntohs(x)
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View File

@@ -45,6 +45,16 @@ const
NewLines* = {'\13', '\10'}
## the set of characters a newline terminator can start with
AllChars* = {'\x00'..'\xFF'}
## A set with all the possible characters. Not very useful by its own, you
## can use it to create *inverted* sets to make the ``find()`` proc find
## **invalid** characters in strings. Example:
##
## .. code-block:: nimrod
## let invalid = AllChars - Digits
## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2
proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case. This works only for the letters A-Z.

View File

@@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
result.months = months
result.years = years
proc isLeapYear(year: int): bool =
proc isLeapYear*(year: int): bool =
## returns true if ``year`` is a leap year
if year mod 400 == 0:
return true
elif year mod 100 == 0:
@@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
else:
return false
proc getDaysInMonth(month: TMonth, year: int): int =
proc getDaysInMonth*(month: TMonth, year: int): int =
## gets the amount of days in a ``month`` of a ``year``
# http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
case month
of mFeb: result = if isLeapYear(year): 29 else: 28

View File

@@ -96,7 +96,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
## &entity;
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
next(x)
of xmlEof: nil
of xmlEof: discard
proc parseXml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode =
@@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string,
of xmlElementOpen, xmlElementStart:
result = parse(x, errors)
break
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
of xmlError:
errors.add(errorMsg(x))
else:

View File

@@ -2333,29 +2333,29 @@ when not defined(JS): #and not defined(NimrodVM):
elif defined(JS):
# Stubs:
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
proc GC_disable() = nil
proc GC_enable() = nil
proc GC_fullCollect() = nil
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_enableMarkAndSweep() = nil
proc GC_disableMarkAndSweep() = nil
proc GC_disable() = discard
proc GC_enable() = discard
proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: TGC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc dealloc(p: pointer) = nil
proc alloc(size: int): pointer = nil
proc alloc0(size: int): pointer = nil
proc realloc(p: Pointer, newsize: int): pointer = nil
proc dealloc(p: pointer) = discard
proc alloc(size: int): pointer = discard
proc alloc0(size: int): pointer = discard
proc realloc(p: Pointer, newsize: int): pointer = discard
proc allocShared(size: int): pointer = nil
proc allocShared0(size: int): pointer = nil
proc deallocShared(p: pointer) = nil
proc reallocShared(p: pointer, newsize: int): pointer = nil
proc allocShared(size: int): pointer = discard
proc allocShared0(size: int): pointer = discard
proc deallocShared(p: pointer) = discard
proc reallocShared(p: pointer, newsize: int): pointer = discard
when defined(JS):
include "system/jssys"
@@ -2490,11 +2490,11 @@ proc staticRead*(filename: string): string {.magic: "Slurp".}
## ``slurp`` is an alias for ``staticRead``.
proc gorge*(command: string, input = ""): string {.
magic: "StaticExec".} = nil
magic: "StaticExec".} = discard
## This is an alias for ``staticExec``.
proc staticExec*(command: string, input = ""): string {.
magic: "StaticExec".} = nil
magic: "StaticExec".} = discard
## Executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
@@ -2561,7 +2561,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
## $pos.line, astToStr(code)]
## assert false, "A test expecting failure succeeded?"
## except exception:
## nil
## discard
##
## proc tester(pos: int): int =
## let

View File

@@ -23,7 +23,7 @@ else:
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
header: "<windows.h>", nodecl.}
proc writeToStdErr(msg: CString) =
proc writeToStdErr(msg: cstring) =
discard MessageBoxA(0, msg, nil, 0)
proc showErrorMessage(data: cstring) =

View File

@@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture.
# Addresses decrease as the stack grows.
while sp <= max:
gcMark(gch, sp[])
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer))
elif defined(ELATE):
{.error: "stack marking code is to be written for this architecture".}

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -59,11 +59,11 @@ var
gch {.rtlThreadVar.}: TGcHeap
when not defined(useNimRtl):
InstantiateForRegion(gch.region)
instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock)
acquireSys(HeapLock)
template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
@@ -90,7 +90,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
# used for code generation concerning debugging
result = usrToCell(c).typ
proc unsureAsgnRef(dest: ppointer, src: pointer) {.inline.} =
proc unsureAsgnRef(dest: PPointer, src: pointer) {.inline.} =
dest[] = src
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
@@ -114,10 +114,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
# forward declarations:
proc collectCT(gch: var TGcHeap)
proc IsOnStack*(p: pointer): bool {.noinline.}
proc isOnStack*(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
# we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) =
@@ -162,19 +162,19 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
var d = cast[TAddress](dest)
if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags:
case mt.Kind
case mt.kind
of tyRef, tyString, tySequence: # leaf:
doOperation(cast[ppointer](d)[], op)
doOperation(cast[PPointer](d)[], op)
of tyObject, tyTuple:
forAllSlotsAux(dest, mt.node, op)
of tyArray, tyArrayConstr, tyOpenArray:
for i in 0..(mt.size div mt.base.size)-1:
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: nil
else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) =
gcAssert(cell != nil, "forAllChildren: 1")
@@ -184,7 +184,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
if marker != nil:
marker(cellToUsr(cell), op.int)
else:
case cell.typ.Kind
case cell.typ.kind
of tyRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence:
@@ -194,7 +194,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
else: nil
else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
# generates a new object and sets its reference counter to 0
@@ -466,7 +466,7 @@ else:
sp = sp +% sizeof(pointer)*8
# last few entries:
while sp <=% max:
gcMark(gch, cast[ppointer](sp)[])
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
# ----------------------------------------------------------------------------
@@ -505,7 +505,7 @@ when not defined(useNimRtl):
else:
dec(gch.recGcLock)
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_setStrategy(strategy: TGC_Strategy) = discard
proc GC_enableMarkAndSweep() =
gch.cycleThreshold = InitialThreshold

View File

@@ -16,8 +16,12 @@ const
type
THandle* = int
LONG* = int32
ULONG* = int
PULONG* = ptr int
WINBOOL* = int32
DWORD* = int32
PDWORD* = ptr DWORD
LPINT* = ptr int32
HDC* = THandle
HGLRC* = THandle
@@ -195,16 +199,31 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode:
proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
flags: DWORD): int32 {.
importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall.}
proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
security: pointer=nil): int32 {.
importc:"CreateHardLinkW", dynlib: "kernel32", stdcall.}
else:
proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
flags: DWORD): int32 {.
importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall.}
proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
security: pointer=nil): int32 {.
importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.}
const
FILE_ATTRIBUTE_ARCHIVE* = 32'i32
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
@@ -212,6 +231,7 @@ const
FILE_ATTRIBUTE_DIRECTORY* = 16'i32
FILE_ATTRIBUTE_HIDDEN* = 2'i32
FILE_ATTRIBUTE_READONLY* = 1'i32
FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32
FILE_ATTRIBUTE_SYSTEM* = 4'i32
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
@@ -284,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".}
@@ -616,3 +636,76 @@ when not useWinUnicode:
proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".}
type
TOVERLAPPED* {.final, pure.} = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
POVERLAPPED* = ptr TOVERLAPPED
POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
para3: POVERLAPPED){.stdcall.}
TGUID* {.final, pure.} = object
D1*: int32
D2*: int16
D3*: int16
D4*: array [0..7, int8]
const
ERROR_IO_PENDING* = 997
proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE,
CompletionKey: DWORD,
NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc GetQueuedCompletionStatus*(CompletionPort: THandle,
lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
lpOverlapped: ptr POverlapped,
dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
const
IOC_OUT* = 0x40000000
IOC_IN* = 0x80000000
IOC_WS2* = 0x08000000
IOC_INOUT* = IOC_IN or IOC_OUT
template WSAIORW*(x,y): expr = (IOC_INOUT or x or y)
const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
var
WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED,
lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
{.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
type
TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
len*: ULONG
buf*: cstring
proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesReceived, flags: PDWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}

View File

@@ -109,11 +109,12 @@ type
cur*: cstring
dataSize*: cint
finished*: TBsonBool
stack*: array[0..32 - 1, cint]
ownsData*: TBsonBool
err*: cint
stackSize*: cint
stackPos*: cint
err*: cint ## Bitfield representing errors or warnings on this buffer
errstr*: cstring ## A string representation of the most recent error
## or warning.
stackPtr*: ptr csize
stack*: array[0..32 - 1, csize]
TDate* = int64
@@ -141,6 +142,7 @@ proc print*(TBson: cstring, depth: cint) {.stdcall,
importc: "bson_print_raw", dynlib: bsondll.}
## Print a string representation of a BSON object up to `depth`.
proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data",
dynlib: bsondll.}
## Return a pointer to the raw buffer stored by this bson object.
@@ -590,19 +592,30 @@ type
hosts*: ptr THostPort ## List of host/ports given by the replica set
name*: cstring ## Name of the replica set.
primary_connected*: TBsonBool ## Primary node connection status.
TWriteConcern*{.pure, final.} = object ## mongo_write_concern
w*: cint
wtimeout*: cint
j*: cint
fsync*: cint
mode*: cstring
cmd*: TBSon
TMongo*{.pure, final.} = object ## mongo
primary*: ptr THostPort ## Primary connection info.
replset*: ptr TReplSet ## replset object if connected to a replica set.
sock*: cint ## Socket file descriptor.
flags*: cint ## Flags on this connection object.
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
connected*: TBsonBool ## Connection status.
err*: TError ## Most recent driver error code.
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
lasterrcode*: cint ## getlasterror code given by the server on error.
lasterrstr*: cstring ## getlasterror string generated by server.
primary*: ptr THostPort ## Primary connection info.
replset*: ptr TReplSet ## replset object if connected to a replica set.
sock*: cint ## Socket file descriptor.
flags*: cint ## Flags on this connection object.
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
max_bson_size*: cint ## Largest BSON object allowed on this connection.
connected*: TBsonBool ## Connection status.
write_concern*: TWriteConcern ## The default write concern.
err*: TError ## Most recent driver error code.
errcode*: cint ## Most recent errno or WSAGetLastError().
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
lasterrcode*: cint ## getlasterror code given by the server on error.
lasterrstr*: array[0..128 - 1, char] ## getlasterror string generated by server.
TCursor*{.pure, final.} = object ## cursor
reply*: ptr TReply ## reply is owned by cursor
@@ -654,7 +667,11 @@ proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.}
proc connect*(conn: var TMongo, host: cstring = defaultHost,
port: cint = defaultPort): cint {.stdcall,
importc: "mongo_connect", dynlib: mongodll.}
importc: "mongo_connect", dynlib: mongodll, deprecated.}
## Connect to a single MongoDB server.
proc client*(conn: var TMongo, host: cstring = defaultHost,
port: cint = defaultPort): cint {.stdcall,
importc: "mongo_client", dynlib: mongodll.}
## Connect to a single MongoDB server.
proc replsetInit*(conn: var TMongo, name: cstring){.stdcall,
@@ -714,7 +731,8 @@ proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy",
## You must always call this function when finished with the connection
## object.
proc insert*(conn: var TMongo, ns: cstring, data: var TBson): cint{.stdcall,
proc insert*(conn: var TMongo, ns: cstring, data: var TBson,
custom_write_concern: ptr TWriteConcern): cint{.stdcall,
importc: "mongo_insert", dynlib: mongodll, discardable.}
## Insert a BSON document into a MongoDB server. This function
## will fail if the supplied BSON struct is not UTF-8 or if

View File

@@ -268,14 +268,22 @@ proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSS
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
when not defined(windows):
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
dynlib: DLLSSLName, importc.}
proc CRYPTO_malloc_init*() =
when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True:
nil
proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
when true:
discard
else:
proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl,
dynlib: DLLSSLName, importc.}
@@ -288,7 +296,6 @@ else:
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int
proc SSLSetMode*(s: PSSL, mode: int): int
proc SSLCTXGetMode*(ctx: PSSL_CTX): int
proc SSLGetMode*(s: PSSL): int
@@ -417,15 +424,12 @@ else:
enc: cInt){.cdecl, dynlib: DLLUtilName, importc.}
# implementation
proc SSLCTXSetMode(ctx: PSSL_CTX, mode: int): int =
Result = SslCTXCtrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSLSetMode(s: PSSL, mode: int): int =
Result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
proc SSLCTXGetMode(ctx: PSSL_CTX): int =
Result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
proc SSLGetMode(s: PSSL): int =
Result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)
result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)