mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-31 04:29:02 +00:00
Merge branch 'devel' of git://github.com/Araq/Nimrod
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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) {.
|
||||
|
||||
@@ -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].} =
|
||||
|
||||
@@ -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
88
lib/posix/epoll.nim
Normal 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
@@ -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
|
||||
|
||||
@@ -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
485
lib/pure/asyncio2.nim
Normal 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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ------------------------------
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
267
lib/pure/logging.nim
Normal 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, [])
|
||||
|
||||
|
||||
@@ -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
40
lib/pure/net.nim
Normal 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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
249
lib/pure/selectors.nim
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
202
lib/pure/sockets2.nim
Normal 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())
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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".}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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".}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user