summary refs log tree commit diff stats
path: root/lib/impure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/impure')
-rw-r--r--lib/impure/db_mysql.nim43
-rw-r--r--lib/impure/db_sqlite.nim9
-rw-r--r--lib/impure/nre.nim9
-rw-r--r--lib/impure/rdstdin.nim46
-rw-r--r--lib/impure/re.nim54
5 files changed, 65 insertions, 96 deletions
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index 1b79b3543..ca0e29d11 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -89,7 +89,7 @@ import db_common
 export db_common
 
 type
-  DbConn* = PMySQL     ## encapsulates a database connection
+  DbConn* = distinct PMySQL ## encapsulates a database connection
   Row* = seq[string]   ## a row of a dataset. NULL database values will be
                        ## converted to nil.
   InstantRow* = object ## a handle that can be used to get a row's
@@ -102,7 +102,7 @@ proc dbError*(db: DbConn) {.noreturn.} =
   ## raises a DbError exception.
   var e: ref DbError
   new(e)
-  e.msg = $mysql.error(db)
+  e.msg = $mysql.error(PMySQL db)
   raise e
 
 when false:
@@ -128,7 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   var a = 0
   for c in items(string(formatstr)):
     if c == '?':
-      if args[a] == nil:
+      if args[a].isNil:
         add(result, "NULL")
       else:
         add(result, dbQuote(args[a]))
@@ -140,17 +140,17 @@ proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
   tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
-  return mysql.realQuery(db, q, q.len) == 0'i32
+  return mysql.realQuery(PMySQL db, q, q.len) == 0'i32
 
 proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
   var q = dbFormat(query, args)
-  if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
+  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db)
 
 proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
   tags: [ReadDbEffect, WriteDbEffect].} =
   ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
-  if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
+  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db)
 
 proc newRow(L: int): Row =
   newSeq(result, L)
@@ -171,7 +171,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
   ## Breaking the fastRows() iterator during a loop will cause the next
   ## database query to raise an [EDb] exception ``Commands out of sync``.
   rawExec(db, query, args)
-  var sqlres = mysql.useResult(db)
+  var sqlres = mysql.useResult(PMySQL db)
   if sqlres != nil:
     var
       L = int(mysql.numFields(sqlres))
@@ -210,7 +210,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
   ## Same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within the iterator body.
   rawExec(db, query, args)
-  var sqlres = mysql.useResult(db)
+  var sqlres = mysql.useResult(PMySQL db)
   if sqlres != nil:
     let L = int(mysql.numFields(sqlres))
     var row: cstringArray
@@ -290,7 +290,7 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
   ## Same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within the iterator body.
   rawExec(db, query, args)
-  var sqlres = mysql.useResult(db)
+  var sqlres = mysql.useResult(PMySQL db)
   if sqlres != nil:
     let L = int(mysql.numFields(sqlres))
     setColumnInfo(columns, sqlres, L)
@@ -315,7 +315,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
   ## Retrieves a single row. If the query doesn't return any rows, this proc
   ## will return a Row with empty strings for each column.
   rawExec(db, query, args)
-  var sqlres = mysql.useResult(db)
+  var sqlres = mysql.useResult(PMySQL db)
   if sqlres != nil:
     var L = int(mysql.numFields(sqlres))
     result = newRow(L)
@@ -334,7 +334,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
   ## executes the query and returns the whole result dataset.
   result = @[]
   rawExec(db, query, args)
-  var sqlres = mysql.useResult(db)
+  var sqlres = mysql.useResult(PMySQL db)
   if sqlres != nil:
     var L = int(mysql.numFields(sqlres))
     var row: cstringArray
@@ -369,10 +369,10 @@ proc tryInsertId*(db: DbConn, query: SqlQuery,
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
-  if mysql.realQuery(db, q, q.len) != 0'i32:
+  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32:
     result = -1'i64
   else:
-    result = mysql.insertId(db)
+    result = mysql.insertId(PMySQL db)
 
 proc insertId*(db: DbConn, query: SqlQuery,
                args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
@@ -387,32 +387,33 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery,
   ## runs the query (typically "UPDATE") and returns the
   ## number of affected rows
   rawExec(db, query, args)
-  result = mysql.affectedRows(db)
+  result = mysql.affectedRows(PMySQL db)
 
 proc close*(db: DbConn) {.tags: [DbEffect].} =
   ## closes the database connection.
-  if db != nil: mysql.close(db)
+  if PMySQL(db) != nil: mysql.close(PMySQL db)
 
 proc open*(connection, user, password, database: string): DbConn {.
   tags: [DbEffect].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
-  result = mysql.init(nil)
-  if result == nil: dbError("could not open database connection")
+  var res = mysql.init(nil)
+  if res == nil: dbError("could not open database connection")
   let
     colonPos = connection.find(':')
     host = if colonPos < 0: connection
            else: substr(connection, 0, colonPos-1)
     port: int32 = if colonPos < 0: 0'i32
                   else: substr(connection, colonPos+1).parseInt.int32
-  if mysql.realConnect(result, host, user, password, database,
+  if mysql.realConnect(res, host, user, password, database,
                        port, nil, 0) == nil:
-    var errmsg = $mysql.error(result)
-    db_mysql.close(result)
+    var errmsg = $mysql.error(res)
+    mysql.close(res)
     dbError(errmsg)
+  result = DbConn(res)
 
 proc setEncoding*(connection: DbConn, encoding: string): bool {.
   tags: [DbEffect].} =
   ## sets the encoding of a database connection, returns true for
   ## success, false for failure.
-  result = mysql.set_character_set(connection, encoding) == 0
+  result = mysql.set_character_set(PMySQL connection, encoding) == 0
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 21049571f..fd25b2b94 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -31,7 +31,7 @@
 ##
 ## .. code-block:: Nim
 ##     import db_sqlite
-##     let db = open("localhost", "user", "password", "dbname")
+##     let db = open("mytest.db", nil, nil, nil)  # user, password, database name can be nil
 ##     db.close()
 ##
 ## Creating a table
@@ -57,7 +57,7 @@
 ##
 ##  import db_sqlite, math
 ##
-##  let theDb = open("mytest.db", nil, nil, nil)
+##  let theDb = open("mytest.db", "", "", "")
 ##
 ##  theDb.exec(sql"Drop table if exists myTestTbl")
 ##  theDb.exec(sql("""create table myTestTbl (
@@ -81,7 +81,7 @@
 ##
 ##  theDb.close()
 
-{.deadCodeElim:on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 import strutils, sqlite3
 
@@ -126,6 +126,7 @@ proc tryExec*(db: DbConn, query: SqlQuery,
               args: varargs[string, `$`]): bool {.
               tags: [ReadDbEffect, WriteDbEffect].} =
   ## tries to execute the query and returns true if successful, false otherwise.
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
@@ -144,6 +145,7 @@ proc newRow(L: int): Row =
 
 proc setupQuery(db: DbConn, query: SqlQuery,
                 args: varargs[string]): Pstmt =
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
 
@@ -267,6 +269,7 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
                   {.tags: [WriteDbEffect], raises: [].} =
   ## executes the query (typically "INSERT") and returns the
   ## generated ID for the row or -1 in case of an error.
+  assert(not db.isNil, "Database not connected.")
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   result = -1
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 3d4afc0ae..6058128dd 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -10,7 +10,7 @@
 from pcre import nil
 import nre.private.util
 import tables
-from strutils import toLower, `%`
+from strutils import `%`
 from math import ceil
 import options
 from unicode import runeLenAt
@@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string =
 
 proc `==`*(a, b: Regex): bool =
   if not a.isNil and not b.isNil:
-    return a.pattern   == b.pattern and
-           a.pcreObj   == b.pcreObj and
+    return a.pattern == b.pattern and
+           a.pcreObj == b.pcreObj and
            a.pcreExtra == b.pcreExtra
   else:
     return system.`==`(a, b)
 
 proc `==`*(a, b: RegexMatch): bool =
   return a.pattern == b.pattern and
-         a.str     == b.str
+         a.str == b.str
 # }}}
 
 # Creation & Destruction {{{
@@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
     let bounds = match.matchBounds
     result.add(str.substr(lastIdx, bounds.a - 1))
     let nextVal = replacement
-    assert(nextVal != nil)
     result.add(nextVal)
 
     lastIdx = bounds.b + 1
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index b06e8de6c..54bab82f0 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -13,7 +13,7 @@
 ## is used. This suffices because Windows' console already provides the
 ## wanted functionality.
 
-{.deadCodeElim: on.}
+{.deadCodeElim: on.}  # dce option deprecated
 
 when defined(Windows):
   proc readLineFromStdin*(prompt: string): TaintedString {.
@@ -73,32 +73,6 @@ when defined(Windows):
          discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
          return result
 
-  from unicode import toUTF8, Rune, runeLenAt
-
-  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
-                              bool {.tags: [ReadIOEffect, WriteIOEffect].} =
-    ## Reads a `password` from stdin without printing it. `password` must not
-    ## be ``nil``! Returns ``false`` if the end of the file has been reached,
-    ## ``true`` otherwise.
-    password.setLen(0)
-    stdout.write(prompt)
-    while true:
-      let c = getch()
-      case c.char
-      of '\r', chr(0xA):
-        break
-      of '\b':
-        # ensure we delete the whole UTF-8 character:
-        var i = 0
-        var x = 1
-        while i < password.len:
-          x = runeLenAt(password, i)
-          inc i, x
-        password.setLen(max(password.len - x, 0))
-      else:
-        password.add(toUTF8(c.Rune))
-    stdout.write "\n"
-
 else:
   import linenoise, termios
 
@@ -124,21 +98,3 @@ else:
     linenoise.free(buffer)
     result = true
 
-  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
-                              bool {.tags: [ReadIOEffect, WriteIOEffect].} =
-    password.setLen(0)
-    let fd = stdin.getFileHandle()
-    var cur, old: Termios
-    discard fd.tcgetattr(cur.addr)
-    old = cur
-    cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
-    discard fd.tcsetattr(TCSADRAIN, cur.addr)
-    stdout.write prompt
-    result = stdin.readLine(password)
-    stdout.write "\n"
-    discard fd.tcsetattr(TCSADRAIN, old.addr)
-
-proc readPasswordFromStdin*(prompt: string): TaintedString =
-  ## Reads a password from stdin without printing it.
-  result = TaintedString("")
-  discard readPasswordFromStdin(prompt, result)
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index c7f8f336b..201c490f3 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -7,18 +7,14 @@
 #    distribution, for details about the copyright.
 #
 
-## Regular expression support for Nim. This module still has some
-## obscure bugs and limitations,
-## consider using the ``nre`` or ``pegs`` modules instead.
-## We had to de-deprecate this module since too much code relies on it
-## and many people prefer its API over ``nre``'s.
+## Regular expression support for Nim.
 ##
 ## This module is implemented by providing a wrapper around the
-## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
-## C library. This means that your application will depend on the PRCE
+## `PCRE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
+## C library. This means that your application will depend on the PCRE
 ## library's licence when using this module, which should not be a problem
 ## though.
-## PRCE's licence follows:
+## PCRE's licence follows:
 ##
 ## .. include:: ../../doc/regexprs.txt
 ##
@@ -502,7 +498,7 @@ proc transformFile*(infile, outfile: string,
   var x = readFile(infile).string
   writeFile(outfile, x.multiReplace(subs))
 
-iterator split*(s: string, sep: Regex): string =
+iterator split*(s: string, sep: Regex; maxsplit = -1): string =
   ## Splits the string ``s`` into substrings.
   ##
   ## Substrings are separated by the regular expression ``sep``
@@ -524,22 +520,28 @@ iterator split*(s: string, sep: Regex): string =
   ##   "example"
   ##   ""
   ##
-  var
-    first = -1
-    last = -1
-  while last < len(s):
-    var x = matchLen(s, sep, last)
-    if x > 0: inc(last, x)
-    first = last
-    if x == 0: inc(last)
+  var last = 0
+  var splits = maxsplit
+  var x: int
+  while last <= len(s):
+    var first = last
+    var sepLen = 1
     while last < len(s):
       x = matchLen(s, sep, last)
-      if x >= 0: break
+      if x >= 0:
+        sepLen = x
+        break
       inc(last)
-    if first <= last:
-      yield substr(s, first, last-1)
-
-proc split*(s: string, sep: Regex): seq[string] {.inline.} =
+    if x == 0:
+      if last >= len(s): break
+      inc last
+    if splits == 0: last = len(s)
+    yield substr(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last, sepLen)
+
+proc split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} =
   ## Splits the string ``s`` into a seq of substrings.
   ##
   ## The portion matched by ``sep`` is not returned.
@@ -636,6 +638,14 @@ when isMainModule:
     accum.add(word)
   doAssert(accum == @["AAA", "", "BBB"])
 
+  doAssert(split("abc", re"") == @["a", "b", "c"])
+  doAssert(split("", re"") == @[])
+
+  doAssert(split("a;b;c", re";") == @["a", "b", "c"])
+  doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"])
+  doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""])
+  doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""])
+
   for x in findAll("abcdef", re"^{.}", 3):
     doAssert x == "d"
   accum = @[]