summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/impure/db_mysql.nim27
-rw-r--r--lib/impure/db_postgres.nim21
-rw-r--r--lib/impure/db_sqlite.nim24
-rw-r--r--lib/pure/httpclient.nim6
-rw-r--r--lib/pure/pegs.nim1
-rw-r--r--lib/pure/streams.nim2
-rw-r--r--lib/pure/terminal.nim103
-rw-r--r--lib/pure/unicode.nim10
-rw-r--r--lib/system.nim3
9 files changed, 165 insertions, 32 deletions
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index 619c2a656..7c2901efd 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -16,6 +16,9 @@ type
   DbConn* = PMySQL    ## encapsulates a database connection
   Row* = seq[string]   ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = tuple[row: cstringArray, len: int]  ## a handle that can be
+                                                    ## used to get a row's
+                                                    ## column text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
 
   SqlQuery* = distinct string ## an SQL query string
@@ -127,6 +130,30 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
       yield result
     properFreeResult(sqlres, row)
 
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within interator body.
+  rawExec(db, query, args)
+  var sqlres = mysql.useResult(db)
+  if sqlres != nil:
+    let L = int(mysql.numFields(sqlres))
+    var row: cstringArray
+    while true:
+      row = mysql.fetchRow(sqlres)
+      if row == nil: break
+      yield (row: row, len: L)
+    properFreeResult(sqlres, row)
+
+proc `[]`*(row: InstantRow, col: int): string {.inline.} =
+  ## returns text for given column of the row
+  $row.row[col]
+
+proc len*(row: InstantRow): int {.inline.} =
+  ## returns number of columns in the row
+  row.len
+
 proc getRow*(db: DbConn, query: SqlQuery,
              args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 774cb1510..c88e660f4 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -16,6 +16,9 @@ type
   DbConn* = PPGconn   ## encapsulates a database connection
   Row* = seq[string]  ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = tuple[res: PPGresult, line: int32]  ## a handle that can be
+                                                    ## used to get a row's
+                                                    ## column text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
   
   SqlQuery* = distinct string ## an SQL query string
@@ -159,6 +162,24 @@ iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
     yield result
   pqClear(res)
 
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within interator body.
+  var res = setupQuery(db, query, args)
+  for i in 0..pqNtuples(res)-1:
+    yield (res: res, line: i)
+  pqClear(res)
+
+proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
+  ## returns text for given column of the row
+  $pqgetvalue(row.res, row.line, col)
+
+proc len*(row: InstantRow): int32 {.inline.} =
+  ## returns number of columns in the row
+  pqNfields(row.res)
+
 proc getRow*(db: DbConn, query: SqlQuery,
              args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 47e7c1900..1a037becc 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -16,6 +16,8 @@ type
   DbConn* = PSqlite3  ## encapsulates a database connection
   Row* = seq[string]  ## a row of a dataset. NULL database values will be
                        ## transformed always to the empty string.
+  InstantRow* = Pstmt  ## a handle that can be used to get a row's column
+                       ## text on demand
   EDb* = object of IOError ## exception that is raised if a database error occurs
   
   SqlQuery* = distinct string ## an SQL query string
@@ -109,6 +111,24 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
     yield result
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
+iterator instantRows*(db: DbConn, query: SqlQuery,
+                      args: varargs[string, `$`]): InstantRow
+                      {.tags: [FReadDb].} =
+  ## same as fastRows but returns a handle that can be used to get column text
+  ## on demand using []. Returned handle is valid only within interator body.
+  var stmt = setupQuery(db, query, args)
+  while step(stmt) == SQLITE_ROW:
+    yield stmt
+  if finalize(stmt) != SQLITE_OK: dbError(db)
+
+proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
+  ## returns text for given column of the row
+  $column_text(row, col)
+
+proc len*(row: InstantRow): int32 {.inline.} =
+  ## returns number of columns in the row
+  column_count(row)
+
 proc getRow*(db: DbConn, query: SqlQuery,
              args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
   ## retrieves a single row. If the query doesn't return any rows, this proc
@@ -216,5 +236,7 @@ when not defined(testing) and isMainModule:
   #db.query("insert into tbl1 values('goodbye', 20)")
   for r in db.rows(sql"select * from tbl1", []):
     echo(r[0], r[1])
-  
+  for r in db.instantRows(sql"select * from tbl1", []):
+    echo(r[0], r[1])
+
   db_sqlite.close(db)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index e083d44ea..6a2913713 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -569,7 +569,7 @@ proc downloadFile*(url: string, outputFilename: string,
     fileError("Unable to open file")
 
 proc generateHeaders(r: Uri, httpMethod: string,
-                     headers: StringTableRef): string =
+                     headers: StringTableRef, body: string): string =
   # TODO: Use this in the blocking HttpClient once it supports proxies.
   result = substr(httpMethod, len("http"))
   # TODO: Proxies
@@ -582,6 +582,8 @@ proc generateHeaders(r: Uri, httpMethod: string,
 
   add(result, "Host: " & r.hostname & "\c\L")
   add(result, "Connection: Keep-Alive\c\L")
+  if body.len > 0 and not headers.hasKey("Content-Length"):
+    add(result, "Content-Length: " & $body.len & "\c\L")
   for key, val in headers:
     add(result, key & ": " & val & "\c\L")
 
@@ -786,7 +788,7 @@ proc request*(client: AsyncHttpClient, url: string, httpMethod: string,
   if not client.headers.hasKey("user-agent") and client.userAgent != "":
     client.headers["User-Agent"] = client.userAgent
 
-  var headers = generateHeaders(r, $httpMethod, client.headers)
+  var headers = generateHeaders(r, $httpMethod, client.headers, body)
 
   await client.socket.send(headers)
   if body != "":
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index ce837d9d1..e165d7dca 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -1060,7 +1060,6 @@ type
     lineStart: int            ## index of last line start in buffer
     colOffset: int            ## column to add
     filename: string
-{.deprecated: [TTokKind: TokKind, TToken: Token, TModifier: Modifier].}
 
 const
   tokKindToStr: array[TokKind, string] = [
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index e6004b5d0..50b5c219a 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -279,7 +279,7 @@ proc ssAtEnd(s: Stream): bool =
 
 proc ssSetPosition(s: Stream, pos: int) =
   var s = StringStream(s)
-  s.pos = clamp(pos, 0, s.data.high)
+  s.pos = clamp(pos, 0, s.data.len)
 
 proc ssGetPosition(s: Stream): int =
   var s = StringStream(s)
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 1e9c40f06..15e2eefec 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -17,28 +17,87 @@
 import macros
 
 when defined(windows):
-  import windows, os
+  import winlean, os
+
+  const
+    DUPLICATE_SAME_ACCESS = 2
+    FOREGROUND_BLUE = 1
+    FOREGROUND_GREEN = 2
+    FOREGROUND_RED = 4
+    FOREGROUND_INTENSITY = 8
+    BACKGROUND_BLUE = 16
+    BACKGROUND_GREEN = 32
+    BACKGROUND_RED = 64
+    BACKGROUND_INTENSITY = 128
+
+  type
+    SHORT = int16
+    COORD = object
+      X: SHORT
+      Y: SHORT
+
+    SMALL_RECT = object
+      Left: SHORT
+      Top: SHORT
+      Right: SHORT
+      Bottom: SHORT
+
+    CONSOLE_SCREEN_BUFFER_INFO = object
+      dwSize: COORD
+      dwCursorPosition: COORD
+      wAttributes: int16
+      srWindow: SMALL_RECT
+      dwMaximumWindowSize: COORD
+
+  proc duplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE,
+                       hTargetProcessHandle: HANDLE, lpTargetHandle: ptr HANDLE,
+                       dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
+                       dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32",
+      importc: "DuplicateHandle".}
+  proc getCurrentProcess(): HANDLE{.stdcall, dynlib: "kernel32",
+                                     importc: "GetCurrentProcess".}
+  proc getConsoleScreenBufferInfo(hConsoleOutput: HANDLE,
+    lpConsoleScreenBufferInfo: ptr CONSOLE_SCREEN_BUFFER_INFO): WINBOOL{.stdcall,
+    dynlib: "kernel32", importc: "GetConsoleScreenBufferInfo".}
+
+  proc setConsoleCursorPosition(hConsoleOutput: HANDLE,
+                                dwCursorPosition: COORD): WINBOOL{.
+      stdcall, dynlib: "kernel32", importc: "SetConsoleCursorPosition".}
+
+  proc fillConsoleOutputCharacter(hConsoleOutput: Handle, cCharacter: char,
+                                  nLength: DWORD, dwWriteCoord: Coord,
+                                  lpNumberOfCharsWritten: ptr DWORD): WINBOOL{.
+      stdcall, dynlib: "kernel32", importc: "FillConsoleOutputCharacterA".}
+
+  proc fillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: int16,
+                                  nLength: DWORD, dwWriteCoord: COORD,
+                                  lpNumberOfAttrsWritten: ptr DWORD): WINBOOL{.
+      stdcall, dynlib: "kernel32", importc: "FillConsoleOutputAttribute".}
+
+  proc setConsoleTextAttribute(hConsoleOutput: HANDLE,
+                               wAttributes: int16): WINBOOL{.
+      stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".}
 
   var
     conHandle: Handle
   # = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
 
   block:
-    var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
-    if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
+    var hTemp = getStdHandle(STD_OUTPUT_HANDLE)
+    if duplicateHandle(getCurrentProcess(), hTemp, getCurrentProcess(),
                        addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
       raiseOSError(osLastError())
 
   proc getCursorPos(): tuple [x,y: int] =
     var c: CONSOLESCREENBUFFERINFO
-    if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0:
+    if getConsoleScreenBufferInfo(conHandle, addr(c)) == 0:
       raiseOSError(osLastError())
     return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y))
 
   proc getAttributes(): int16 =
     var c: CONSOLESCREENBUFFERINFO
     # workaround Windows bugs: try several times
-    if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
+    if getConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
       return c.wAttributes
     return 0x70'i16 # ERROR: return white background, black text
 
@@ -67,7 +126,7 @@ proc setCursorPos*(x, y: int) =
     var c: COORD
     c.X = int16(x)
     c.Y = int16(y)
-    if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
+    if setConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
   else:
     stdout.write("\e[" & $y & ';' & $x & 'f')
 
@@ -77,11 +136,11 @@ proc setCursorXPos*(x: int) =
   when defined(windows):
     var scrbuf: CONSOLESCREENBUFFERINFO
     var hStdout = conHandle
-    if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
+    if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
     var origin = scrbuf.dwCursorPosition
     origin.X = int16(x)
-    if SetConsoleCursorPosition(conHandle, origin) == 0:
+    if setConsoleCursorPosition(conHandle, origin) == 0:
       raiseOSError(osLastError())
   else:
     stdout.write("\e[" & $x & 'G')
@@ -93,11 +152,11 @@ when defined(windows):
     when defined(windows):
       var scrbuf: CONSOLESCREENBUFFERINFO
       var hStdout = conHandle
-      if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
+      if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
         raiseOSError(osLastError())
       var origin = scrbuf.dwCursorPosition
       origin.Y = int16(y)
-      if SetConsoleCursorPosition(conHandle, origin) == 0:
+      if setConsoleCursorPosition(conHandle, origin) == 0:
         raiseOSError(osLastError())
     else:
       discard
@@ -175,18 +234,18 @@ proc eraseLine* =
     var scrbuf: CONSOLESCREENBUFFERINFO
     var numwrote: DWORD
     var hStdout = conHandle
-    if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
+    if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
     var origin = scrbuf.dwCursorPosition
     origin.X = 0'i16
-    if SetConsoleCursorPosition(conHandle, origin) == 0:
+    if setConsoleCursorPosition(conHandle, origin) == 0:
       raiseOSError(osLastError())
     var ht = scrbuf.dwSize.Y - origin.Y
     var wt = scrbuf.dwSize.X - origin.X
-    if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
+    if fillConsoleOutputCharacter(hStdout,' ', ht*wt,
                                   origin, addr(numwrote)) == 0:
       raiseOSError(osLastError())
-    if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
+    if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
                                   scrbuf.dwCursorPosition, addr(numwrote)) == 0:
       raiseOSError(osLastError())
   else:
@@ -201,14 +260,14 @@ proc eraseScreen* =
     var origin: COORD # is inititalized to 0, 0
     var hStdout = conHandle
 
-    if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
+    if getConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
     let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y)
 
-    if FillConsoleOutputCharacter(hStdout, ' ', numChars,
+    if fillConsoleOutputCharacter(hStdout, ' ', numChars,
                                   origin, addr(numwrote)) == 0:
       raiseOSError(osLastError())
-    if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, numChars,
+    if fillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, numChars,
                                   origin, addr(numwrote)) == 0:
       raiseOSError(osLastError())
     setCursorXPos(0)
@@ -219,7 +278,7 @@ proc resetAttributes* {.noconv.} =
   ## resets all attributes; it is advisable to register this as a quit proc
   ## with ``system.addQuitProc(resetAttributes)``.
   when defined(windows):
-    discard SetConsoleTextAttribute(conHandle, oldAttr)
+    discard setConsoleTextAttribute(conHandle, oldAttr)
   else:
     stdout.write("\e[0m")
 
@@ -249,7 +308,7 @@ proc setStyle*(style: set[Style]) =
     if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY)
     if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
     if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
-    discard SetConsoleTextAttribute(conHandle, a)
+    discard setConsoleTextAttribute(conHandle, a)
   else:
     for s in items(style):
       stdout.write("\e[" & $ord(s) & 'm')
@@ -260,7 +319,7 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
     var old = getAttributes()
     setStyle(style)
     stdout.write(txt)
-    discard SetConsoleTextAttribute(conHandle, old)
+    discard setConsoleTextAttribute(conHandle, old)
   else:
     setStyle(style)
     stdout.write(txt)
@@ -309,7 +368,7 @@ proc setForegroundColor*(fg: ForegroundColor, bright=false) =
       (FOREGROUND_RED or FOREGROUND_BLUE),
       (FOREGROUND_BLUE or FOREGROUND_GREEN),
       (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
-    discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg]))
+    discard setConsoleTextAttribute(conHandle, toU16(old or lookup[fg]))
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
@@ -330,7 +389,7 @@ proc setBackgroundColor*(bg: BackgroundColor, bright=false) =
       (BACKGROUND_RED or BACKGROUND_BLUE),
       (BACKGROUND_BLUE or BACKGROUND_GREEN),
       (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
-    discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg]))
+    discard setConsoleTextAttribute(conHandle, toU16(old or lookup[bg]))
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 5fd3c2418..4446eaa0c 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -372,11 +372,17 @@ const
     0xfe74]  #
 
   spaceRanges = [
-    0x0009,  0x000a,  # tab and newline
+    0x0009,  0x000d,  # tab and newline
     0x0020,  0x0020,  # space
+    0x0085,  0x0085,  # next line
     0x00a0,  0x00a0,  #
-    0x2000,  0x200b,  #  -
+    0x1680,  0x1680,  # Ogham space mark
+    0x2000,  0x200b,  # en dash .. zero-width space
+    0x200e,  0x200f,  # LTR mark .. RTL mark (pattern whitespace)
     0x2028,  0x2029,  #  -     0x3000,  0x3000,  #
+    0x202f,  0x202f,  # narrow no-break space
+    0x205f,  0x205f,  # medium mathematical space
+    0x3000,  0x3000,  # ideographic space
     0xfeff,  0xfeff]  #
 
   toupperRanges = [
diff --git a/lib/system.nim b/lib/system.nim
index 90587f306..949443dd9 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2859,9 +2859,6 @@ proc `/`*(x, y: int): float {.inline, noSideEffect.} =
   ## integer division that results in a float.
   result = toFloat(x) / toFloat(y)
 
-template `-|`*(b, s: expr): expr =
-  (if b >= 0: b else: s.len + b)
-
 template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
   # make room for additional elements or cut:
   var slen = s.len