Merge branch 'master' of https://github.com/Araq/Nimrod into vm2

Conflicts:
	web/news.txt
This commit is contained in:
Araq 2013-12-23 01:23:16 +01:00
commit b76729df1c
35 changed files with 1047 additions and 269 deletions

View file

@ -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):

View file

@ -342,16 +342,35 @@ proc writeContentType*() =
##
## .. code-block:: Nimrod
## write(stdout, "Content-type: text/html\n\n")
##
## It also modifies the debug stack traces so that they contain
## ``<br />`` and are easily readable in a browser.
write(stdout, "Content-type: text/html\n\n")
system.stackTraceNewLine = "<br />\n"
proc setStackTraceNewLine*() =
## Modifies the debug stack traces so that they contain
## ``<br />`` and are easily readable in a browser.
system.stackTraceNewLine = "<br />\n"
proc resetForStacktrace() =
stdout.write """<!--: spam
Content-Type: text/html
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
</font> </font> </font> </script> </object> </blockquote> </pre>
</table> </table> </table> </table> </table> </font> </font> </font>
"""
proc writeErrorMessage*(data: string) =
## Tries to reset browser state and writes `data` to stdout in
## <plaintext> tag.
resetForStacktrace()
# We use <plaintext> here, instead of escaping, so stacktrace can
# be understood by human looking at source.
stdout.write("<plaintext>\n")
stdout.write(data)
proc setStackTraceStdout*() =
## Makes Nimrod output stacktraces to stdout, instead of server log.
errorMessageWriter = writeErrorMessage
proc setStackTraceNewLine*() {.deprecated.} =
## Makes Nimrod output stacktraces to stdout, instead of server log.
## Depracated alias for setStackTraceStdout.
setStackTraceStdout()
proc setCookie*(name, value: string) =
## Sets a cookie.
@ -374,4 +393,3 @@ when isMainModule:
const test1 = "abc\L+def xyz"
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
assert UrlDecode(UrlEncode(test1)) == test1

View file

@ -956,17 +956,37 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
if crename(source, dest) != 0'i32:
raise newException(EOS, $strerror(errno))
when not defined(ENOENT):
when not defined(ENOENT) and not defined(Windows):
var ENOENT {.importc, header: "<errno.h>".}: cint
when defined(Windows):
when useWinUnicode:
template DeleteFile(file: expr): expr {.immediate.} = DeleteFileW(file)
template SetFileAttributes(file, attrs: expr): expr {.immediate.} =
SetFileAttributesW(file, attrs)
else:
template DeleteFile(file: expr): expr {.immediate.} = DeleteFileA(file)
template SetFileAttributes(file, attrs: expr): expr {.immediate.} =
SetFileAttributesA(file, attrs)
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
## if the file never existed in the first place.
## On Windows, ignores the read-only attribute.
when defined(Windows):
setFilePermissions(file, {fpUserWrite})
if cremove(file) != 0'i32 and errno != ENOENT:
raise newException(EOS, $strerror(errno))
when useWinUnicode:
let f = newWideCString(file)
else:
let f = file
if DeleteFile(f) == 0:
if GetLastError() == ERROR_ACCESS_DENIED:
if SetFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
OSError(OSLastError())
if DeleteFile(f) == 0:
OSError(OSLastError())
else:
if cremove(file) != 0'i32 and errno != ENOENT:
raise newException(EOS, $strerror(errno))
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [FExecIO].} =

View file

@ -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("") == "''"

View file

@ -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: