summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/impure/re.nim5
-rw-r--r--lib/pure/htmlparser.nim2
-rw-r--r--lib/pure/numeric.nim83
-rw-r--r--lib/pure/os.nim41
-rw-r--r--lib/pure/parseutils.nim28
-rw-r--r--lib/pure/poly.nim367
-rw-r--r--lib/pure/strutils.nim20
-rw-r--r--lib/system.nim12
-rw-r--r--lib/wrappers/zip/libzip.nim2
9 files changed, 537 insertions, 23 deletions
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index ef02a3b1d..b92d39bf0 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -201,7 +201,10 @@ proc find*(s: string, pattern: TRegEx, start = 0): int =
   return rawMatches[0]
   
 iterator findAll*(s: string, pattern: TRegEx, start = 0): string = 
-  ## yields all matching *substrings* of `s` that match `pattern`.
+  ## Yields all matching *substrings* of `s` that match `pattern`.
+  ##
+  ## Note that since this is an iterator you should not modify the string you
+  ## are iterating over: bad things could happen.
   var i = int32(start)
   var rawMatches: array[0..maxSubpatterns * 3 - 1, cint]
   while true:
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index e1a0096f6..d60d2e583 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -460,7 +460,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
       if cmpIgnoreCase(x.elemName, result.tag) == 0: 
         next(x)
       else:
-        echo "5; expected: ", result.htmltag, " ", x.elemName 
+        #echo "5; expected: ", result.htmltag, " ", x.elemName 
         errors.add(expected(x, result))
         # do not skip it here!
       break
diff --git a/lib/pure/numeric.nim b/lib/pure/numeric.nim
new file mode 100644
index 000000000..8ef5fabda
--- /dev/null
+++ b/lib/pure/numeric.nim
@@ -0,0 +1,83 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2013 Robert Persson
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+type TOneVarFunction* =proc (x:float):float
+
+proc brent*(xmin,xmax:float ,function:TOneVarFunction, tol:float,maxiter=1000): 
+  tuple[rootx, rooty: float, success: bool]=
+  ## Searches `function` for a root between `xmin` and `xmax` 
+  ## using brents method. If the function value at `xmin`and `xmax` has the
+  ## same sign, `rootx`/`rooty` is set too the extrema value closest to x-axis
+  ## and succes is set to false.
+  ## Otherwise there exists at least one root and success is set to true.
+  ## This root is searched for at most `maxiter` iterations.
+  ## If `tol` tolerance is reached within `maxiter` iterations 
+  ## the root refinement stops and success=true.
+
+  # see http://en.wikipedia.org/wiki/Brent%27s_method
+  var 
+    a=xmin
+    b=xmax
+    c=a
+    d=1.0e308 
+    fa=function(a)
+    fb=function(b)
+    fc=fa
+    s=0.0
+    fs=0.0
+    mflag:bool
+    i=0
+    tmp2:float
+
+  if fa*fb>=0:
+    if abs(fa)<abs(fb):
+      return (a,fa,false)
+    else:
+      return (b,fb,false)
+  
+  if abs(fa)<abs(fb):
+    swap(fa,fb)
+    swap(a,b)
+  
+  while fb!=0.0 and abs(a-b)>tol:
+    if fa!=fc and fb!=fc: # inverse quadratic interpolation
+      s = a * fb * fc / (fa - fb) / (fa - fc) + b * fa * fc / (fb - fa) / (fb - fc) + c * fa * fb / (fc - fa) / (fc - fb)
+    else: #secant rule
+      s = b - fb * (b - a) / (fb - fa)
+    tmp2 = (3.0 * a + b) / 4.0
+    if not((s > tmp2 and s < b) or (s < tmp2 and s > b)) or 
+      (mflag and abs(s - b) >= (abs(b - c) / 2.0)) or 
+      (not mflag and abs(s - b) >= abs(c - d) / 2.0):
+      s=(a+b)/2.0
+      mflag=true
+    else:
+      if (mflag and (abs(b - c) < tol)) or (not mflag and (abs(c - d) < tol)):
+        s=(a+b)/2.0
+        mflag=true
+      else:
+        mflag=false
+    fs = function(s)
+    d = c
+    c = b
+    fc = fb
+    if fa * fs<0.0:
+      b=s
+      fb=fs
+    else:
+      a=s
+      fa=fs
+    if abs(fa)<abs(fb):
+      swap(a,b)
+      swap(fa,fb)
+    inc i
+    if i>maxiter:
+      break
+  
+  return (b,fb,true)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index eaa22d351..bd5e83432 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -474,8 +474,17 @@ proc JoinPath*(head, tail: string): string {.
   ## .. code-block:: nimrod
   ##   "usr/lib"
   ##
-  ## If head is the empty string, tail is returned.
-  ## If tail is the empty string, head is returned.
+  ## If head is the empty string, tail is returned. If tail is the empty
+  ## string, head is returned with a trailing path separator. If tail starts
+  ## with a path separator it will be removed when concatenated to head. Other
+  ## path separators not located on boundaries won't be modified. More
+  ## examples on Unix:
+  ##
+  ## .. code-block:: nimrod
+  ##   assert JoinPath("usr", "") == "usr/"
+  ##   assert JoinPath("", "lib") == "lib"
+  ##   assert JoinPath("", "/lib") == "/lib"
+  ##   assert JoinPath("usr/", "/lib") == "usr/lib"
   if len(head) == 0:
     result = tail
   elif head[len(head)-1] in {DirSep, AltSep}:
@@ -491,15 +500,24 @@ proc JoinPath*(head, tail: string): string {.
 
 proc JoinPath*(parts: varargs[string]): string {.noSideEffect,
   rtl, extern: "nos$1OpenArray".} =
-  ## The same as `JoinPath(head, tail)`, but works with any number
-  ## of directory parts.
+  ## The same as `JoinPath(head, tail)`, but works with any number of directory
+  ## parts. You need to pass at least one element or the proc will assert in
+  ## debug builds and crash on release builds.
   result = parts[0]
   for i in 1..high(parts):
     result = JoinPath(result, parts[i])
 
 proc `/` * (head, tail: string): string {.noSideEffect.} =
-  ## The same as ``joinPath(head, tail)``
-  return joinPath(head, tail)
+  ## The same as ``JoinPath(head, tail)``
+  ##
+  ## Here are some examples for Unix:
+  ##
+  ## .. code-block:: nimrod
+  ##   assert "usr" / "" == "usr/"
+  ##   assert "" / "lib" == "lib"
+  ##   assert "" / "/lib" == "/lib"
+  ##   assert "usr/" / "/lib" == "usr/lib"
+  return JoinPath(head, tail)
 
 proc SplitPath*(path: string): tuple[head, tail: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
@@ -814,8 +832,13 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
 
 proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", 
   tags: [FReadIO, FWriteIO].} =
-  ## Copies a file from `source` to `dest`. If this fails,
-  ## `EOS` is raised.
+  ## Copies a file from `source` to `dest`.
+  ##
+  ## If this fails, `EOS` is raised. On the Windows platform this proc will
+  ## copy the source file's attributes into dest. On other platforms you need
+  ## to use getFilePermissions and setFilePermissions to copy them by hand,
+  ## otherwise `dest` will inherit the default permissions of a newly created
+  ## file for the user.
   when defined(Windows):
     when useWinUnicode:
       let s = newWideCString(source)
@@ -1498,7 +1521,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
       if len(result) > 0 and result[0] != DirSep: # not an absolute path?
         # iterate over any path in the $PATH environment variable
         for p in split(string(getEnv("PATH")), {PathSep}):
-          var x = joinPath(p, result)
+          var x = JoinPath(p, result)
           if ExistsFile(x): return x
 
 proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} =
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 1c543e666..c11265bfd 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -27,8 +27,24 @@ proc toLower(c: char): char {.inline.} =
 
 proc parseHex*(s: string, number: var int, start = 0): int {.
   rtl, extern: "npuParseHex", noSideEffect.}  = 
-  ## parses a hexadecimal number and stores its value in ``number``. Returns
-  ## the number of the parsed characters or 0 in case of an error.
+  ## Parses a hexadecimal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error. This
+  ## proc is sensitive to the already existing value of ``number`` and will
+  ## likely not do what you want unless you make sure ``number`` is zero. You
+  ## can use this feature to *chain* calls, though the result int will quickly
+  ## overflow. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   var value = 0
+  ##   discard parseHex("0x38", value)
+  ##   assert value == 56
+  ##   discard parseHex("0x34", value)
+  ##   assert value == 56 * 256 + 52
+  ##   value = -1
+  ##   discard parseHex("0x38", value)
+  ##   assert value == -200
+  ##
   var i = start
   var foundDigit = false
   if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
@@ -383,6 +399,14 @@ iterator interpolatedFragments*(s: string): tuple[kind: TInterpolatedKind,
 when isMainModule:
   for k, v in interpolatedFragments("$test{}  $this is ${an{  example}}  "):
     echo "(", k, ", \"", v, "\")"
+  var value = 0
+  discard parseHex("0x38", value)
+  assert value == 56
+  discard parseHex("0x34", value)
+  assert value == 56 * 256 + 52
+  value = -1
+  discard parseHex("0x38", value)
+  assert value == -200
 
 
 {.pop.}
diff --git a/lib/pure/poly.nim b/lib/pure/poly.nim
new file mode 100644
index 000000000..45e528604
--- /dev/null
+++ b/lib/pure/poly.nim
@@ -0,0 +1,367 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2013 Robert Persson
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import math
+import strutils
+import numeric
+
+type 
+    TPoly* = object
+        cofs:seq[float]
+
+  
+proc degree*(p:TPoly):int=
+  ## Returns the degree of the polynomial,
+  ## that is the number of coefficients-1
+  return p.cofs.len-1
+
+
+proc eval*(p:TPoly,x:float):float=
+  ## Evaluates a polynomial function value for `x`
+  ## quickly using Horners method
+  var n=p.degree
+  result=p.cofs[n]
+  dec n
+  while n>=0:
+    result = result*x+p.cofs[n]
+    dec n
+
+proc `[]` *(p:TPoly;idx:int):float=
+  ## Gets a coefficient of the polynomial.
+  ## p[2] will returns the quadric term, p[3] the cubic etc.
+  ## Out of bounds index will return 0.0.
+  if idx<0 or idx>p.degree:
+      return 0.0
+  return p.cofs[idx]
+    
+proc `[]=` *(p:var TPoly;idx:int,v:float)=
+  ## Sets an coefficient of the polynomial by index.
+  ## p[2] set the quadric term, p[3] the cubic etc.
+  ## If index is out of range for the coefficients,
+  ## the polynomial grows to the smallest needed degree.
+  assert(idx>=0)
+
+  if idx>p.degree:  #polynomial must grow
+    var oldlen=p.cofs.len
+    p.cofs.setLen(idx+1)
+    for q in oldlen.. <high(p.cofs):
+      p.cofs[q]=0.0 #new-grown coefficients set to zero
+
+  p.cofs[idx]=v
+    
+      
+iterator items*(p:TPoly):float=
+  ## Iterates through the corfficients of the polynomial.
+  var i=p.degree
+  while i>=0:
+    yield p[i]
+    dec i    
+    
+proc clean*(p:var TPoly;zerotol=0.0)=
+  ## Removes leading zero coefficients of the polynomial.
+  ## An optional tolerance can be given for what's considered zero.
+  var n=p.degree
+  var relen=false
+
+  while n>0 and abs(p[n])<=zerotol:    # >0 => keep at least one coefficient
+    dec n
+    relen=true
+
+  if relen: p.cofs.setLen(n+1)
+
+
+proc `$` *(p:TPoly):string = 
+  ## Gets a somewhat reasonable string representation of the polynomial
+  ## The format should be compatible with most online function plotters,
+  ## for example directly in google search
+  result=""
+  var first=true #might skip + sign if first coefficient
+  
+  for idx in countdown(p.degree,0):
+    let a=p[idx]
+    
+    if a==0.0:
+      continue
+    
+    if a>= 0.0 and not first:
+      result.add('+')
+    first=false
+
+    if a!=1.0 or idx==0:
+      result.add(formatFloat(a,ffDefault,0))
+    if idx>=2:
+      result.add("x^" & $idx)
+    elif idx==1:
+      result.add("x")
+
+  if result=="":
+      result="0"
+          
+
+proc derivative*(p:TPoly):TPoly=
+  ## Returns a new polynomial, which is the derivative of `p`
+  newSeq[float](result.cofs,p.degree)
+  for idx in 0..high(result.cofs):
+    result.cofs[idx]=p.cofs[idx+1]*float(idx+1)
+    
+proc diff*(p:TPoly,x:float):float=
+  ## Evaluates the differentiation of a polynomial with
+  ## respect to `x` quickly using a modifed Horners method
+  var n=p.degree
+  result=p[n]*float(n)
+  dec n
+  while n>=1:
+    result = result*x+p[n]*float(n)
+    dec n
+
+proc integral*(p:TPoly):TPoly=
+  ## Returns a new polynomial which is the indefinite
+  ## integral of `p`. The constant term is set to 0.0
+  newSeq(result.cofs,p.cofs.len+1)
+  result.cofs[0]=0.0  #constant arbitrary term, use 0.0
+  for i in 1..high(result.cofs):
+    result.cofs[i]=p.cofs[i-1]/float(i)
+        
+
+proc integrate*(p:TPoly;xmin,xmax:float):float=
+  ## Computes the definite integral of `p` between `xmin` and `xmax`
+  ## quickly using a modified version of Horners method
+  var
+    n=p.degree
+    s1=p[n]/float(n+1)
+    s2=s1
+    fac:float
+
+  dec n
+  while n>=0:
+    fac=p[n]/float(n+1)
+    s1 = s1*xmin+fac
+    s2 = s2*xmax+fac
+    dec n
+ 
+  result=s2*xmax-s1*xmin
+  
+proc initPoly*(cofs:varargs[float]):TPoly=
+  ## Initializes a polynomial with given coefficients.
+  ## The most significant coefficient is first, so to create x^2-2x+3:
+  ## intiPoly(1.0,-2.0,3.0)
+  if len(cofs)<=0:
+      result.cofs= @[0.0]  #need at least one coefficient
+  else:
+    # reverse order of coefficients so indexing matches degree of
+    # coefficient...
+    result.cofs= @[]
+    for idx in countdown(cofs.len-1,0):  
+      result.cofs.add(cofs[idx])
+
+  result.clean #remove leading zero terms
+
+
+proc divMod*(p,d:TPoly;q,r:var TPoly)=
+  ## Divides `p` with `d`, and stores the quotinent in `q` and
+  ## the remainder in `d`
+  var 
+    pdeg=p.degree
+    ddeg=d.degree
+    power=p.degree-d.degree
+    ratio:float
+  
+  r.cofs = p.cofs #initial remainder=numerator
+  if power<0: #denominator is larger than numerator
+    q.cofs= @ [0.0] #quotinent is 0.0
+    return # keep remainder as numerator
+      
+  q.cofs=newSeq[float](power+1)
+  
+  for i in countdown(pdeg,ddeg):
+    ratio=r.cofs[i]/d.cofs[ddeg]
+    
+    q.cofs[i-ddeg]=ratio
+    r.cofs[i]=0.0
+    
+    for j in countup(0,<ddeg):
+        var idx=i-ddeg+j
+        r.cofs[idx] = r.cofs[idx] - d.cofs[j]*ratio
+     
+  r.clean # drop zero coefficients in remainder
+
+proc `+` *(p1:TPoly,p2:TPoly):TPoly=
+  ## Adds two polynomials
+  var n=max(p1.cofs.len,p2.cofs.len)
+  newSeq(result.cofs,n)
+  
+  for idx in countup(0,n-1):
+      result[idx]=p1[idx]+p2[idx]
+      
+  result.clean # drop zero coefficients in remainder
+    
+proc `*` *(p1:TPoly,p2:TPoly):TPoly=
+  ## Multiplies the polynomial `p1` with `p2`
+  var 
+    d1=p1.degree
+    d2=p2.degree
+    n=d1+d2
+    idx:int
+      
+  newSeq(result.cofs,n)
+
+  for i1 in countup(0,d1):
+    for i2 in countup(0,d2):
+      idx=i1+i2
+      result[idx]=result[idx]+p1[i1]*p2[i2]
+
+  result.clean
+
+proc `*` *(p:TPoly,f:float):TPoly=
+  ## Multiplies the polynomial `p` with a real number
+  newSeq(result.cofs,p.cofs.len)
+  for i in 0..high(p.cofs):
+    result[i]=p.cofs[i]*f
+  result.clean
+  
+proc `*` *(f:float,p:TPoly):TPoly=
+  ## Multiplies a real number with a polynomial
+  return p*f
+    
+proc `-`*(p:TPoly):TPoly=
+  ## Negates a polynomial
+  result=p
+  for i in countup(0,<result.cofs.len):
+    result.cofs[i]= -result.cofs[i]
+    
+proc `-` *(p1:TPoly,p2:TPoly):TPoly=
+  ## Subtract `p1` with `p2`
+  var n=max(p1.cofs.len,p2.cofs.len)
+  newSeq(result.cofs,n)
+  
+  for idx in countup(0,n-1):
+      result[idx]=p1[idx]-p2[idx]
+      
+  result.clean # drop zero coefficients in remainder
+    
+proc `/`*(p:TPoly,f:float):TPoly=
+  ## Divides polynomial `p` with a real number `f`
+  newSeq(result.cofs,p.cofs.len)
+  for i in 0..high(p.cofs):
+    result[i]=p.cofs[i]/f
+  result.clean
+  
+proc `/` *(p,q:TPoly):TPoly=
+  ## Divides polynomial `p` with polynomial `q`
+  var dummy:TPoly
+  p.divMod(q,result,dummy)  
+
+proc `mod` *(p,q:TPoly):TPoly=
+  ## Computes the polynomial modulo operation,
+  ## that is the remainder of `p`/`q`
+  var dummy:TPoly
+  p.divMod(q,dummy,result)
+
+
+proc normalize*(p:var TPoly)=
+  ## Multiplies the polynomial inplace by a term so that
+  ## the leading term is 1.0.
+  ## This might lead to an unstable polynomial
+  ## if the leading term is zero.
+  p=p/p[p.degree]
+
+
+proc solveQuadric*(a,b,c:float;zerotol=0.0):seq[float]=
+  ## Solves the quadric equation `ax^2+bx+c`, with a possible
+  ## tolerance `zerotol` to find roots of curves just 'touching'
+  ## the x axis. Returns sequence with 0,1 or 2 solutions.
+  
+  var p,q,d:float
+  
+  p=b/(2.0*a)
+  
+  if p==inf or p==neginf: #linear equation..
+    var linrt= -c/b
+    if linrt==inf or linrt==neginf: #constant only
+      return @[]
+    return @[linrt]
+  
+  q=c/a
+  d=p*p-q
+  
+  if d<0.0:
+    #check for inside zerotol range for neg. roots
+    var err=a*p*p-b*p+c #evaluate error at parabola center axis
+    if(err<=zerotol): return @[-p]
+    return @[]
+  else:
+    var sr=sqrt(d)
+    result= @[-sr-p,sr-p]
+
+proc getRangeForRoots(p:TPoly):tuple[xmin,xmax:float]=
+  ## helper function for `roots` function
+  ## quickly computes a range, guaranteed to contain
+  ## all the real roots of the polynomial
+  # see http://www.mathsisfun.com/algebra/polynomials-bounds-zeros.html
+
+  var deg=p.degree
+  var d=p[deg]
+  var bound1,bound2:float
+  
+  for i in countup(0,deg):
+      var c=abs(p.cofs[i]/d)
+      bound1=max(bound1,c+1.0)
+      bound2=bound2+c
+      
+  bound2=max(1.0,bound2)
+  result.xmax=min(bound1,bound2)
+  result.xmin= -result.xmax
+
+
+proc addRoot(p:TPoly,res:var seq[float],xp0,xp1,tol,zerotol,mergetol:float,maxiter:int)=
+  ## helper function for `roots` function
+  ## try to do a numeric search for a single root in range xp0-xp1,
+  ## adding it to `res` (allocating `res` if nil)
+  var br=brent(xp0,xp1, proc(x:float):float=p.eval(x),tol)
+  if br.success:
+    if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
+      res.add(br.rootx) 
+  else:
+    #this might be a 'touching' case, check function value against
+    #zero tolerance
+    if abs(br.rooty)<=zerotol:
+      if res.len==0 or br.rootx>=res[high(res)]+mergetol: #dont add equal roots.
+        res.add(br.rootx) 
+
+
+proc roots*(p:TPoly,tol=1.0e-9,zerotol=1.0e-6,mergetol=1.0e-12,maxiter=1000):seq[float]=
+  ## Computes the real roots of the polynomial `p`
+  ## `tol` is the tolerance used to break searching for each root when reached.
+  ## `zerotol` is the tolerance, which is 'close enough' to zero to be considered a root
+  ## and is used to find roots for curves that only 'touch' the x-axis.
+  ## `mergetol` is the tolerance, of which two x-values are considered beeing the same root.
+  ## `maxiter` can be used to limit the number of iterations for each root.
+  ## Returns a (possibly empty) sorted sequence with the solutions.
+  var deg=p.degree
+  if deg<=0: #constant only => no roots
+    return @[]
+  elif p.degree==1: #linear
+    var linrt= -p.cofs[0]/p.cofs[1]
+    if linrt==inf or linrt==neginf:
+      return @[] #constant only => no roots
+    return @[linrt]
+  elif p.degree==2:
+    return solveQuadric(p.cofs[2],p.cofs[1],p.cofs[0],zerotol)
+  else:
+    # degree >=3 , find min/max points of polynomial with recursive
+    # derivative and do a numerical search for root between each min/max
+    var range=p.getRangeForRoots()
+    var minmax=p.derivative.roots(tol,zerotol,mergetol)
+    result= @[]
+    if minmax!=nil: #ie. we have minimas/maximas in this function
+      for x in minmax.items:
+        addRoot(p,result,range.xmin,x,tol,zerotol,mergetol,maxiter)
+        range.xmin=x
+    addRoot(p,result,range.xmin,range.xmax,tol,zerotol,mergetol,maxiter)
+
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 88f0736bb..73dd9132c 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -440,16 +440,23 @@ proc repeatStr*(count: int, s: string): string {.noSideEffect,
   result = newStringOfCap(count*s.len)

   for i in 0..count-1: result.add(s)

 

-proc align*(s: string, count: int): string {.

+proc align*(s: string, count: int, padding = ' '): string {.

   noSideEffect, rtl, extern: "nsuAlignString".} =

-  ## Aligns a string `s` with spaces, so that is of length `count`. Spaces are

-  ## added before `s` resulting in right alignment. If ``s.len >= count``, no

-  ## spaces are added and `s` is returned unchanged. If you need to left align

-  ## a string use the repeatChar proc.

+  ## Aligns a string `s` with `padding`, so that is of length `count`.
+  ## `padding` characters (by default spaces) are added before `s` resulting in
+  ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
+  ## returned unchanged. If you need to left align a string use the
+  ## ``repeatChar`` proc. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   assert align("abc", 4) == " abc"
+  ##   assert align("a", 0) == "a"
+  ##   assert align("1232", 6) == "  1232"
+  ##   assert align("1232", 6, '#') == "##1232"
   if s.len < count:

     result = newString(count)

     var spaces = count - s.len

-    for i in 0..spaces-1: result[i] = ' '

+    for i in 0..spaces-1: result[i] = padding

     for i in spaces..count-1: result[i] = s[i-spaces]

   else:

     result = s

@@ -1211,6 +1218,7 @@ when isMainModule:
   doAssert align("abc", 4) == " abc"

   doAssert align("a", 0) == "a"

   doAssert align("1232", 6) == "  1232"

+  doAssert align("1232", 6, '#') == "##1232"

   echo wordWrap(""" this is a long text --  muchlongerthan10chars and here

                    it goes""", 10, false)

   doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"

diff --git a/lib/system.nim b/lib/system.nim
index b60ccc306..6750a2d22 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2392,8 +2392,10 @@ proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) =
     spliceImpl(s, a, L, b)
   
 proc slurp*(filename: string): string {.magic: "Slurp".}
+  ## This is an alias for ``staticRead``.
+
 proc staticRead*(filename: string): string {.magic: "Slurp".}
-  ## compile-time ``readFile`` proc for easy `resource`:idx: embedding:
+  ## Compile-time ``readFile`` proc for easy `resource`:idx: embedding:
   ##
   ## .. code-block:: nimrod
   ##     const myResource = staticRead"mydatafile.bin"
@@ -2402,9 +2404,11 @@ proc staticRead*(filename: string): string {.magic: "Slurp".}
 
 proc gorge*(command: string, input = ""): string {.
   magic: "StaticExec".} = nil
+  ## This is an alias for ``staticExec``.
+
 proc staticExec*(command: string, input = ""): string {.
   magic: "StaticExec".} = nil
-  ## executes an external process at compile-time.
+  ## Executes an external process at compile-time.
   ## if `input` is not an empty string, it will be passed as a standard input
   ## to the executed program.
   ##
@@ -2412,7 +2416,9 @@ proc staticExec*(command: string, input = ""): string {.
   ##     const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & 
   ##                       "\nCompiled on " & staticExec("uname -v")
   ##
-  ## ``gorge`` is an alias for ``staticExec``.
+  ## ``gorge`` is an alias for ``staticExec``. Note that you can use this proc
+  ## inside a pragma like `passC <nimrodc.html#passc-pragma>`_ or `passL
+  ## <nimrodc.html#passl-pragma>`_.
 
 proc `+=`*[T: TOrdinal](x: var T, y: T) {.magic: "Inc", noSideEffect.}
   ## Increments an ordinal
diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim
index f25e45671..c3d1784a5 100644
--- a/lib/wrappers/zip/libzip.nim
+++ b/lib/wrappers/zip/libzip.nim
@@ -51,7 +51,7 @@ when defined(unix) and not defined(useLibzipSrc):
   when defined(macosx):
     {.pragma: mydll, dynlib: "libzip2.dylib".}
   else:
-    {.pragma: mydll, dynlib: "libzip2.so(|.2|.1|.0)".}
+    {.pragma: mydll, dynlib: "libzip(|2).so(|.2|.1|.0)".}
 else:
   when defined(unix):
     {.passl: "-lz".}