From b8072bd22ec6e3ccf1a6be1b19fae9de3a0fdcc9 Mon Sep 17 00:00:00 2001 From: Michał Zieliński Date: Tue, 10 Dec 2013 22:56:29 +0100 Subject: add quoteShell to osproc.nim --- lib/pure/osproc.nim | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) (limited to 'lib/pure/osproc.nim') diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 754e34b85..7772585a9 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 {. @@ -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("") == "''" -- cgit 1.4.1-2-gfad0 From 39cabcdd2702098825eba32fcbc6737b9aa45e6e Mon Sep 17 00:00:00 2001 From: Michał Zieliński Date: Wed, 11 Dec 2013 22:27:39 +0100 Subject: Use quoteShell in stdlib, where appropriate. --- compiler/extccomp.nim | 32 ++++++++++++++++---------------- compiler/nimrod.nim | 12 ++++++------ lib/pure/browsers.nim | 4 ++-- lib/pure/osproc.nim | 8 ++++---- tools/niminst/niminst.nim | 5 ++--- 5 files changed, 30 insertions(+), 31 deletions(-) (limited to 'lib/pure/osproc.nim') diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 13eb972f6..fe1bea3ff 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -463,9 +463,9 @@ proc getCompileOptions: string = proc getLinkOptions: string = result = linkOptions for linkedLib in items(cLinkedLibs): - result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteIfContainsWhite) + result.add(cc[ccompiler].linkLibCmd % linkedLib.quoteShell) for libDir in items(cLibs): - result.add([cc[ccompiler].linkDirCmd, libDir.quoteIfContainsWhite]) + result.add([cc[ccompiler].linkDirCmd, libDir.quoteShell]) proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or @@ -485,10 +485,10 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var includeCmd, compilePattern: string if not noAbsolutePaths(): # compute include paths: - includeCmd = cc[c].includeCmd & quoteIfContainsWhite(libpath) + includeCmd = cc[c].includeCmd & quoteShell(libpath) for includeDir in items(cIncludes): - includeCmd.add([cc[c].includeCmd, includeDir.quoteIfContainsWhite]) + includeCmd.add([cc[c].includeCmd, includeDir.quoteShell]) compilePattern = JoinPath(ccompilerpath, exe) else: @@ -501,17 +501,17 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = toObjFile(cfile) else: completeCFilePath(toObjFile(cfile)) - cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt)) - objfile = quoteIfContainsWhite(objfile) - result = quoteIfContainsWhite(compilePattern % [ + cfile = quoteShell(AddFileExt(cfile, cExt)) + objfile = quoteShell(objfile) + result = quoteShell(compilePattern % [ "file", cfile, "objfile", objfile, "options", options, "include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath]) add(result, ' ') addf(result, cc[c].compileTmpl, [ "file", cfile, "objfile", objfile, "options", options, "include", includeCmd, - "nimrod", quoteIfContainsWhite(getPrefixDir()), - "lib", quoteIfContainsWhite(libpath)]) + "nimrod", quoteShell(getPrefixDir()), + "lib", quoteShell(libpath)]) proc footprint(filename: string): TCrc32 = result = crcFromFile(filename) >< @@ -590,7 +590,7 @@ proc CallCCompiler*(projectfile: string) = while it != nil: let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data add(objfiles, ' ') - add(objfiles, quoteIfContainsWhite( + add(objfiles, quoteShell( addFileExt(objFile, cc[ccompiler].objExt))) it = PStrEntry(it.next) @@ -602,8 +602,8 @@ proc CallCCompiler*(projectfile: string) = var linkerExe = getConfigVar(c, ".linkerexe") if len(linkerExe) == 0: linkerExe = cc[c].linkerExe if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") - if noAbsolutePaths(): linkCmd = quoteIfContainsWhite(linkerExe) - else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe)) + if noAbsolutePaths(): linkCmd = quoteShell(linkerExe) + else: linkCmd = quoteShell(JoinPath(ccompilerpath, linkerExe)) if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui else: buildGui = "" var exefile: string @@ -617,17 +617,17 @@ proc CallCCompiler*(projectfile: string) = exefile = options.outFile if not noAbsolutePaths(): exefile = joinPath(splitFile(projectFile).dir, exefile) - exefile = quoteIfContainsWhite(exefile) + exefile = quoteShell(exefile) let linkOptions = getLinkOptions() - linkCmd = quoteIfContainsWhite(linkCmd % ["builddll", builddll, + linkCmd = quoteShell(linkCmd % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]) linkCmd.add ' ' addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, - "nimrod", quoteIfContainsWhite(getPrefixDir()), - "lib", quoteIfContainsWhite(libpath)]) + "nimrod", quoteShell(getPrefixDir()), + "lib", quoteShell(libpath)]) if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) else: linkCmd = "" diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index 2f10e32e3..8e3c0e61e 100644 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -13,9 +13,9 @@ when defined(gcc) and defined(windows): else: {.link: "icons/nimrod_icon.o".} -import - commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, - extccomp, strutils, os, platform, main, parseopt, service +import + commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, + extccomp, strutils, os, osproc, platform, main, parseopt, service when hasTinyCBackend: import tccgen @@ -23,7 +23,7 @@ when hasTinyCBackend: when defined(profiler) or defined(memProfiler): {.hint: "Profiling support is turned on!".} import nimprof - + proc prependCurDir(f: string): string = when defined(unix): if os.isAbsolute(f): result = f @@ -61,11 +61,11 @@ proc HandleCmdLine() = tccgen.run() if optRun in gGlobalOptions: if gCmd == cmdCompileToJS: - var ex = quoteIfContainsWhite( + var ex = quoteShell( completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) execExternalProgram("node " & ex & ' ' & service.arguments) else: - var ex = quoteIfContainsWhite( + var ex = quoteShell( changeFileExt(gProjectFull, exeExt).prependCurDir) execExternalProgram(ex & ' ' & service.arguments) 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 7772585a9..61b940ce8 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -359,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) @@ -562,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))) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 25ec0d283..0c9717e12 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -14,7 +14,7 @@ when haveZipLib: import zipfiles import - os, strutils, parseopt, parsecfg, strtabs, streams, debcreation + os, osproc, strutils, parseopt, parsecfg, strtabs, streams, debcreation const maxOS = 20 # max number of OSes @@ -486,7 +486,7 @@ proc setupDist(c: var TConfigData) = if c.innoSetup.path.len == 0: c.innoSetup.path = "iscc.exe" var outcmd = if c.outdir.len == 0: "build" else: c.outdir - var cmd = "$# $# /O$# $#" % [quoteIfContainsWhite(c.innoSetup.path), + var cmd = "$# $# /O$# $#" % [quoteShell(c.innoSetup.path), c.innoSetup.flags, outcmd, n] echo(cmd) if execShellCmd(cmd) == 0: @@ -587,4 +587,3 @@ if actionZip in c.actions: quit("libzip is not installed") if actionDeb in c.actions: debDist(c) - -- cgit 1.4.1-2-gfad0