summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-07-31 12:59:22 -0700
committerAraq <rumpf_a@web.de>2013-07-31 12:59:22 -0700
commit3ae6043e7e77eef8603cab77dda0a5016f0d87fd (patch)
treebd6e540f8a43d60b51c9abf3f0fc41fde198846c
parent3ab4b3d58f3c32eb58c133411bbbf4579534e30a (diff)
parent55bcd0ba1eba91211a7b042e3a10311d5a833803 (diff)
downloadNim-3ae6043e7e77eef8603cab77dda0a5016f0d87fd.tar.gz
Merge pull request #533 from gradha/pr_helper_procs
Adds some helper procs
-rw-r--r--lib/pure/os.nim52
-rw-r--r--lib/pure/xmltree.nim39
2 files changed, 87 insertions, 4 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index bd5e83432..d57331eae 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -836,9 +836,11 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
   ##
   ## 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.
+  ## to use getFilePermissions and setFilePermissions to copy them by hand (or
+  ## use the convenience copyFileWithPermissions() proc), otherwise `dest` will
+  ## inherit the default permissions of a newly created file for the user. If
+  ## `dest` already exists, the file attributes will be preserved and the
+  ## content overwritten.
   when defined(Windows):
     when useWinUnicode:
       let s = newWideCString(source)
@@ -1383,6 +1385,26 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
       var res2 = SetFileAttributesA(filename, res)
     if res2 == - 1'i32: OSError(OSLastError())
   
+proc copyFileWithPermissions*(source, dest: string,
+                              ignorePermissionErrors = true) =
+  ## Copies a file from `source` to `dest` preserving file permissions.
+  ##
+  ## This is a wrapper proc around copyFile, getFilePermissions and
+  ## setFilePermissions on non Windows platform. On windows this proc is just a
+  ## wrapper for copyFile since that proc already copies attributes.
+  ##
+  ## On non windows systems permissions are copied after the file itself has
+  ## been copied, which won't happen atomically and could lead to a race
+  ## condition. If ignorePermissionErrors is true, errors while reading/setting
+  ## file attributes will be ignored, otherwise will raise `OSError`.
+  copyFile(source, dest)
+  when not defined(Windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source))
+    except:
+      if not ignorePermissionErrors:
+        raise
+
 proc inclFilePermissions*(filename: string, 
                           permissions: set[TFilePermission]) {.
   rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
@@ -1403,6 +1425,9 @@ proc exclFilePermissions*(filename: string,
 
 proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [FReadEnv].} =
   ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the expandTilde proc for the convenience of
+  ## processing paths coming from user configuration files.
   when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
   else: return string(getEnv("HOME")) & "/"
 
@@ -1580,5 +1605,26 @@ proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
     if ExistsFile(x): return x
   result = ""
 
+proc expandTilde*(path: string): string =
+  ## Expands a path starting with ``~/`` to a full path.
+  ##
+  ## If `path` starts with the tilde character and is followed by `/` or `\\`
+  ## this proc will return the reminder of the path appended to the result of
+  ## the getHomeDir() proc, otherwise the input path will be returned without
+  ## modification.
+  ##
+  ## The behaviour of this proc is the same on the Windows platform despite not
+  ## having this convention. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   let configFile = expandTilde("~" / "appname.cfg")
+  ##   echo configFile
+  ##   # --> C:\Users\amber\appname.cfg
+
+  if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
+    result = getHomeDir() / path[2..len(path)-1]
+  else:
+    result = path
+
 {.pop.}
 
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index d5821d8f3..7621094f3 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -24,7 +24,7 @@ type
   PXmlAttributes* = PStringTable ## an alias for a string to string mapping
   
   TXmlNode {.pure, final, acyclic.} = object 
-    case k: TXmlNodeKind
+    case k: TXmlNodeKind # private, use the kind() proc to read this field.
     of xnText, xnComment, xnCData, xnEntity: 
       fText: string
     of xnElement:
@@ -297,3 +297,40 @@ proc attr*(n: PXmlNode, name: string): string =
   assert n.kind == xnElement
   if n.attrs == nil: return ""
   return n.attrs[name]
+
+proc findAll*(n: PXmlNode, tag: string, result: var seq[PXmlNode]) =
+  ## Iterates over all the children of `n` returning those matching `tag`.
+  ##
+  ## Found nodes satisfying the condition will be appended to the `result`
+  ## sequence, which can't be nil or the proc will crash. Usage example:
+  ##
+  ## .. code-block:: nimrod
+  ##   var
+  ##     html: PXmlNode
+  ##     tags: seq[PXmlNode] = @[]
+  ##
+  ##   html = buildHtml()
+  ##   findAll(html, "img", tags)
+  ##   for imgTag in tags:
+  ##     process(imgTag)
+  assert isNil(result) == false
+  assert n.k == xnElement
+  for child in n.items():
+    if child.k != xnElement:
+      continue
+    if child.tag == tag:
+      result.add(child)
+    elif child.k == xnElement:
+      child.findAll(tag, result)
+
+proc findAll*(n: PXmlNode, tag: string): seq[PXmlNode] =
+  ## Shortcut version to assign in let blocks. Example:
+  ##
+  ## .. code-block:: nimrod
+  ##   var html: PXmlNode
+  ##
+  ##   html = buildHtml(html)
+  ##   for imgTag in html.findAll("img"):
+  ##     process(imgTag)
+  newSeq(result, 0)
+  findAll(n, tag, result)