summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/browsers.nim4
-rw-r--r--lib/pure/osproc.nim73
-rw-r--r--lib/pure/strutils.nim4
3 files changed, 72 insertions, 9 deletions
diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim
index 6f5bf7ddb..b44f406c5 100644
--- a/lib/pure/browsers.nim
+++ b/lib/pure/browsers.nim
@@ -33,10 +33,10 @@ proc openDefaultBrowser*(url: string) =
     else:
       discard ShellExecuteA(0'i32, "open", url, nil, nil, SW_SHOWNORMAL)
   elif defined(macosx):
-    discard execShellCmd("open " & quoteIfContainsWhite(url))
+    discard execShellCmd("open " & quoteShell(url))
   else:
     const attempts = ["gnome-open ", "kde-open ", "xdg-open "]
-    var u = quoteIfContainsWhite(url)
+    var u = quoteShell(url)
     for a in items(attempts):
       if execShellCmd(a & u) == 0: return
     for b in getEnv("BROWSER").string.split(PathSep):
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 754e34b85..61b940ce8 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -41,6 +41,58 @@ type
     poStdErrToStdOut,    ## merge stdout and stderr to the stdout stream
     poParentStreams      ## use the parent's streams
 
+proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote s, so it can be safely passed to Windows API.
+  ## Based on Python's subprocess.list2cmdline
+  ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+  let needQuote = {' ', '\t'} in s or s.len == 0
+
+  result = ""
+  var backslashBuff = ""
+  if needQuote:
+    result.add("\"")
+
+  for c in s:
+    if c == '\\':
+      backslashBuff.add(c)
+    elif c == '\"':
+      result.add(backslashBuff)
+      result.add(backslashBuff)
+      backslashBuff.setLen(0)
+      result.add("\\\"")
+    else:
+      if backslashBuff.len != 0:
+        result.add(backslashBuff)
+        backslashBuff.setLen(0)
+      result.add(c)
+
+  if needQuote:
+    result.add("\"")
+
+proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote s, so it can be safely passed to POSIX shell.
+  ## Based on Python's pipes.quote
+  const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
+                         '0'..'9', 'A'..'Z', 'a'..'z'}
+  if s.len == 0:
+    return "''"
+
+  let safe = s.allCharsInSet(safeUnixChars)
+
+  if safe:
+    return s
+  else:
+    return "'" & s.replace("'", "'\"'\"'") & "'"
+
+proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote s, so it can be safely passed to shell.
+  when defined(Windows):
+    return quoteShellWindows(s)
+  elif defined(posix):
+    return quoteShellPosix(s)
+  else:
+    {.error:"quoteShell is not supported on your system".}
+
 proc execProcess*(command: string,
                   options: set[TProcessOption] = {poStdErrToStdOut,
                                                   poUseShell}): TaintedString {.
@@ -307,10 +359,10 @@ when defined(Windows) and not defined(useNimRtl):
     result.writeDataImpl = hsWriteData
 
   proc buildCommandLine(a: string, args: openarray[string]): cstring =
-    var res = quoteIfContainsWhite(a)
+    var res = quoteShell(a)
     for i in 0..high(args):
       res.add(' ')
-      res.add(quoteIfContainsWhite(args[i]))
+      res.add(quoteShell(args[i]))
     result = cast[cstring](alloc0(res.len+1))
     copyMem(result, cstring(res), res.len)
 
@@ -510,10 +562,10 @@ elif not defined(useNimRtl):
     writeIdx = 1
 
   proc addCmdArgs(command: string, args: openarray[string]): string =
-    result = quoteIfContainsWhite(command)
+    result = quoteShell(command)
     for i in 0 .. high(args):
       add(result, " ")
-      add(result, quoteIfContainsWhite(args[i]))
+      add(result, quoteShell(args[i]))
 
   proc toCStringArray(b, a: openarray[string]): cstringArray =
     result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
@@ -792,5 +844,14 @@ proc execCmdEx*(command: string, options: set[TProcessOption] = {
   close(p)
 
 when isMainModule:
-  var x = execProcess("gcc -v")
-  echo "ECHO ", x
+  assert quoteShellWindows("aaa") == "aaa"
+  assert quoteShellWindows("aaa\"") == "aaa\\\""
+  assert quoteShellWindows("") == "\"\""
+
+  assert quoteShellPosix("aaa") == "aaa"
+  assert quoteShellPosix("aaa a") == "'aaa a'"
+  assert quoteShellPosix("") == "''"
+  assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
+
+  when defined(posix):
+    assert quoteShell("") == "''"
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index a4aa81578..fe71cb77b 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -709,9 +709,11 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
     if result != -1: return
   return -1
 

-proc quoteIfContainsWhite*(s: string): string =

+proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
   ## returns ``'"' & s & '"'`` if `s` contains a space and does not

   ## start with a quote, else returns `s`

+  ## DEPRECATED as it was confused for shell quoting function.
+  ## For this application use osproc.quoteShell.
   if find(s, {' ', '\t'}) >= 0 and s[0] != '"':

     result = '"' & s & '"'

   else: