merged upstream master

This commit is contained in:
Zahary Karadjov 2013-01-27 23:41:45 +02:00
commit 81a3585872
127 changed files with 4440 additions and 1496 deletions

View file

@ -45,8 +45,16 @@ type
nnkConstDef, nnkTypeDef,
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkIncludeStmt, nnkBindStmt, nnkMixinStmt,
nnkDiscardStmt, nnkStmtList,
nnkImportStmt,
nnkImportExceptStmt,
nnkExportStmt,
nnkExportExceptStmt,
nnkFromStmt,
nnkIncludeStmt,
nnkBindStmt, nnkMixinStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Dominik Picheta, Andreas Rumpf
# (c) Copyright 2013 Dominik Picheta, Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -139,12 +139,15 @@ proc invokeNewSeq*(x: TAny, len: int) =
var z = newSeq(x.rawType, len)
genericShallowAssign(x.value, addr(z), x.rawType)
proc extendSeq*(x: TAny, elems = 1) =
## performs ``setLen(x, x.len+elems)``. `x` needs to represent a ``seq``.
proc extendSeq*(x: TAny) =
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
assert x.rawType.kind == tySequence
var y = cast[ptr PGenSeq](x.value)[]
var z = incrSeq(y, x.rawType.base.size * elems)
genericShallowAssign(x.value, addr(z), x.rawType)
var z = incrSeq(y, x.rawType.base.size)
# 'incrSeq' already freed the memory for us and copied over the RC!
# So we simply copy the raw pointer into 'x.value':
cast[ppointer](x.value)[] = z
#genericShallowAssign(x.value, addr(z), x.rawType)
proc setObjectRuntimeType*(x: TAny) =
## this needs to be called to set `x`'s runtime object type field.

View file

@ -17,7 +17,8 @@
## Currently only requesting URLs is implemented. The implementation depends
## on the libcurl library!
##
## **Deprecated since version 0.8.8:** Use the ``httpclient`` module instead.
## **Deprecated since version 0.8.8:** Use the
## `httpclient <httpclient.html>`_ module instead.
##
{.deprecated.}

View file

@ -35,6 +35,9 @@ import sockets, os
## that in the future this type's fields will not be exported therefore breaking
## your code.
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
##
## Asynchronous sockets
## ====================
##
@ -123,6 +126,7 @@ type
handleTask*: proc (s: PAsyncSocket) {.closure.}
lineBuffer: TaintedString ## Temporary storage for ``recvLine``
sendBuffer: string ## Temporary storage for ``send``
sslNeedAccept: bool
proto: TProtocol
deleg: PDelegate
@ -152,6 +156,7 @@ proc newAsyncSocket(): PAsyncSocket =
result.handleTask = (proc (s: PAsyncSocket) = nil)
result.lineBuffer = "".TaintedString
result.sendBuffer = ""
proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP,
@ -222,10 +227,22 @@ proc asyncSockHandleWrite(h: PObject) =
else:
PAsyncSocket(h).deleg.mode = fmReadWrite
else:
if PAsyncSocket(h).handleWrite != nil:
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
if PAsyncSocket(h).sendBuffer != "":
let sock = PAsyncSocket(h)
let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
assert bytesSent > 0
if bytesSent != sock.sendBuffer.len:
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
elif bytesSent == sock.sendBuffer.len:
sock.sendBuffer = ""
if PAsyncSocket(h).handleWrite != nil:
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
else:
PAsyncSocket(h).deleg.mode = fmRead
if PAsyncSocket(h).handleWrite != nil:
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
else:
PAsyncSocket(h).deleg.mode = fmRead
when defined(ssl):
proc asyncSockDoHandshake(h: PObject) =
@ -337,7 +354,8 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
# deleg.open is set in ``toDelegate``.
client.socket = c
client.lineBuffer = ""
client.lineBuffer = "".TaintedString
client.sendBuffer = ""
client.info = SockConnected
proc accept*(server: PAsyncSocket, client: var PAsyncSocket) =
@ -422,6 +440,9 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool =
## sockets properly. This function guarantees that ``line`` is a full line,
## if this function can only retrieve some data; it will save this data and
## add it to the result when a full line is retrieved.
##
## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL
## exception if an error occurs.
setLen(line.string, 0)
var dataReceived = "".TaintedString
var ret = s.socket.recvLineAsync(dataReceived)
@ -440,8 +461,29 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool =
of RecvDisconnected:
result = true
of RecvFail:
s.SocketError(async = true)
result = false
proc send*(sock: PAsyncSocket, data: string) =
## Sends ``data`` to socket ``sock``. This is basically a nicer implementation
## of ``sockets.sendAsync``.
##
## If ``data`` cannot be sent immediately it will be buffered and sent
## when ``sock`` becomes writeable (during the ``handleWrite`` event).
## It's possible that only a part of ``data`` will be sent immediately, while
## the rest of it will be buffered and sent later.
if sock.sendBuffer.len != 0:
sock.sendBuffer.add(data)
return
let bytesSent = sock.socket.sendAsync(data)
assert bytesSent >= 0
if bytesSent == 0:
sock.sendBuffer.add(data)
sock.deleg.mode = fmReadWrite
elif bytesSent != data.len:
sock.sendBuffer.add(data[bytesSent .. -1])
sock.deleg.mode = fmReadWrite
proc timeValFromMilliseconds(timeout = 500): TTimeVal =
if timeout != -1:
var seconds = timeout div 1000
@ -556,7 +598,9 @@ when isMainModule:
proc testRead(s: PAsyncSocket, no: int) =
echo("Reading! " & $no)
var data = s.getSocket.recv()
var data = ""
if not s.recvLine(data):
OSError()
if data == "":
echo("Closing connection. " & $no)
s.close()

View file

@ -190,6 +190,11 @@ proc `$`*(s: TIntSet): string =
## The `$` operator for int sets.
dollarImpl()
proc empty*(s: TIntSet): bool {.inline.} =
## returns true if `s` is empty. This is safe to call even before
## the set has been initialized with `initIntSet`.
result = s.counter == 0
when isMainModule:
var x = initIntSet()
x.incl(1)

View file

@ -9,8 +9,16 @@
## :Author: Alex Mitchell
##
## This module implements operations for the built-in `seq`:idx: type
## which were inspired by functional programming languages.
## This module implements operations for the built-in `seq`:idx: type which
## were inspired by functional programming languages. If you are looking for
## the typical `map` function which applies a function to every element in a
## sequence, it already exists in the `system <system.html>`_ module in both
## mutable and immutable styles.
##
## Also, for functional style programming you may want to pass `anonymous procs
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
## which is more convenient in certain situations.
##
## **Note**: This interface will change as soon as the compiler supports
## closures and proper coroutines.
@ -19,7 +27,17 @@ when not defined(nimhygiene):
{.pragma: dirty.}
proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
## Takes several sequences' items and returns them inside of one sequence.
## Takes several sequences' items and returns them inside a new sequence.
##
## Example:
##
## .. code-block:: nimrod
## let
## s1 = @[1, 2, 3]
## s2 = @[4, 5]
## s3 = @[6, 7]
## total = concat(s1, s2, s3)
## assert total == @[1, 2, 3, 4, 5, 6, 7]
var L = 0
for seqitm in items(seqs): inc(L, len(seqitm))
newSeq(result, L)
@ -30,14 +48,42 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
inc(i)
proc distnct*[T](seq1: seq[T]): seq[T] =
## Removes duplicates from a sequence and returns it.
## Returns a new sequence without duplicates.
##
## This proc is `misspelled` on purpose to avoid a clash with the keyword
## ``distinct`` used to `define a derived type incompatible with its base
## type <manual.html#distinct-type>`_. Example:
##
## .. code-block:: nimrod
## let
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
## dup2 = @["a", "a", "c", "d", "d"]
## unique1 = distnct(dup1)
## unique2 = distnct(dup2)
## assert unique1 == @[1, 3, 4, 2, 8]
## assert unique2 == @["a", "c", "d"]
result = @[]
for itm in items(seq1):
if not result.contains(itm): result.add(itm)
proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
## Combines two sequences. If one sequence is too short,
## the remaining items in the longer sequence are discarded.
## Returns a new sequence with a combination of the two input sequences.
##
## For convenience you can access the returned tuples through the named
## fields `a` and `b`. If one sequence is shorter, the remaining items in the
## longer sequence are discarded. Example:
##
## .. code-block:: nimrod
## let
## short = @[1, 2, 3]
## long = @[6, 5, 4, 3, 2, 1]
## words = @["one", "two", "three"]
## zip1 = zip(short, long)
## zip2 = zip(short, words)
## assert zip1 == @[(1, 6), (2, 5), (3, 4)]
## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
## assert zip1[2].b == 4
## assert zip2[2].b == "three"
var m = min(seq1.len, seq2.len)
newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
@ -45,21 +91,124 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## Iterates through a sequence and yields every item that fulfills the
## predicate.
##
## Example:
##
## .. code-block:: nimrod
## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n)
## # echoes 4, 8, 4 in separate lines
for i in countup(0, len(seq1) -1):
var item = seq1[i]
if pred(item): yield seq1[i]
proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
## Returns all items in a sequence that fulfilled the predicate.
## Returns a new sequence with all the items that fulfilled the predicate.
##
## Example:
##
## .. code-block:: nimrod
## let
## colors = @["red", "yellow", "black"]
## f1 = filter(colors, proc(x: string): bool = x.len < 6)
## f2 = filter(colors) do (x: string) -> bool : x.len > 5
## assert f1 == @["red", "black"]
## assert f2 == @["yellow"]
accumulateResult(filter(seq1, pred))
template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} =
## Finds a specific item in a sequence as long as the
## predicate returns true. The predicate needs to be an expression
## containing ``it``: ``filterIt("abcxyz", it == 'x')``.
block:
var result: type(seq1) = @[]
for it in items(seq1):
if pred: result.add(it)
result
## Returns a new sequence with all the items that fulfilled the predicate.
##
## Unlike the `proc` version, the predicate needs to be an expression using
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
## Example:
##
## .. code-block:: nimrod
## let
## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
## acceptable = filterIt(temperatures, it < 50 and it > -10)
## assert acceptable == @[-2.0, 24.5, 44.31]
var result {.gensym.}: type(seq1) = @[]
for it in items(seq1):
if pred: result.add(it)
result
template toSeq*(iter: expr): expr {.immediate.} =
## Transforms any iterator into a sequence.
##
## Example:
##
## .. code-block:: nimrod
## let
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
## if x mod 2 == 1:
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]
##
var result {.gensym.}: seq[type(iter)] = @[]
for x in iter: add(result, x)
result
when isMainModule:
import strutils
proc toStr(x: int): string {.procvar.} = $x
# concat test
let
s1 = @[1, 2, 3]
s2 = @[4, 5]
s3 = @[6, 7]
total = concat(s1, s2, s3)
assert total == @[1, 2, 3, 4, 5, 6, 7]
# duplicates test
let
dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
dup2 = @["a", "a", "c", "d", "d"]
unique1 = distnct(dup1)
unique2 = distnct(dup2)
assert unique1 == @[1, 3, 4, 2, 8]
assert unique2 == @["a", "c", "d"]
# zip test
let
short = @[1, 2, 3]
long = @[6, 5, 4, 3, 2, 1]
words = @["one", "two", "three"]
zip1 = zip(short, long)
zip2 = zip(short, words)
assert zip1 == @[(1, 6), (2, 5), (3, 4)]
assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
assert zip1[2].b == 4
assert zip2[2].b == "three"
# filter proc test
let
colors = @["red", "yellow", "black"]
f1 = filter(colors, proc(x: string): bool = x.len < 6)
f2 = filter(colors) do (x: string) -> bool : x.len > 5
assert f1 == @["red", "black"]
assert f2 == @["yellow"]
# filter iterator test
let numbers = @[1, 4, 5, 8, 9, 7, 4]
for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
echo($n)
# echoes 4, 8, 4 in separate lines
# filterIt test
let
temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
acceptable = filterIt(temperatures, it < 50 and it > -10)
assert acceptable == @[-2.0, 24.5, 44.31]
# toSeq test
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
if x mod 2 == 1:
result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]
echo "Finished doc tests"

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -308,6 +308,52 @@ proc `$`*[A, B](t: TOrderedTable[A, B]): string =
## The `$` operator for ordered hash tables.
dollarImpl()
proc sort*[A, B](t: var TOrderedTable[A, B],
cmp: proc (x,y: tuple[key: A, val: B]): int) =
## sorts `t` according to `cmp`. This modifies the internal list
## that kept the insertion order, so insertion order is lost after this
## call but key lookup and insertions remain possible after `sort` (in
## contrast to the `sort` for count tables).
var list = t.first
var
p, q, e, tail, oldhead: int
nmerges, psize, qsize, i: int
if t.counter == 0: return
var insize = 1
while true:
p = list; oldhead = list
list = -1; tail = -1; nmerges = 0
while p >= 0:
inc(nmerges)
q = p
psize = 0
i = 0
while i < insize:
inc(psize)
q = t.data[q].next
if q < 0: break
inc(i)
qsize = insize
while psize > 0 or (qsize > 0 and q >= 0):
if psize == 0:
e = q; q = t.data[q].next; dec(qsize)
elif qsize == 0 or q < 0:
e = p; p = t.data[p].next; dec(psize)
elif cmp((t.data[p].key, t.data[p].val),
(t.data[q].key, t.data[q].val)) <= 0:
e = p; p = t.data[p].next; dec(psize)
else:
e = q; q = t.data[q].next; dec(qsize)
if tail >= 0: t.data[tail].next = e
else: list = e
tail = e
p = q
t.data[tail].next = -1
if nmerges <= 1: break
insize = insize * 2
t.first = list
t.last = tail
# ------------------------------ count tables -------------------------------
type

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -18,6 +18,10 @@ proc LoadLib*(path: string): TLibHandle
## loads a library from `path`. Returns nil if the library could not
## be loaded.
proc LoadLib*(): TLibHandle
## gets the handle from the current executable. Returns nil if the
## library could not be loaded.
proc UnloadLib*(lib: TLibHandle)
## unloads the library `lib`
@ -57,6 +61,7 @@ when defined(posix):
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 symAddr(lib: TLibHandle, name: cstring): pointer =
return dlsym(lib, name)
@ -78,6 +83,8 @@ elif defined(windows) or defined(dos):
proc LoadLib(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
proc LoadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil))
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
proc symAddr(lib: TLibHandle, name: cstring): pointer =

View file

@ -254,6 +254,8 @@ when defined(windows):
else:
when defined(haiku):
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
elif defined(macosx):
const iconvDll = "libiconv.dylib"
else:
const iconvDll = "(libc.so.6|libiconv.so)"
@ -449,8 +451,13 @@ proc convert*(s: string, destEncoding = "UTF-8",
close(c)
when IsMainModule:
var orig = "öäüß"
var crap = convert(orig, "CP1252", "UTF-8")
echo convert(crap, "ibm850", "CP1252")
echo getCurrentEncoding()
let
orig = "öäüß"
cp1252 = convert(orig, "CP1252", "UTF-8")
ibm850 = convert(cp1252, "ibm850", "CP1252")
current = getCurrentEncoding()
echo "Original string from source code: ", orig
echo "Forced ibm850 encoding: ", ibm850
echo "Current encoding: ", current
echo "From ibm850 to current: ", convert(ibm850, current, "ibm850")

View file

@ -17,9 +17,7 @@
## module will therefore not work with any Linux kernel prior to that, unless
## it has been patched to support inotify.
when defined(windows):
{.error: "Windows is not yet supported by this module.".}
elif defined(linux):
when defined(linux) or defined(nimdoc):
from posix import read
else:
{.error: "Your platform is not supported.".}

View file

@ -26,6 +26,10 @@ import sockets, strutils, parseutils, times, os, asyncio
## var ftp = FTPClient("example.org", user = "user", pass = "pass")
## ftp.connect()
## ftp.retrFile("file.ext", "file.ext")
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
type
TFTPClient* = object of TObject
@ -92,8 +96,9 @@ type
EFTP* = object of ESynch
proc FTPClient*(address: string, port = TPort(21),
user, pass = ""): TFTPClient =
## Create a ``TFTPClient`` object.
user, pass = ""): PFTPClient =
## Create a ``PFTPClient`` object.
new(result)
result.user = user
result.pass = pass
result.address = address
@ -109,7 +114,7 @@ proc getDSock(ftp: PFTPClient): TSocket =
proc getCSock(ftp: PFTPClient): TSocket =
if ftp.isAsync: return ftp.asyncCSock else: return ftp.csock
template blockingOperation(sock: TSocket, body: stmt) =
template blockingOperation(sock: TSocket, body: stmt) {.immediate.} =
if ftp.isAsync:
sock.setBlocking(true)
body
@ -274,11 +279,20 @@ proc getLines(ftp: PFTPClient, async: bool = false): bool =
## It doesn't if `async` is true, because it doesn't check for 226 then.
if ftp.dsockConnected:
var r = TaintedString""
if getDSock(ftp).recvAsync(r):
if r.string != "":
ftp.job.lines.add(r.string)
else:
ftp.dsockConnected = False
if ftp.isAsync:
if ftp.asyncDSock.recvLine(r):
if r.string == "":
ftp.dsockConnected = false
else:
ftp.job.lines.add(r.string & "\n")
else:
assert(not async)
if ftp.dsock.recvLine(r):
if r.string == "":
ftp.dsockConnected = false
else:
ftp.job.lines.add(r.string & "\n")
else: OSError()
if not async:
var readSocks: seq[TSocket] = @[ftp.getCSock()]
@ -385,7 +399,7 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string =
proc retrText*(ftp: PFTPClient, file: string, async = false): string =
## Retrieves ``file``. File must be ASCII text.
## If ``async`` is true, this function will return immediately and
## it will be your job to call ``poll`` to progress this operation.
## it will be your job to call asyncio's ``poll`` to progress this operation.
ftp.createJob(getLines, JRetrText)
ftp.pasv()
assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
@ -400,12 +414,14 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string =
proc getFile(ftp: PFTPClient, async = false): bool =
if ftp.dsockConnected:
var r = "".TaintedString
var bytesRead = 0
var returned = false
if async:
if not ftp.isAsync: raise newException(EFTP, "FTPClient must be async.")
returned = ftp.AsyncDSock.recvAsync(r)
bytesRead = ftp.AsyncDSock.recvAsync(r, BufferSize)
returned = bytesRead != -1
else:
r = getDSock(ftp).recv()
bytesRead = getDSock(ftp).recv(r, BufferSize)
returned = true
let r2 = r.string
if r2 != "":
@ -425,8 +441,9 @@ proc getFile(ftp: PFTPClient, async = false): bool =
proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) =
## Downloads ``file`` and saves it to ``dest``. Usage of this function
## asynchronously is recommended to view the progress of the download.
## The ``EvRetr`` event is given by ``poll`` when the download is finished,
## and the ``filename`` field will be equal to ``file``.
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
## when the download is finished, and the ``filename`` field will be equal
## to ``file``.
ftp.createJob(getFile, JRetr)
ftp.job.file = open(dest, mode = fmWrite)
ftp.pasv()
@ -450,11 +467,13 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
if ftp.dsockConnected:
if ftp.job.toStore.len() > 0:
assert(async)
if ftp.asyncDSock.sendAsync(ftp.job.toStore):
let bytesSent = ftp.asyncDSock.sendAsync(ftp.job.toStore)
if bytesSent == ftp.job.toStore.len:
ftp.job.toStore = ""
ftp.job.progress.inc(ftp.job.toStore.len)
ftp.job.oneSecond.inc(ftp.job.toStore.len)
elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
ftp.job.toStore = ftp.job.toStore[bytesSent .. -1]
ftp.job.progress.inc(bytesSent)
ftp.job.oneSecond.inc(bytesSent)
else:
var s = newStringOfCap(4000)
var len = ftp.job.file.readBuffer(addr(s[0]), 4000)
@ -472,8 +491,12 @@ proc doUpload(ftp: PFTPClient, async = false): bool =
if not async:
getDSock(ftp).send(s)
else:
if not ftp.asyncDSock.sendAsync(s):
ftp.job.toStore = s
let bytesSent = ftp.asyncDSock.sendAsync(s)
if bytesSent == 0:
ftp.job.toStore.add(s)
elif bytesSent != s.len:
ftp.job.toStore.add(s[bytesSent .. -1])
len = bytesSent
ftp.job.progress.inc(len)
ftp.job.oneSecond.inc(len)
@ -482,8 +505,9 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) =
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
## function asynchronously is recommended to view the progress of
## the download.
## The ``EvStore`` event is given by ``poll`` when the upload is finished,
## and the ``filename`` field will be equal to ``file``.
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
## when the upload is finished, and the ``filename`` field will be
## equal to ``file``.
ftp.createJob(doUpload, JStore)
ftp.job.file = open(file)
ftp.job.total = ftp.job.file.getFileSize()
@ -508,16 +532,6 @@ proc close*(ftp: PFTPClient) =
ftp.csock.close()
ftp.dsock.close()
discard """proc getSocket(h: PObject): tuple[info: TInfo, sock: TSocket] =
result = (SockIdle, InvalidSocket)
var ftp = PAsyncFTPClient(h)
if ftp.jobInProgress:
case ftp.job.typ
of JRetrText, JRetr, JStore:
if ftp.dsockStatus == SockConnecting or ftp.dsockStatus == SockConnected:
result = (ftp.dsockStatus, ftp.dsock)
else: result = (SockIdle, ftp.dsock)"""
proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
if ftp.jobInProgress:
assertReply ftp.expectReply(), "226" # Make sure the transfer completed.
@ -540,32 +554,6 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
ftp.handleEvent(ftp, r)
discard """proc handleConnect(h: PObject) =
var ftp = PAsyncFTPClient(h)
ftp.dsockStatus = SockConnected
assert(ftp.jobInProgress)
if ftp.job.typ == JStore:
ftp.dele.mode = MWriteable
else:
ftp.dele.mode = MReadable"""
discard """proc handleRead(h: PObject) =
var ftp = PAsyncFTPClient(h)
assert(ftp.jobInProgress)
assert(ftp.job.typ != JStore)
# This can never return true, because it shouldn't check for code
# 226 from csock.
assert(not ftp.job.prc(ftp[], true))
"""
discard """proc csockGetSocket(h: PObject): tuple[info: TInfo, sock: TSocket] =
# This only returns the csock if a job is in progress. Otherwise handle read
# would capture data which is not for it to capture.
result = (SockIdle, InvalidSocket)
var ftp = PAsyncFTPClient(h)
if ftp.jobInProgress:
result = (SockConnected, ftp.csock)"""
proc AsyncFTPClient*(address: string, port = TPort(21),
user, pass = "",
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} =

View file

@ -123,7 +123,7 @@ type
tagVar ## the HTML ``var`` element
const
tagStrs = [
tagToStr* = [
"a", "abbr", "acronym", "address", "applet", "area",
"b", "base", "basefont", "bdo", "big", "blockquote", "body",
"br", "button", "caption", "center", "cite", "code",
@ -243,13 +243,13 @@ proc binaryStrSearch(x: openarray[string], y: string): int =
proc htmlTag*(n: PXmlNode): THtmlTag =
## gets `n`'s tag as a ``THtmlTag``.
if n.clientData == 0:
n.clientData = binaryStrSearch(tagStrs, n.tag)+1
n.clientData = binaryStrSearch(tagToStr, n.tag)+1
result = THtmlTag(n.clientData)
proc htmlTag*(s: string): THtmlTag =
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
## returned.
result = THtmlTag(binaryStrSearch(tagStrs, s.toLower)+1)
result = THtmlTag(binaryStrSearch(tagToStr, s.toLower)+1)
proc entityToUtf8*(entity: string): string =
## converts an HTML entity name like ``&Uuml;`` to its UTF-8 equivalent.

View file

@ -80,62 +80,43 @@ proc fileError(msg: string) =
e.msg = msg
raise e
proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} =
result = d[i]
while result == '\0':
d = string(s.recv())
i = 0
result = d[i]
proc parseChunks(s: TSocket): string =
# get chunks:
var i = 0
result = ""
var d = s.recv().string
var ri = 0
while true:
var chunkSizeStr = ""
var chunkSize = 0
var digitFound = false
while true:
case d[i]
of '0'..'9':
digitFound = true
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('0'))
of 'a'..'f':
digitFound = true
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('a') + 10)
of 'A'..'F':
digitFound = true
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('A') + 10)
of '\0':
d = string(s.recv())
i = -1
else: break
inc(i)
if not digitFound: httpError("Chunksize expected")
if s.recvLine(chunkSizeStr):
var i = 0
if chunkSizeStr == "":
httpError("Server terminated connection prematurely")
while true:
case chunkSizeStr[i]
of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
of 'a'..'f':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions.
break
else:
httpError("Invalid chunk size: " & chunkSizeStr)
inc(i)
if chunkSize <= 0: break
while charAt(d, i, s) notin {'\C', '\L', '\0'}: inc(i)
if charAt(d, i, s) == '\C': inc(i)
if charAt(d, i, s) == '\L': inc(i)
else: httpError("CR-LF after chunksize expected")
var x = substr(d, i, i+chunkSize-1)
var size = x.len
result.add(x)
inc(i, size)
if size < chunkSize:
# read in the rest:
var missing = chunkSize - size
var L = result.len
setLen(result, L + missing)
while missing > 0:
var bytesRead = s.recv(addr(result[L]), missing)
inc(L, bytesRead)
dec(missing, bytesRead)
# next chunk:
d = string(s.recv())
i = 0
# skip trailing CR-LF:
while charAt(d, i, s) in {'\C', '\L'}: inc(i)
result.setLen(ri+chunkSize)
var bytesRead = 0
while bytesRead != chunkSize:
let ret = recv(s, addr(result[ri]), chunkSize-bytesRead)
ri += ret
bytesRead += ret
s.skip(2) # Skip \c\L
# Trailer headers will only be sent if the request specifies that we want
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1
proc parseBody(s: TSocket,
headers: PStringTable): string =
@ -238,12 +219,18 @@ type
httpCONNECT ## Converts the request connection to a transparent
## TCP/IP tunnel, usually used for proxies.
when not defined(ssl):
type PSSLContext = ref object
let defaultSSLContext: PSSLContext = nil
else:
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
body = ""): TResponse =
body = "",
sslContext: PSSLContext = defaultSSLContext): TResponse =
## | Requests ``url`` with the specified ``httpMethod``.
## | Extra headers can be specified and must be seperated by ``\c\L``
var r = parseUrl(url)
var headers = substr($httpMethod, len("http"))
headers.add(" /" & r.path & r.query)
@ -257,7 +244,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
var port = TPort(80)
if r.scheme == "https":
when defined(ssl):
s.wrapSocket(verifyMode = CVerifyNone)
sslContext.wrapSocket(s)
else:
raise newException(EHttpRequestErr, "SSL support was not compiled in. Cannot connect over SSL.")
port = TPort(443)
@ -277,32 +264,32 @@ proc redirection(status: string): bool =
if status.startsWith(i):
return True
proc get*(url: string, maxRedirects = 5): TResponse =
## | GET's the ``url`` and returns a ``TResponse`` object
proc get*(url: string, maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
## | GETs the ``url`` and returns a ``TResponse`` object
## | This proc also handles redirection
result = request(url)
for i in 1..maxRedirects:
if result.status.redirection():
var locationHeader = result.headers["Location"]
if locationHeader == "": httpError("location header expected")
result = request(locationHeader)
result = request(locationHeader, sslContext = sslContext)
proc getContent*(url: string): string =
## | GET's the body and returns it as a string.
proc getContent*(url: string, sslContext: PSSLContext = defaultSSLContext): string =
## | GETs the body and returns it as a string.
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
var r = get(url)
var r = get(url, sslContext = sslContext)
if r.status[0] in {'4','5'}:
raise newException(EHTTPRequestErr, r.status)
else:
return r.body
proc post*(url: string, extraHeaders = "", body = "",
maxRedirects = 5): TResponse =
## | POST's ``body`` to the ``url`` and returns a ``TResponse`` object.
maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object.
## | This proc adds the necessary Content-Length header.
## | This proc also handles redirection.
var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
result = request(url, httpPOST, xh, body)
result = request(url, httpPOST, xh, body, sslContext)
for i in 1..maxRedirects:
if result.status.redirection():
var locationHeader = result.headers["Location"]
@ -310,8 +297,9 @@ proc post*(url: string, extraHeaders = "", body = "",
var meth = if result.status != "307": httpGet else: httpPost
result = request(locationHeader, meth, xh, body)
proc postContent*(url: string, extraHeaders = "", body = ""): string =
## | POST's ``body`` to ``url`` and returns the response's body as a string
proc postContent*(url: string, extraHeaders = "", body = "",
sslContext: PSSLContext = defaultSSLContext): string =
## | POSTs ``body`` to ``url`` and returns the response's body as a string
## | Raises exceptions for the status codes ``4xx`` and ``5xx``
var r = post(url, extraHeaders, body)
if r.status[0] in {'4','5'}:
@ -319,11 +307,12 @@ proc postContent*(url: string, extraHeaders = "", body = ""): string =
else:
return r.body
proc downloadFile*(url: string, outputFilename: string) =
proc downloadFile*(url: string, outputFilename: string,
sslContext: PSSLContext = defaultSSLContext) =
## Downloads ``url`` and saves it to ``outputFilename``
var f: TFile
if open(f, outputFilename, fmWrite):
f.write(getContent(url))
f.write(getContent(url, sslContext))
f.close()
else:
fileError("Unable to open file")

View file

@ -22,6 +22,8 @@
##
## run(handleRequest, TPort(80))
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio

View file

@ -16,17 +16,23 @@
## the amount of lag.
##
## .. code-block:: Nimrod
## var client = irc("irc.server.net", joinChans = @["#channel"])
##
## var client = irc("picheta.me", joinChans = @["#bots"])
## client.connect()
## while True:
## var event: TIRCEvent
## if client.poll(event):
## case event.typ
## of EvDisconnected: break
## of EvConnected: nil
## of EvDisconnected:
## client.reconnect()
## of EvMsg:
## # Where all the magic happens.
## # Write your message reading code here.
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import sockets, strutils, parseutils, times, asyncio
import sockets, strutils, parseutils, times, asyncio, os
type
TIRC* = object of TObject
@ -34,11 +40,15 @@ type
port: TPort
nick, user, realname, serverPass: string
case isAsync: bool
of false:
sock: TSocket
of true:
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
asyncSock: PAsyncSocket
myDispatcher: PDispatcher
of false:
dummyA: pointer
dummyB: pointer # workaround a Nimrod API issue
dummyC: pointer
sock: TSocket
status: TInfo
lastPing: float
lastPong: float
@ -46,6 +56,9 @@ type
channelsToJoin: seq[string]
msgLimit: bool
messageBuffer: seq[tuple[timeToSend: float, m: string]]
lastReconnect: float
PIRC* = ref TIRC
PAsyncIRC* = ref TAsyncIRC
TAsyncIRC* = object of TIRC
@ -68,9 +81,13 @@ type
MError
TIRCEventType* = enum
EvMsg, EvDisconnected
EvMsg, EvConnected, EvDisconnected
TIRCEvent* = object ## IRC Event
case typ*: TIRCEventType
of EvConnected:
## Connected to server.
## Only occurs with AsyncIRC.
nil
of EvDisconnected:
## Disconnected from the server
nil
@ -82,7 +99,7 @@ type
origin*: string ## The channel/user that this msg originated from
raw*: string ## Raw IRC message
proc send*(irc: var TIRC, message: string, sendImmediately = false) =
proc send*(irc: PIRC, message: string, sendImmediately = false) =
## Sends ``message`` as a raw command. It adds ``\c\L`` for you.
var sendMsg = true
if irc.msgLimit and not sendImmediately:
@ -104,15 +121,15 @@ proc send*(irc: var TIRC, message: string, sendImmediately = false) =
# but I can't exactly check for EBrokenPipe.
irc.status = SockClosed
proc privmsg*(irc: var TIRC, target, message: string) =
proc privmsg*(irc: PIRC, target, message: string) =
## Sends ``message`` to ``target``. ``Target`` can be a channel, or a user.
irc.send("PRIVMSG $1 :$2" % [target, message])
proc notice*(irc: var TIRC, target, message: string) =
proc notice*(irc: PIRC, target, message: string) =
## Sends ``notice`` to ``target``. ``Target`` can be a channel, or a user.
irc.send("NOTICE $1 :$2" % [target, message])
proc join*(irc: var TIRC, channel: string, key = "") =
proc join*(irc: PIRC, channel: string, key = "") =
## Joins ``channel``.
##
## If key is not ``""``, then channel is assumed to be key protected and this
@ -122,16 +139,19 @@ proc join*(irc: var TIRC, channel: string, key = "") =
else:
irc.send("JOIN " & channel & " " & key)
proc part*(irc: var TIRC, channel, message: string) =
proc part*(irc: PIRC, channel, message: string) =
## Leaves ``channel`` with ``message``.
irc.send("PART " & channel & " :" & message)
proc close*(irc: var TIRC) =
proc close*(irc: PIRC) =
## Closes connection to an IRC server.
##
## **Warning:** This procedure does not send a ``QUIT`` message to the server.
irc.status = SockClosed
irc.sock.close()
if irc.isAsync:
irc.asyncSock.close()
else:
irc.sock.close()
proc isNumber(s: string): bool =
## Checks if `s` contains only numbers.
@ -202,12 +222,11 @@ proc parseMessage(msg: string): TIRCEvent =
inc(i) # Skip `:`.
result.params.add(msg[i..msg.len-1])
proc connect*(irc: var TIRC) =
proc connect*(irc: PIRC) =
## Connects to an IRC server as specified by ``irc``.
assert(irc.address != "")
assert(irc.port != TPort(0))
irc.sock = socket()
irc.sock.connect(irc.address, irc.port)
irc.status = SockConnected
@ -217,13 +236,28 @@ proc connect*(irc: var TIRC) =
irc.send("NICK " & irc.nick, true)
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
proc reconnect*(irc: PIRC, timeout = 5000) =
## Reconnects to an IRC server.
##
## ``Timeout`` specifies the time to wait in miliseconds between multiple
## consecutive reconnections.
##
## This should be used when an ``EvDisconnected`` event occurs.
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
if secSinceReconnect < timeout:
sleep(timeout - secSinceReconnect)
irc.sock = socket()
irc.connect()
irc.lastReconnect = epochTime()
proc irc*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot",
user = "NimrodBot",
realname = "NimrodBot", serverPass = "",
joinChans: seq[string] = @[],
msgLimit: bool = true): TIRC =
msgLimit: bool = true): PIRC =
## Creates a ``TIRC`` object.
new(result)
result.address = address
result.port = port
result.nick = nick
@ -237,8 +271,9 @@ proc irc*(address: string, port: TPort = 6667.TPort,
result.msgLimit = msgLimit
result.messageBuffer = @[]
result.status = SockIdle
result.sock = socket()
proc processLine(irc: var TIRC, line: string): TIRCEvent =
proc processLine(irc: PIRC, line: string): TIRCEvent =
if line.len == 0:
irc.close()
result.typ = EvDisconnected
@ -271,7 +306,7 @@ proc processLine(irc: var TIRC, line: string): TIRCEvent =
if result.nick == irc.nick:
irc.nick = result.params[0]
proc processOther(irc: var TIRC, ev: var TIRCEvent): bool =
proc processOther(irc: PIRC, ev: var TIRCEvent): bool =
result = false
if epochTime() - irc.lastPing >= 20.0:
irc.lastPing = epochTime()
@ -290,7 +325,7 @@ proc processOther(irc: var TIRC, ev: var TIRCEvent): bool =
break # messageBuffer is guaranteed to be from the quickest to the
# later-est.
proc poll*(irc: var TIRC, ev: var TIRCEvent,
proc poll*(irc: PIRC, ev: var TIRCEvent,
timeout: int = 500): bool =
## This function parses a single message from the IRC server and returns
## a TIRCEvent.
@ -316,46 +351,32 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
if processOther(irc, ev): result = true
proc getLag*(irc: var TIRC): float =
proc getLag*(irc: PIRC): float =
## Returns the latency between this client and the IRC server in seconds.
##
## If latency is unknown, returns -1.0.
return irc.lag
proc isConnected*(irc: var TIRC): bool =
proc isConnected*(irc: PIRC): bool =
## Returns whether this IRC client is connected to an IRC server.
return irc.status == SockConnected
proc getNick*(irc: var TIRC): string =
proc getNick*(irc: PIRC): string =
## Returns the current nickname of the client.
return irc.nick
# -- Asyncio dispatcher
proc connect*(irc: PAsyncIRC) =
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
assert(irc.address != "")
assert(irc.port != TPort(0))
irc.asyncSock = AsyncSocket()
irc.asyncSock.connect(irc.address, irc.port)
proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) =
# Greet the server :)
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
irc[].send("NICK " & irc.nick, true)
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
discard """proc handleConnect(h: PObject) =
var irc = PAsyncIRC(h)
# Greet the server :)
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
irc[].send("NICK " & irc.nick, true)
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
if irc.serverPass != "": irc.send("PASS " & irc.serverPass, true)
irc.send("NICK " & irc.nick, true)
irc.send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
irc.status = SockConnected
"""
var ev: TIRCEvent
ev.typ = EvConnected
irc.handleEvent(irc, ev)
proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
var line = "".TaintedString
@ -363,42 +384,55 @@ proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
if ret:
if line == "":
var ev: TIRCEvent
irc[].close()
irc.close()
ev.typ = EvDisconnected
irc[].handleEvent(irc[], ev)
irc.handleEvent(irc, ev)
else:
var ev = irc[].processLine(line.string)
irc[].handleEvent(irc[], ev)
discard """proc handleRead(h: PObject) =
var irc = PAsyncIRC(h)
var line = "".TaintedString
var ret = irc.sock.recvLineAsync(line)
case ret
of RecvFullLine:
var ev = irc[].processLine(irc.lineBuffer.string & line.string)
irc.handleEvent(irc[], ev, irc.userArg)
irc.lineBuffer = "".TaintedString
of RecvPartialLine:
if line.string != "":
string(irc.lineBuffer).add(line.string)
of RecvDisconnected:
var ev: TIRCEvent
irc[].close()
ev.typ = EvDisconnected
irc.handleEvent(irc[], ev, irc.userArg)
of RecvFail: nil"""
var ev = irc.processLine(line.string)
irc.handleEvent(irc, ev)
proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
var ev: TIRCEvent
if irc[].processOther(ev):
irc.handleEvent(irc[], ev)
if irc.processOther(ev):
irc.handleEvent(irc, ev)
proc register*(d: PDispatcher, irc: PAsyncIRC) =
## Registers ``irc`` with dispatcher ``d``.
irc.asyncSock.handleConnect =
proc (s: PAsyncSocket) =
handleConnect(s, irc)
irc.asyncSock.handleRead =
proc (s: PAsyncSocket) =
handleRead(s, irc)
irc.asyncSock.handleTask =
proc (s: PAsyncSocket) =
handleTask(s, irc)
d.register(irc.asyncSock)
irc.myDispatcher = d
proc connect*(irc: PAsyncIRC) =
## Equivalent of connect for ``TIRC`` but specifically created for asyncio.
assert(irc.address != "")
assert(irc.port != TPort(0))
discard """proc handleTask(h: PObject) =
var irc = PAsyncIRC(h)
var ev: TIRCEvent
if PAsyncIRC(h)[].processOther(ev):
irc.handleEvent(irc[], ev, irc.userArg)"""
irc.asyncSock.connect(irc.address, irc.port)
proc reconnect*(irc: PAsyncIRC, timeout = 5000) =
## Reconnects to an IRC server.
##
## ``Timeout`` specifies the time to wait in miliseconds between multiple
## consecutive reconnections.
##
## This should be used when an ``EvDisconnected`` event occurs.
##
## When successfully reconnected an ``EvConnected`` event will occur.
let secSinceReconnect = int(epochTime() - irc.lastReconnect)
if secSinceReconnect < timeout:
sleep(timeout - secSinceReconnect)
irc.asyncSock = AsyncSocket()
irc.myDispatcher.register(irc)
irc.connect()
irc.lastReconnect = epochTime()
proc asyncIRC*(address: string, port: TPort = 6667.TPort,
nick = "NimrodBot",
@ -406,7 +440,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
realname = "NimrodBot", serverPass = "",
joinChans: seq[string] = @[],
msgLimit: bool = true,
ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
): PAsyncIRC =
## Use this function if you want to use asyncio's dispatcher.
##
@ -429,19 +463,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
result.msgLimit = msgLimit
result.messageBuffer = @[]
result.handleEvent = ircEvent
proc register*(d: PDispatcher, irc: PAsyncIRC) =
## Registers ``irc`` with dispatcher ``d``.
irc.asyncSock.handleConnect =
proc (s: PAsyncSocket) =
handleConnect(s, irc)
irc.asyncSock.handleRead =
proc (s: PAsyncSocket) =
handleRead(s, irc)
irc.asyncSock.handleTask =
proc (s: PAsyncSocket) =
handleTask(s, irc)
d.register(irc.asyncSock)
result.asyncSock = AsyncSocket()
when isMainModule:
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
@ -456,6 +478,8 @@ when isMainModule:
var event: TIRCEvent
if client.poll(event):
case event.typ
of EvConnected:
nil
of EvDisconnected:
break
of EvMsg:

View file

@ -13,6 +13,23 @@
## (unlike XML). It is easy for machines to parse and generate.
## JSON is based on a subset of the JavaScript Programming Language,
## Standard ECMA-262 3rd Edition - December 1999.
##
## Usage example:
##
## .. code-block:: nimrod
## let
## small_json = """{"test": 1.3, "key2": true}"""
## jobj = parseJson(small_json)
## assert (jobj.kind == JObject)
## echo($jobj["test"].fnum)
## echo($jobj["key2"].bval)
##
## Results in:
##
## .. code-block:: nimrod
##
## 1.3000000000000000e+00
## true
import
hashes, strutils, lexbase, streams, unicode
@ -524,6 +541,11 @@ proc newJString*(s: String): PJsonNode =
result.kind = JString
result.str = s
proc newJStringMove(s: String): PJsonNode =
new(result)
result.kind = JString
shallowCopy(result.str, s)
proc newJInt*(n: biggestInt): PJsonNode =
## Creates a new `JInt PJsonNode`.
new(result)
@ -607,7 +629,7 @@ proc len*(n: PJsonNode): int =
else: nil
proc `[]`*(node: PJsonNode, name: String): PJsonNode =
## Gets a field from a `JObject`.
## Gets a field from a `JObject`. Returns nil if the key is not found.
assert(node.kind == JObject)
for key, item in items(node.fields):
if key == name:
@ -792,7 +814,9 @@ proc parseJson(p: var TJsonParser): PJsonNode =
## Parses JSON from a JSON Parser `p`.
case p.tok
of tkString:
result = newJString(p.a)
# we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
result = newJStringMove(p.a)
p.a = ""
discard getTok(p)
of tkInt:
result = newJInt(parseBiggestInt(p.a))
@ -893,6 +917,10 @@ when isMainModule:
echo(parsed["keyÄÖöoßß"])
echo()
echo(pretty(parsed2))
try:
echo(parsed["key2"][12123])
raise newException(EInvalidValue, "That line was expected to fail")
except EInvalidIndex: echo()
discard """
while true:

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -141,6 +141,11 @@ proc randomize*()
## number, i.e. a tickcount. Note: Does nothing for the ECMAScript target,
## as ECMAScript does not support this.
proc randomize*(seed: int)
## initializes the random number generator with a specific seed.
## Note: Does nothing for the ECMAScript target,
## as ECMAScript does not support this.
when not defined(ECMAScript):
proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".}
## computes the square root of `x`.
@ -190,15 +195,17 @@ when not defined(ECMAScript):
proc rand(): cint {.importc: "rand", nodecl.}
when not defined(windows):
proc srand48(seed: cint) {.importc: "srand48", nodecl.}
proc srand48(seed: clong) {.importc: "srand48", nodecl.}
proc drand48(): float {.importc: "drand48", nodecl.}
proc random(max: float): float =
result = drand48() * max
proc randomize() =
let x = gettime(nil)
srand(x)
when defined(srand48): srand48(x)
randomize(gettime(nil))
proc randomize(seed: int) =
srand(cint(seed))
when defined(srand48): srand48(seed)
proc random(max: int): int =
result = int(rand()) mod max
@ -217,6 +224,7 @@ else:
proc random(max: float): float =
result = float(mathrandom() * float(max))
proc randomize() = nil
proc randomize(seed: int) = nil
proc sqrt*(x: float): float {.importc: "Math.sqrt", nodecl.}
proc ln*(x: float): float {.importc: "Math.log", nodecl.}
@ -301,3 +309,18 @@ proc standardDeviation*(s: TRunningStat): float =
{.pop.}
{.pop.}
when isMainModule and not defined(ECMAScript):
# Verifies random seed initialization.
let seed = gettime(nil)
randomize(seed)
const SIZE = 10
var buf : array[0..SIZE, int]
# Fill the buffer with random values
for i in 0..SIZE-1:
buf[i] = random(high(int))
# Check that the second random calls are the same for each position.
randomize(seed)
for i in 0..SIZE-1:
assert buf[i] == random(high(int)), "non deterministic random seeding"
echo "random values equal after reseeding"

View file

@ -554,7 +554,8 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
var dotPos = path.len
for i in countdown(len(path)-1, 0):
if path[i] == ExtSep:
if dotPos == path.len and i > 0: dotPos = i
if dotPos == path.len and i > 0 and
path[i-1] notin {dirsep, altsep}: dotPos = i
elif path[i] in {dirsep, altsep}:
sepPos = i
break
@ -1448,7 +1449,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} =
else:
var a, b: Ttimespec
a.tv_sec = TTime(milsecs div 1000)
a.tv_nsec = (milsecs mod 1000) * 1000
a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
discard posix.nanosleep(a, b)
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -201,9 +201,9 @@ proc execProcesses*(cmds: openArray[string],
q[r] = startCmd(cmds[i], options=options)
inc(i)
if i > high(cmds): break
for i in 0..m-1:
if q[i] != nil: close(q[i])
result = max(waitForExit(q[i]), result)
for j in 0..m-1:
if q[j] != nil: close(q[j])
result = max(waitForExit(q[j]), result)
else:
for i in 0..high(cmds):
var p = startCmd(cmds[i], options=options)

View file

@ -49,26 +49,35 @@ proc raiseNoOK(status: string) =
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
proc parseStatus(r: TRedis): TRedisStatus =
var line = r.socket.recv.string
var line = ""
if r.socket.recvLine(line):
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
return line.substr(1, line.len-3) # Strip '+' and \c\L.
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
return line.substr(1) # Strip '+'
else:
OSError()
proc parseInteger(r: TRedis): TRedisInteger =
var line = r.socket.recv.string
var line = ""
if r.socket.recvLine(line):
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != ':':
raiseInvalidReply(':', line[0])
# Strip ':' and \c\L.
if parseBiggestInt(line, result, 1) == 0:
raise newException(EInvalidReply, "Unable to parse integer.")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != ':':
raiseInvalidReply(':', line[0])
# Strip ':'
if parseBiggestInt(line, result, 1) == 0:
raise newException(EInvalidReply, "Unable to parse integer.")
else: OSError()
proc recv(sock: TSocket, size: int): TaintedString =
result = newString(size).TaintedString
@ -838,8 +847,11 @@ proc save*(r: TRedis) =
proc shutdown*(r: TRedis) =
## Synchronously save the dataset to disk and then shut down the server
r.sendCommand("SHUTDOWN")
var s = r.socket.recv()
if s.string.len != 0: raise newException(ERedis, s.string)
var s = "".TaintedString
if r.socket.recvLine(s):
if s.string.len != 0: raise newException(ERedis, s.string)
else:
OSError()
proc slaveof*(r: TRedis, host: string, port: string) =
## Make the server a slave of another instance, or promote it as master

View file

@ -23,6 +23,8 @@
##
## run(handleRequest)
##
## **Warning:** The API of this module is unstable, and therefore is subject
## to change.
import sockets, strutils, os, strtabs, asyncio

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -14,6 +14,8 @@
## For OpenSSL support compile with ``-d:ssl``. When using SSL be aware that
## most functions will then raise ``ESSL`` on SSL errors.
{.deadCodeElim: on.}
when hostos == "solaris":
{.passl: "-lsocket -lnsl".}
@ -45,12 +47,15 @@ when defined(ssl):
TSSLAcceptResult* = enum
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
const
BufferSize*: int = 4000 ## size of a buffered socket's buffer
type
TSocketImpl = object ## socket type
fd: cint
case isBuffered: bool # determines whether this socket is buffered.
of true:
buffer: array[0..4000, char]
buffer: array[0..BufferSize, char]
currPos: int # current index in buffer
bufLen: int # current length of buffer
of false: nil
@ -60,6 +65,8 @@ type
sslHandle: PSSL
sslContext: PSSLContext
sslNoHandshake: bool # True if needs handshake.
sslHasPeekChar: bool
sslPeekChar: char
of false: nil
TSocket* = ref TSocketImpl
@ -256,7 +263,10 @@ when defined(ssl):
of protSSLv23:
newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
of protSSLv2:
newCTX = SSL_CTX_new(SSLv2_method())
when not defined(linux):
newCTX = SSL_CTX_new(SSLv2_method())
else:
SSLError()
of protSSLv3:
newCTX = SSL_CTX_new(SSLv3_method())
of protTLSv1:
@ -286,12 +296,55 @@ when defined(ssl):
socket.sslContext = ctx
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext))
socket.sslNoHandshake = false
socket.sslHasPeekChar = false
if socket.sslHandle == nil:
SSLError()
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
SSLError()
proc SocketError*(socket: TSocket, err: int = -1, async = false) =
## Raises proper errors based on return values of ``recv`` functions.
##
## If ``async`` is ``True`` no error will be thrown in the case when the
## error was caused by no data being available to be read.
##
## If ``err`` is not lower than 0 no exception will be raised.
when defined(ssl):
if socket.isSSL:
if err <= 0:
var ret = SSLGetError(socket.sslHandle, err.cint)
case ret
of SSL_ERROR_ZERO_RETURN:
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
if async:
return
else: SSLError("Not enough data on socket.")
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
if async:
return
else: SSLError("Not enough data on socket.")
of SSL_ERROR_WANT_X509_LOOKUP:
SSLError("Function for x509 lookup has been called.")
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
SSLError()
else: SSLError("Unknown Error")
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
if async:
when defined(windows):
# TODO: Test on Windows
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
return
else: OSError()
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
return
else: OSError()
else: OSError()
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
@ -844,11 +897,8 @@ proc checkBuffer(readfds: var seq[TSocket]): int =
var res: seq[TSocket] = @[]
result = 0
for s in readfds:
if s.isBuffered:
if s.bufLen <= 0 or s.currPos == s.bufLen:
res.add(s)
else:
inc(result)
if hasDataBuffered(s):
inc(result)
else:
res.add(s)
readfds = res
@ -970,47 +1020,76 @@ template retRead(flags, readBytes: int) =
proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} =
## receives data from a socket
if size == 0: return
if socket.isBuffered:
if socket.bufLen == 0:
retRead(0'i32, 0)
when true:
var read = 0
while read < size:
if socket.currPos >= socket.bufLen:
retRead(0'i32, read)
let chunk = min(socket.bufLen-socket.currPos, size-read)
var d = cast[cstring](data)
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
read.inc(chunk)
socket.currPos.inc(chunk)
else:
var read = 0
while read < size:
if socket.currPos >= socket.bufLen:
retRead(0'i32, read)
var d = cast[cstring](data)
d[read] = socket.buffer[socket.currPos]
read.inc(1)
socket.currPos.inc(1)
var read = 0
while read < size:
if socket.currPos >= socket.bufLen:
retRead(0'i32, read)
let chunk = min(socket.bufLen-socket.currPos, size-read)
var d = cast[cstring](data)
copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
read.inc(chunk)
socket.currPos.inc(chunk)
result = read
else:
when defined(ssl):
if socket.isSSL:
result = SSLRead(socket.sslHandle, data, size)
if socket.sslHasPeekChar:
copyMem(data, addr(socket.sslPeekChar), 1)
socket.sslHasPeekChar = false
if size-1 > 0:
var d = cast[cstring](data)
result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1
else:
result = 1
else:
result = SSLRead(socket.sslHandle, data, size)
else:
result = recv(socket.fd, data, size.cint, 0'i32)
else:
result = recv(socket.fd, data, size.cint, 0'i32)
proc recv*(socket: TSocket, data: var string, size: int): int =
## higher-level version of the above
##
## When 0 is returned the socket's connection has been closed.
##
## This function will throw an EOS exception when an error occurs. A value
## lower than 0 is never returned.
##
## **Note**: ``data`` must be initialised.
data.setLen(size)
result = recv(socket, cstring(data), size)
if result < 0:
data.setLen(0)
socket.SocketError(result)
data.setLen(result)
proc recvAsync*(socket: TSocket, data: var string, size: int): int =
## Async version of the above.
##
## When socket is non-blocking and no data is available on the socket,
## ``-1`` will be returned and ``data`` will be ``""``.
##
## **Note**: ``data`` must be initialised.
data.setLen(size)
result = recv(socket, cstring(data), size)
if result < 0:
data.setLen(0)
socket.SocketError(async = true)
result = -1
data.setLen(result)
proc waitFor(socket: TSocket, waited: var float, timeout: int): int {.
tags: [FTime].} =
## returns the number of characters available to be read. In unbuffered
## sockets this is always 1, otherwise this may as big as the buffer, currently
## 4000.
## sockets this is always 1, otherwise this may as big as ``BufferSize``.
result = 1
if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos:
result = socket.bufLen - socket.currPos
@ -1040,6 +1119,18 @@ proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {.
result = read
proc recv*(socket: TSocket, data: var string, size: int, timeout: int): int =
## higher-level version of the above.
##
## Similar to the non-timeout version this will throw an EOS exception
## when an error occurs.
data.setLen(size)
result = recv(socket, cstring(data), size, timeout)
if result < 0:
data.setLen(0)
socket.SocketError()
data.setLen(result)
proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} =
if socket.isBuffered:
result = 1
@ -1052,8 +1143,12 @@ proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} =
else:
when defined(ssl):
if socket.isSSL:
raise newException(ESSL, "Sorry, you cannot use recvLine on an unbuffered SSL socket.")
if not socket.sslHasPeekChar:
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
socket.sslHasPeekChar = true
c = socket.sslPeekChar
return
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
proc recvLine*(socket: TSocket, line: var TaintedString): bool {.
@ -1062,14 +1157,12 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool {.
## added to ``line``, however if solely ``\r\L`` is received then ``line``
## will be set to it.
##
## ``True`` is returned if data is available. ``False`` usually suggests an
## error, EOS exceptions are not raised in favour of this.
## ``True`` is returned if data is available. ``False`` suggests an
## error, EOS exceptions are not raised and ``False`` is simply returned
## instead.
##
## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
## will be returned.
##
## **Warning:** Using this function on a unbuffered ssl socket will result
## in an error.
template addNLIfEmpty(): stmt =
if line.len == 0:
line.add("\c\L")
@ -1096,6 +1189,8 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool {.
tags: [FReadIO, FTime].} =
## variant with a ``timeout`` parameter, the timeout parameter specifies
## how many miliseconds to wait for data.
##
## ``ETimeout`` will be raised if ``timeout`` is exceeded.
template addNLIfEmpty(): stmt =
if line.len == 0:
line.add("\c\L")
@ -1148,11 +1243,13 @@ proc recvLineAsync*(socket: TSocket,
elif c == '\L': return RecvFullLine
add(line.string, c)
proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO].} =
proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
## receives all the available data from the socket.
## Socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
## ``""`` will be returned.
##
## **Deprecated since version 0.9.2**: This function is not safe for use.
const bufSize = 4000
result = newStringOfCap(bufSize).TaintedString
var pos = 0
@ -1177,25 +1274,31 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO].} =
add(result.string, buf)
if bytesRead != bufSize-1: break
{.push warning[deprecated]: off.}
proc recvTimeout*(socket: TSocket, timeout: int): TaintedString {.
tags: [FReadIO].} =
tags: [FReadIO], deprecated.} =
## overloaded variant to support a ``timeout`` parameter, the ``timeout``
## parameter specifies the amount of miliseconds to wait for data on the
## socket.
##
## **Deprecated since version 0.9.2**: This function is not safe for use.
if socket.bufLen == 0:
var s = @[socket]
if s.select(timeout) != 1:
raise newException(ETimeout, "Call to recv() timed out.")
return socket.recv
{.pop.}
proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
tags: [FReadIO].} =
tags: [FReadIO], deprecated.} =
## receives all the data from a non-blocking socket. If socket is non-blocking
## and there are no messages available, `False` will be returned.
## Other socket errors will result in an ``EOS`` error.
## If socket is not a connectionless socket and socket is not connected
## ``s`` will be set to ``""``.
##
## **Deprecated since version 0.9.2**: This function is not safe for use.
const bufSize = 1000
# ensure bufSize capacity:
setLen(s.string, bufSize)
@ -1282,13 +1385,25 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
return False
else: OSError()
proc skip*(socket: TSocket) {.tags: [FReadIO].} =
proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
## skips all the data that is pending for the socket
##
## **Deprecated since version 0.9.2**: This function is not safe for use.
const bufSize = 1000
var buf = alloc(bufSize)
while recv(socket, buf, bufSize) == bufSize: nil
dealloc(buf)
proc skip*(socket: TSocket, size: int) =
## Skips ``size`` amount of bytes.
##
## Returns the number of skipped bytes.
var dummy = alloc(size)
var bytesSkipped = 0
while bytesSkipped != size:
bytesSkipped += recv(socket, dummy, size-bytesSkipped)
dealloc(dummy)
proc send*(socket: TSocket, data: pointer, size: int): int {.
tags: [FWriteIO].} =
## sends data to a socket.
@ -1312,21 +1427,26 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
OSError()
proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
## sends data to a non-blocking socket. Returns whether ``data`` was sent.
result = true
var bytesSent = send(socket, cstring(data), data.len)
proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
## sends data to a non-blocking socket.
## Returns ``0`` if no data could be sent, if data has been sent
## returns the amount of bytes of ``data`` that was successfully sent. This
## number may not always be the length of ``data`` but typically is.
##
## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error
## occurs.
result = send(socket, cstring(data), data.len)
when defined(ssl):
if socket.isSSL:
if bytesSent <= 0:
let ret = SSLGetError(socket.sslHandle, bytesSent.cint)
if result <= 0:
let ret = SSLGetError(socket.sslHandle, result.cint)
case ret
of SSL_ERROR_ZERO_RETURN:
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
SSLError("Unexpected error occured.") # This should just not happen.
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
return false
return 0
of SSL_ERROR_WANT_X509_LOOKUP:
SSLError("Function for x509 lookup has been called.")
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
@ -1334,17 +1454,18 @@ proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
else: SSLError("Unknown Error")
else:
return
if bytesSent == -1:
if result == -1:
when defined(windows):
var err = WSAGetLastError()
# TODO: Test on windows.
if err == WSAEINPROGRESS:
return false
return 0
else: OSError()
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
return false
return 0
else: OSError()
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
## safe alternative to ``send``. Does not raise an EOS when an error occurs,
@ -1385,8 +1506,7 @@ proc sendTo*(socket: TSocket, address: string, port: TPort,
result = socket.sendTo(address, port, cstring(data), data.len)
when defined(Windows):
const
SOCKET_ERROR = -1
const
IOCPARM_MASK = 127
IOC_IN = int(-2147483648)
FIONBIO = int(IOC_IN or ((sizeof(int) and IOCPARM_MASK) shl 16) or
@ -1399,7 +1519,7 @@ when defined(Windows):
proc setBlocking(s: TSocket, blocking: bool) =
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if SOCKET_ERROR == ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)):
if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
OSError()
else: # BSD sockets
var x: int = fcntl(s.fd, F_GETFL, 0)

View file

@ -267,7 +267,8 @@ proc newFileStream*(f: TFile): PFileStream =
proc newFileStream*(filename: string, mode: TFileMode): PFileStream =
## creates a new stream from the file named `filename` with the mode `mode`.
## If the file cannot be opened, nil is returned.
## If the file cannot be opened, nil is returned. See the `system
## <system.html>`_ module for a list of available TFileMode enums.
var f: TFile
if Open(f, filename, mode): result = newFileStream(f)

View file

@ -36,11 +36,23 @@ proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
result = t.counter
iterator pairs*(t: PStringTable): tuple[key, value: string] =
## iterates over any (key, value) pair in the table `t`.
## iterates over every (key, value) pair in the table `t`.
for h in 0..high(t.data):
if not isNil(t.data[h].key):
yield (t.data[h].key, t.data[h].val)
iterator keys*(t: PStringTable): string =
## iterates over every key in the table `t`.
for h in 0..high(t.data):
if not isNil(t.data[h].key):
yield t.data[h].key
iterator values*(t: PStringTable): string =
## iterates over every value in the table `t`.
for h in 0..high(t.data):
if not isNil(t.data[h].key):
yield t.data[h].val
type
TFormatFlag* = enum ## flags for the `%` operator
useEnvironment, ## use environment variable if the ``$key``

View file

@ -186,7 +186,24 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
## for word in split(";;this;is;an;;example;;;", {';'}):
## writeln(stdout, word)
##
## produces the same output.
## produces the same output. The code:
##
## .. code-block:: nimrod
## let date = "2012-11-20T22:08:08.398990"
## let separators = {' ', '-', ':', 'T'}
## for number in split(date, separators):
## writeln(stdout, number)
##
## Results in:
##
## .. code-block:: nimrod
## "2012"
## "11"
## "20"
## "22"
## "08"
## "08.398990"
##
var last = 0
assert(not ('\0' in seps))
while last < len(s):
@ -833,13 +850,51 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
for c in items(s):
case c
of '\0'..'\31', '\128'..'\255':
add(result, '\\')
add(result, "\\x")
add(result, toHex(ord(c), 2))
of '\\': add(result, "\\\\")
of '\'': add(result, "\\'")
of '\"': add(result, "\\\"")
else: add(result, c)
add(result, suffix)
proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
rtl, extern: "nsuUnescape".} =
## Unescapes a string `s`. This complements ``escape`` as it performs the
## opposite operations.
##
## If `s` does not begin with ``prefix`` and end with ``suffix`` a EInvalidValue
## exception will be raised.
result = newStringOfCap(s.len)
var i = 0
if s[0 .. prefix.len-1] != prefix:
raise newException(EInvalidValue,
"String does not start with a prefix of: " & prefix)
i.inc()
while True:
if i == s.len-suffix.len: break
case s[i]
of '\\':
case s[i+1]:
of 'x':
let j = parseHexInt(s[i+2 .. i+3])
result.add(chr(j))
inc(i, 2)
of '\\':
result.add('\\')
of '\'':
result.add('\'')
of '\"':
result.add('\"')
else: result.add("\\" & s[i+1])
inc(i)
of '\0': break
else:
result.add(s[i])
i.inc()
if s[i .. -1] != suffix:
raise newException(EInvalidValue,
"String does not end with a suffix of: " & suffix)
proc validIdentifier*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidIdentifier".} =

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -38,7 +38,8 @@ type
char* {.magic: Char.} ## built-in 8 bit character type (unsigned)
string* {.magic: String.} ## built-in string type
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
pointer* {.magic: Pointer.} ## built-in pointer type
pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
## operator to get a pointer to a variable
const
on* = true ## alias for ``true``
@ -116,7 +117,13 @@ proc new*(T: typedesc): ref T =
## creates a new object of type ``T`` and returns a safe (traced)
## reference to it as result value
new(result)
proc unsafeNew*[T](a: var ref T, size: int) {.magic: "New", noSideEffect.}
## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. This is **unsafe** as it allocates an object
## of the passed ``size``. This should only be used for optimization
## purposes when you know what you're doing!
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
## leaked implementation detail. Do not use.
@ -1060,86 +1067,87 @@ proc substr*(s: string, first, last: int): string {.
## is used instead: This means ``substr`` can also be used to `cut`:idx:
## or `limit`:idx: a string's length.
proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
## overwrites the contents of the memory at ``p`` with the value 0.
## Exactly ``size`` bytes will be overwritten. Like any procedure
## dealing with raw memory this is *unsafe*.
when not defined(nimrodVM):
proc zeroMem*(p: Pointer, size: int) {.importc, noDecl.}
## overwrites the contents of the memory at ``p`` with the value 0.
## Exactly ``size`` bytes will be overwritten. Like any procedure
## dealing with raw memory this is *unsafe*.
proc copyMem*(dest, source: Pointer, size: int) {.importc: "memcpy", noDecl.}
## copies the contents from the memory at ``source`` to the memory
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
## regions may not overlap. Like any procedure dealing with raw
## memory this is *unsafe*.
proc copyMem*(dest, source: Pointer, size: int) {.importc: "memcpy", noDecl.}
## copies the contents from the memory at ``source`` to the memory
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
## regions may not overlap. Like any procedure dealing with raw
## memory this is *unsafe*.
proc moveMem*(dest, source: Pointer, size: int) {.importc: "memmove", noDecl.}
## copies the contents from the memory at ``source`` to the memory
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
## regions may overlap, ``moveMem`` handles this case appropriately
## and is thus somewhat more safe than ``copyMem``. Like any procedure
## dealing with raw memory this is still *unsafe*, though.
proc moveMem*(dest, source: Pointer, size: int) {.importc: "memmove", noDecl.}
## copies the contents from the memory at ``source`` to the memory
## at ``dest``. Exactly ``size`` bytes will be copied. The memory
## regions may overlap, ``moveMem`` handles this case appropriately
## and is thus somewhat more safe than ``copyMem``. Like any procedure
## dealing with raw memory this is still *unsafe*, though.
proc equalMem*(a, b: Pointer, size: int): bool {.
importc: "equalMem", noDecl, noSideEffect.}
## compares the memory blocks ``a`` and ``b``. ``size`` bytes will
## be compared. If the blocks are equal, true is returned, false
## otherwise. Like any procedure dealing with raw memory this is
## *unsafe*.
proc equalMem*(a, b: Pointer, size: int): bool {.
importc: "equalMem", noDecl, noSideEffect.}
## compares the memory blocks ``a`` and ``b``. ``size`` bytes will
## be compared. If the blocks are equal, true is returned, false
## otherwise. Like any procedure dealing with raw memory this is
## *unsafe*.
proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is not initialized, so reading
## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap.
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap.
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
## be freed with ``dealloc``.
## The allocated memory belongs to its allocating thread!
## Use `reallocShared` to reallocate from a shared heap.
proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
## frees the memory allocated with ``alloc``, ``alloc0`` or
## ``realloc``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap.
proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is not initialized, so reading
## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap.
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap.
proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
## be freed with ``dealloc``.
## The allocated memory belongs to its allocating thread!
## Use `reallocShared` to reallocate from a shared heap.
proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
## frees the memory allocated with ``alloc``, ``alloc0`` or
## ``realloc``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap.
proc allocShared*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
proc allocShared0*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``.
proc reallocShared*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at least
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
## block has to be freed with ``deallocShared``.
proc deallocShared*(p: Pointer) {.noconv, rtl.}
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
## ``reallocShared``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
proc allocShared*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
proc allocShared0*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``.
proc reallocShared*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at least
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
## block has to be freed with ``deallocShared``.
proc deallocShared*(p: Pointer) {.noconv, rtl.}
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
## ``reallocShared``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
## swaps the values `a` and `b`. This is often more efficient than
@ -1215,15 +1223,16 @@ const
# GC interface:
proc getOccupiedMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process and hold data.
when not defined(nimrodVM):
proc getOccupiedMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process and hold data.
proc getFreeMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process, but do not
## hold any meaningful data.
proc getFreeMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process, but do not
## hold any meaningful data.
proc getTotalMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process.
proc getTotalMem*(): int {.rtl.}
## returns the number of bytes that are owned by the process.
iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
@ -1452,15 +1461,51 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
result = s[L]
setLen(s, L)
proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] =
proc each*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] {.
deprecated.} =
## The well-known ``map`` operation from functional programming. Applies
## `op` to every item in `data` and returns the result as a sequence.
##
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
newSeq(result, data.len)
for i in 0..data.len-1: result[i] = op(data[i])
proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) =
proc each*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) {.
deprecated.} =
## The well-known ``map`` operation from functional programming. Applies
## `op` to every item in `data`.
## `op` to every item in `data` modifying it directly.
##
## **Deprecated since version 0.9:** Use the ``map`` proc instead.
for i in 0..data.len-1: op(data[i])
proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}): seq[S] =
## Returns a new sequence with the results of `op` applied to every item in
## `data`.
##
## Since the input is not modified you can use this version of ``map`` to
## transform the type of the elements in the input sequence. Example:
##
## .. code-block:: nimrod
## let
## a = @[1, 2, 3, 4]
## b = map(a, proc(x: int): string = $x)
## assert b == @["1", "2", "3", "4"]
newSeq(result, data.len)
for i in 0..data.len-1: result[i] = op(data[i])
proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) =
## Applies `op` to every item in `data` modifying it directly.
##
## Note that this version of ``map`` requires your input and output types to
## be the same, since they are modified in-place. Example:
##
## .. code-block:: nimrod
## var a = @["1", "2", "3", "4"]
## echo repr(a)
## # --> ["1", "2", "3", "4"]
## map(a, proc(x: var string) = x &= "42")
## echo repr(a)
## # --> ["142", "242", "342", "442"]
for i in 0..data.len-1: op(data[i])
iterator fields*[T: tuple](x: T): TObject {.
@ -1541,41 +1586,42 @@ when false:
# ----------------- GC interface ---------------------------------------------
proc GC_disable*() {.rtl, inl.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
when not defined(nimrodVM):
proc GC_disable*() {.rtl, inl.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enable*() {.rtl, inl.}
## enables the GC again.
proc GC_enable*() {.rtl, inl.}
## enables the GC again.
proc GC_fullCollect*() {.rtl.}
## forces a full garbage collection pass.
## Ordinary code does not need to call this (and should not).
proc GC_fullCollect*() {.rtl.}
## forces a full garbage collection pass.
## Ordinary code does not need to call this (and should not).
type
TGC_Strategy* = enum ## the strategy the GC should use for the application
gcThroughput, ## optimize for throughput
gcResponsiveness, ## optimize for responsiveness (default)
gcOptimizeTime, ## optimize for speed
gcOptimizeSpace ## optimize for memory footprint
type
TGC_Strategy* = enum ## the strategy the GC should use for the application
gcThroughput, ## optimize for throughput
gcResponsiveness, ## optimize for responsiveness (default)
gcOptimizeTime, ## optimize for speed
gcOptimizeSpace ## optimize for memory footprint
proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.}
## tells the GC the desired strategy for the application.
## **Deprecated** since version 0.8.14. This has always been a nop.
proc GC_setStrategy*(strategy: TGC_Strategy) {.rtl, deprecated.}
## tells the GC the desired strategy for the application.
## **Deprecated** since version 0.8.14. This has always been a nop.
proc GC_enableMarkAndSweep*() {.rtl.}
proc GC_disableMarkAndSweep*() {.rtl.}
## the current implementation uses a reference counting garbage collector
## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_enableMarkAndSweep*() {.rtl.}
proc GC_disableMarkAndSweep*() {.rtl.}
## the current implementation uses a reference counting garbage collector
## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_getStatistics*(): string {.rtl.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_getStatistics*(): string {.rtl.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref".}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref".}
proc GC_ref*(x: string) {.magic: "GCref".}
@ -1701,28 +1747,29 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
## get type information for `x`. Ordinary code should not use this, but
## the `typeinfo` module instead.
when not defined(EcmaScript) and not defined(NimrodVM):
when not defined(EcmaScript): #and not defined(NimrodVM):
{.push stack_trace: off, profiler:off.}
proc initGC()
when not defined(boehmgc) and not defined(useMalloc):
proc initAllocator() {.inline.}
when not defined(NimrodVM):
proc initGC()
when not defined(boehmgc) and not defined(useMalloc):
proc initAllocator() {.inline.}
proc initStackBottom() {.inline, compilerproc.} =
# WARNING: This is very fragile! An array size of 8 does not work on my
# Linux 64bit system. Very strange, but we are at the will of GCC's
# optimizer...
when defined(setStackBottom):
var locals {.volatile.}: pointer
locals = addr(locals)
setStackBottom(locals)
proc initStackBottom() {.inline, compilerproc.} =
# WARNING: This is very fragile! An array size of 8 does not work on my
# Linux 64bit system. -- That's because the stack direction is the other
# way round.
when defined(setStackBottom):
var locals {.volatile.}: pointer
locals = addr(locals)
setStackBottom(locals)
var
strDesc: TNimType
var
strDesc: TNimType
strDesc.size = sizeof(string)
strDesc.kind = tyString
strDesc.flags = {ntfAcyclic}
strDesc.size = sizeof(string)
strDesc.kind = tyString
strDesc.flags = {ntfAcyclic}
include "system/ansi_c"
@ -1730,28 +1777,27 @@ when not defined(EcmaScript) and not defined(NimrodVM):
result = int(c_strcmp(x, y))
const pccHack = if defined(pcc): "_" else: "" # Hack for PCC
when defined(windows):
# work-around C's sucking abstraction:
# BUGFIX: stdin and stdout should be binary files!
proc setmode(handle, mode: int) {.importc: pccHack & "setmode",
header: "<io.h>".}
proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno",
header: "<fcntl.h>".}
var
O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int
when not defined(NimrodVM):
when defined(windows):
# work-around C's sucking abstraction:
# BUGFIX: stdin and stdout should be binary files!
proc setmode(handle, mode: int) {.importc: pccHack & "setmode",
header: "<io.h>".}
proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno",
header: "<fcntl.h>".}
var
O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int
# we use binary mode in Windows:
setmode(fileno(c_stdin), O_BINARY)
setmode(fileno(c_stdout), O_BINARY)
when defined(endb):
proc endbStep()
# we use binary mode in Windows:
setmode(fileno(c_stdin), O_BINARY)
setmode(fileno(c_stdout), O_BINARY)
when defined(endb):
proc endbStep()
# ----------------- IO Part ------------------------------------------------
type
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
# data hiding
CFile {.importc: "FILE", nodecl, final, incompletestruct.} = object
TFile* = ptr CFile ## The type representing a file handle.
TFileMode* = enum ## The file mode when opening a file.
@ -1777,7 +1823,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
## The standard error stream.
##
## Note: In my opinion, this should not be used -- the concept of a
## separate error stream is a design flaw of UNIX. A seperate *message
## separate error stream is a design flaw of UNIX. A separate *message
## stream* is a good idea, but since it is named ``stderr`` there are few
## programs out there that distinguish properly between ``stdout`` and
## ``stderr``. So, that's what you get if you don't name your variables
@ -1934,31 +1980,32 @@ when not defined(EcmaScript) and not defined(NimrodVM):
# -------------------------------------------------------------------------
proc allocCStringArray*(a: openArray[string]): cstringArray =
## creates a NULL terminated cstringArray from `a`. The result has to
## be freed with `deallocCStringArray` after it's not needed anymore.
result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
for i in 0 .. a.high:
# XXX get rid of this string copy here:
var x = a[i]
result[i] = cast[cstring](alloc0(x.len+1))
copyMem(result[i], addr(x[0]), x.len)
when not defined(NimrodVM):
proc allocCStringArray*(a: openArray[string]): cstringArray =
## creates a NULL terminated cstringArray from `a`. The result has to
## be freed with `deallocCStringArray` after it's not needed anymore.
result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
for i in 0 .. a.high:
# XXX get rid of this string copy here:
var x = a[i]
result[i] = cast[cstring](alloc0(x.len+1))
copyMem(result[i], addr(x[0]), x.len)
proc deallocCStringArray*(a: cstringArray) =
## frees a NULL terminated cstringArray.
var i = 0
while a[i] != nil:
dealloc(a[i])
inc(i)
dealloc(a)
proc deallocCStringArray*(a: cstringArray) =
## frees a NULL terminated cstringArray.
var i = 0
while a[i] != nil:
dealloc(a[i])
inc(i)
dealloc(a)
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
## atomic increment of `memLoc`. Returns the value after the operation.
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.}
## atomic decrement of `memLoc`. Returns the value after the operation.
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
## atomic increment of `memLoc`. Returns the value after the operation.
proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.}
## atomic decrement of `memLoc`. Returns the value after the operation.
include "system/atomics"
include "system/atomics"
type
PSafePoint = ptr TSafePoint
@ -1974,71 +2021,76 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when hasThreadSupport:
include "system/syslocks"
include "system/threads"
elif not defined(nogc):
elif not defined(nogc) and not defined(NimrodVM):
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
initGC()
proc setControlCHook*(hook: proc () {.noconv.})
## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
proc writeStackTrace*() {.tags: [FWriteIO].}
## writes the current stack trace to ``stderr``. This is only works
## for debug builds.
when hostOS != "standalone":
proc getStackTrace*(): string
## gets the current stack trace. This only works for debug builds.
proc getStackTrace*(e: ref E_Base): string
## gets the stack trace associated with `e`, which is the stack that
## lead to the ``raise`` statement. This only works for debug builds.
when not defined(NimrodVM):
proc setControlCHook*(hook: proc () {.noconv.})
## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
{.push stack_trace: off, profiler:off.}
when hostOS == "standalone":
include "system/embedded"
else:
include "system/excpt"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/arithm"
{.pop.} # stack trace
{.pop.} # stack trace
when hostOS != "standalone": include "system/dyncalls"
include "system/sets"
proc writeStackTrace*() {.tags: [FWriteIO].}
## writes the current stack trace to ``stderr``. This is only works
## for debug builds.
when hostOS != "standalone":
proc getStackTrace*(): string
## gets the current stack trace. This only works for debug builds.
const
GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
of 4: d = int(cast[ptr int32](a +% n.offset)[])
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
return d
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
proc getStackTrace*(e: ref E_Base): string
## gets the stack trace associated with `e`, which is the stack that
## lead to the ``raise`` statement. This only works for debug builds.
{.push stack_trace: off, profiler:off.}
when hostOS == "standalone":
include "system/embedded"
else:
result = n.sons[n.len]
include "system/excpt"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/arithm"
{.pop.} # stack trace
{.pop.} # stack trace
when hostOS != "standalone" and not defined(NimrodVM):
include "system/dyncalls"
when not defined(NimrodVM):
include "system/sets"
include "system/mmdisp"
{.push stack_trace: off, profiler:off.}
when hostOS != "standalone": include "system/sysstr"
{.pop.}
const
GenericSeqSize = (2 * sizeof(int))
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
of 4: d = int(cast[ptr int32](a +% n.offset)[])
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
return d
include "system/sysio"
when hasThreadSupport:
include "system/channels"
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
else:
result = n.sons[n.len]
include "system/mmdisp"
{.push stack_trace: off, profiler:off.}
when hostOS != "standalone": include "system/sysstr"
{.pop.}
include "system/sysio"
when hasThreadSupport:
include "system/channels"
else:
include "system/sysio"
iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
## Iterate over any line in the file named `filename`.
@ -2053,7 +2105,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
var res = TaintedString(newStringOfCap(80))
while f.readLine(res): yield TaintedString(res)
when hostOS != "standalone":
when hostOS != "standalone" and not defined(NimrodVM):
include "system/assign"
include "system/repr"
@ -2078,43 +2130,46 @@ when not defined(EcmaScript) and not defined(NimrodVM):
excHandler.raiseAction = action
{.push stack_trace: off, profiler:off.}
when defined(endb):
when defined(endb) and not defined(NimrodVM):
include "system/debugger"
when defined(profiler) or defined(memProfiler):
include "system/profiler"
{.pop.} # stacktrace
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
## can be used to mark a condition to be likely. This is a hint for the
## optimizer.
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
## can be used to mark a condition to be unlikely. This is a hint for the
## optimizer.
when not defined(NimrodVM):
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
## can be used to mark a condition to be likely. This is a hint for the
## optimizer.
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw proc pointer of the closure `x`. This is
## useful for interfacing closures with C.
{.emit: """
`result` = `x`.ClPrc;
""".}
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
## can be used to mark a condition to be unlikely. This is a hint for the
## optimizer.
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw proc pointer of the closure `x`. This is
## useful for interfacing closures with C.
{.emit: """
`result` = `x`.ClPrc;
""".}
proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw environment pointer of the closure `x`. This is
## useful for interfacing closures with C.
{.emit: """
`result` = `x`.ClEnv;
""".}
proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
## retrieves the raw environment pointer of the closure `x`. This is
## useful for interfacing closures with C.
{.emit: """
`result` = `x`.ClEnv;
""".}
proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
## can be used to determine if a first class iterator has finished.
{.emit: """
`result` = *((NI*) `x`.ClEnv) < 0;
""".}
proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
## can be used to determine if a first class iterator has finished.
{.emit: """
`result` = *((NI*) `x`.ClEnv) < 0;
""".}
elif defined(ecmaScript) or defined(NimrodVM):
elif defined(ecmaScript):
# Stubs:
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
proc GC_disable() = nil
proc GC_enable() = nil
proc GC_fullCollect() = nil
@ -2145,6 +2200,10 @@ elif defined(ecmaScript) or defined(NimrodVM):
if x == y: return 0
if x < y: return -1
return 1
when defined(nimffi):
include "system/sysio"
proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
## a shorthand for ``echo(errormsg); quit(errorcode)``.

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -21,9 +21,8 @@ proc c_strlen(a: CString): int {.nodecl, noSideEffect, importc: "strlen".}
proc c_memset(p: pointer, value: cint, size: int) {.nodecl, importc: "memset".}
type
C_TextFile {.importc: "FILE", nodecl, final.} = object # empty record for
# data hiding
C_BinaryFile {.importc: "FILE", nodecl, final.} = object
C_TextFile {.importc: "FILE", nodecl, final, incompleteStruct.} = object
C_BinaryFile {.importc: "FILE", nodecl, final, incompleteStruct.} = object
C_TextFileStar = ptr CTextFile
C_BinaryFileStar = ptr CBinaryFile

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -579,6 +579,10 @@ proc hash(Data: Pointer, Size: int): THash =
Dec(s)
result = !$h
proc hashGcHeader(data: pointer): THash =
const headerSize = sizeof(int)*2
result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
h: THash): THash
proc genericHashAux(dest: Pointer, n: ptr TNimNode, shallow: bool,
@ -606,20 +610,22 @@ proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
result = h
if x != nil:
let s = cast[NimString](x)
when true:
result = result !& hash(x, s.len)
when defined(trackGcHeaders):
result = result !& hashGcHeader(x)
else:
let y = cast[pointer](cast[int](x) -% 2*sizeof(int))
result = result !& hash(y, s.len + 2*sizeof(int))
result = result !& hash(x, s.len)
of tySequence:
var x = cast[ppointer](dest)
var dst = cast[taddress](cast[ppointer](dest)[])
result = h
if dst != 0:
for i in 0..cast[pgenericseq](dst).len-1:
result = result !& genericHashAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
mt.Base, shallow, result)
when defined(trackGcHeaders):
result = result !& hashGcHeader(cast[ppointer](dest)[])
else:
for i in 0..cast[pgenericseq](dst).len-1:
result = result !& genericHashAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
mt.Base, shallow, result)
of tyObject, tyTuple:
# we don't need to copy m_type field for tyObject, as they are equal anyway
result = genericHashAux(dest, mt.node, shallow, h)
@ -630,13 +636,18 @@ proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size),
mt.base, shallow, result)
of tyRef:
if shallow:
result = h !& hash(dest, mt.size)
else:
result = h
when defined(trackGcHeaders):
var s = cast[ppointer](dest)[]
if s != nil:
result = result !& genericHashAux(s, mt.base, shallow, result)
result = result !& hashGcHeader(s)
else:
if shallow:
result = h !& hash(dest, mt.size)
else:
result = h
var s = cast[ppointer](dest)[]
if s != nil:
result = result !& genericHashAux(s, mt.base, shallow, result)
# hash the object header:
#const headerSize = sizeof(int)*2
#result = result !& hash(cast[pointer](cast[int](s) -% headerSize),

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -16596,6 +16596,9 @@ proc message_dialog_new*(parent: PWindow, flags: TDialogFlags,
thetype: TMessageType, buttons: TButtonsType,
message_format: cstring): PMessageDialog{.varargs,
cdecl, importc: "gtk_message_dialog_new", dynlib: lib.}
proc set_markup*(msgDialog: PMessageDialog, str: cstring) {.cdecl,
importc: "gtk_message_dialog_set_markup", dynlib: lib.}
proc signal_new*(name: cstring, signal_flags: TSignalRunType,
object_type: TType, function_offset: guint,
marshaller: TSignalMarshaller, return_val: TType, n_args: guint): guint{.
@ -16893,6 +16896,15 @@ type
proc set_tooltip_text*(w: PWidget, t: cstring){.cdecl,
dynlib: lib, importc: "gtk_widget_set_tooltip_text".}
proc get_tooltip_text*(w: PWidget): cstring{.cdecl,
dynlib: lib, importc: "gtk_widget_get_tooltip_text".}
proc set_tooltip_markup*(w: PWidget, m: cstring) {.cdecl, dynlib: lib,
importc: "gtk_widget_set_tooltip_markup".}
proc get_tooltip_markup*(w: PWidget): cstring {.cdecl, dynlib: lib,
importc: "gtk_widget_get_tooltip_markup".}
proc set_tooltip_column*(w: PTreeview, column: gint){.cdecl,
dynlib: lib, importc: "gtk_tree_view_set_tooltip_column".}
@ -16905,6 +16917,9 @@ proc trigger_tooltip_query*(widg: PTooltip){.cdecl, dynlib: lib,
proc set_has_tooltip*(widget: PWidget, b: gboolean){.cdecl, dynlib: lib,
importc: "gtk_widget_set_has_tooltip".}
proc get_has_tooltip*(widget: PWidget): gboolean{.cdecl, dynlib: lib,
importc: "gtk_widget_get_has_tooltip".}
proc set_markup*(tp: PTooltip, mk: cstring){.cdecl, dynlib: lib,
importc: "gtk_tooltip_set_markup".}
@ -17035,6 +17050,10 @@ proc remove*(combo_box: PComboBoxText; position: gint){.cdecl,
importc: "gtk_combo_box_text_remove", dynlib: lib.}
proc get_active_text*(combo_box: PComboBoxText): cstring{.cdecl,
importc: "gtk_combo_box_text_get_active_text", dynlib: lib.}
proc is_active*(win: PWindow): gboolean{.cdecl,
importc: "gtk_window_is_active", dynlib: lib.}
proc has_toplevel_focus*(win: PWindow): gboolean{.cdecl,
importc: "gtk_window_has_toplevel_focus", dynlib: lib.}
proc nimrod_init*() =
var

149
lib/wrappers/libffi.nim Normal file
View file

@ -0,0 +1,149 @@
# -----------------------------------------------------------------*-C-*-
# libffi 3.0.10 - Copyright (c) 2011 Anthony Green
# - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the ``Software''), to deal in the Software without
# restriction, including without limitation the rights to use, copy,
# modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# -----------------------------------------------------------------------
{.deadCodeElim: on.}
when defined(windows):
const libffidll* = "libffi.dll"
elif defined(macosx):
const libffidll* = "libffi.dylib"
else:
const libffidll* = "libffi.so"
type
TArg* = int
TSArg* = int
when defined(windows) and defined(x86):
type
TABI* {.size: sizeof(cint).} = enum
FIRST_ABI, SYSV, STDCALL
const DEFAULT_ABI* = SYSV
elif defined(amd64) and defined(windows):
type
TABI* {.size: sizeof(cint).} = enum
FIRST_ABI, WIN64
const DEFAULT_ABI* = WIN64
else:
type
TABI* {.size: sizeof(cint).} = enum
FIRST_ABI, SYSV, UNIX64
when defined(i386):
const DEFAULT_ABI* = SYSV
else:
const DEFAULT_ABI* = UNIX64
const
tkVOID* = 0
tkINT* = 1
tkFLOAT* = 2
tkDOUBLE* = 3
tkLONGDOUBLE* = 4
tkUINT8* = 5
tkSINT8* = 6
tkUINT16* = 7
tkSINT16* = 8
tkUINT32* = 9
tkSINT32* = 10
tkUINT64* = 11
tkSINT64* = 12
tkSTRUCT* = 13
tkPOINTER* = 14
tkLAST = tkPOINTER
tkSMALL_STRUCT_1B* = (tkLAST + 1)
tkSMALL_STRUCT_2B* = (tkLAST + 2)
tkSMALL_STRUCT_4B* = (tkLAST + 3)
type
TType* = object
size*: int
alignment*: uint16
typ*: uint16
elements*: ptr ptr TType
var
type_void* {.importc: "ffi_type_void", dynlib: libffidll.}: TType
type_uint8* {.importc: "ffi_type_uint8", dynlib: libffidll.}: TType
type_sint8* {.importc: "ffi_type_sint8", dynlib: libffidll.}: TType
type_uint16* {.importc: "ffi_type_uint16", dynlib: libffidll.}: TType
type_sint16* {.importc: "ffi_type_sint16", dynlib: libffidll.}: TType
type_uint32* {.importc: "ffi_type_uint32", dynlib: libffidll.}: TType
type_sint32* {.importc: "ffi_type_sint32", dynlib: libffidll.}: TType
type_uint64* {.importc: "ffi_type_uint64", dynlib: libffidll.}: TType
type_sint64* {.importc: "ffi_type_sint64", dynlib: libffidll.}: TType
type_float* {.importc: "ffi_type_float", dynlib: libffidll.}: TType
type_double* {.importc: "ffi_type_double", dynlib: libffidll.}: TType
type_pointer* {.importc: "ffi_type_pointer", dynlib: libffidll.}: TType
type_longdouble* {.importc: "ffi_type_longdouble", dynlib: libffidll.}: TType
type
Tstatus* {.size: sizeof(cint).} = enum
OK, BAD_TYPEDEF, BAD_ABI
TTypeKind* = cuint
TCif* {.pure, final.} = object
abi*: TABI
nargs*: cuint
arg_types*: ptr ptr TType
rtype*: ptr TType
bytes*: cuint
flags*: cuint
type
TRaw* = object
sint*: TSArg
proc raw_call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
avalue: ptr TRaw) {.cdecl, importc: "ffi_raw_call",
dynlib: libffidll.}
proc ptrarray_to_raw*(cif: var Tcif; args: ptr pointer; raw: ptr TRaw) {.cdecl,
importc: "ffi_ptrarray_to_raw", dynlib: libffidll.}
proc raw_to_ptrarray*(cif: var Tcif; raw: ptr TRaw; args: ptr pointer) {.cdecl,
importc: "ffi_raw_to_ptrarray", dynlib: libffidll.}
proc raw_size*(cif: var Tcif): int {.cdecl, importc: "ffi_raw_size",
dynlib: libffidll.}
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
atypes: ptr ptr TType): TStatus {.cdecl, importc: "ffi_prep_cif",
dynlib: libffidll.}
proc call*(cif: var Tcif; fn: proc () {.cdecl.}; rvalue: pointer;
avalue: ptr pointer) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
# the same with an easier interface:
type
TParamList* = array[0..100, ptr TType]
TArgList* = array[0..100, pointer]
proc prep_cif*(cif: var Tcif; abi: TABI; nargs: cuint; rtype: ptr TType;
atypes: TParamList): TStatus {.cdecl, importc: "ffi_prep_cif",
dynlib: libffidll.}
proc call*(cif: var Tcif; fn, rvalue: pointer;
avalue: TArgList) {.cdecl, importc: "ffi_call", dynlib: libffidll.}
# Useful for eliminating compiler warnings
##define FFI_FN(f) ((void (*)(void))f)

View file

@ -35,19 +35,32 @@
#** In french or in english
#
when defined(MACOSX):
const
NAME* = "liblua(|5.2|5.1|5.0).dylib"
LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib"
elif defined(UNIX):
const
NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
else:
const
NAME* = "lua(|5.2|5.1|5.0).dll"
LIB_NAME* = "lua(|5.2|5.1|5.0).dll"
when defined(useLuajit):
when defined(MACOSX):
const
NAME* = "libluajit.dylib"
LIB_NAME* = "libluajit.dylib"
elif defined(UNIX):
const
NAME* = "libluajit.so(|.0)"
LIB_NAME* = "libluajit.so(|.0)"
else:
const
NAME* = "luajit.dll"
LIB_NAME* = "luajit.dll"
else:
when defined(MACOSX):
const
NAME* = "liblua(|5.2|5.1|5.0).dylib"
LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib"
elif defined(UNIX):
const
NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
else:
const
NAME* = "lua(|5.2|5.1|5.0).dll"
LIB_NAME* = "lua(|5.2|5.1|5.0).dll"
const
VERSION* = "Lua 5.1"

View file

@ -39,6 +39,8 @@
## OpenSSL support
{.deadCodeElim: on.}
when defined(WINDOWS):
const
DLLSSLName = "(ssleay32|libssl32).dll"

View file

@ -128,19 +128,19 @@
#
#******************************************************************************
import
import
sdl
when defined(windows):
const
when defined(windows):
const
ImageLibName = "SDL_Image.dll"
elif defined(macosx):
const
elif defined(macosx):
const
ImageLibName = "libSDL_image-1.2.0.dylib"
else:
const
ImageLibName = "libSDL_image.so"
const
else:
const
ImageLibName = "libSDL_image(.so|-1.2.so.0)"
const
IMAGE_MAJOR_VERSION* = 1
IMAGE_MINOR_VERSION* = 2
IMAGE_PATCHLEVEL* = 5