Skip to content

Commit

Permalink
fix bug nim-lang#14343, parseCmdLine
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Aug 11, 2021
1 parent d0dd923 commit 1ee9221
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 24 deletions.
39 changes: 15 additions & 24 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2735,6 +2735,9 @@ proc exclFilePermissions*(filename: string,
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions)

when not defined(windows):
import std/private/shlexutils

proc parseCmdLine*(c: string): seq[string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a `command line`:idx: into several components.
Expand Down Expand Up @@ -2774,16 +2777,17 @@ proc parseCmdLine*(c: string): seq[string] {.
## * `paramCount proc <#paramCount>`_
## * `paramStr proc <#paramStr,int>`_
## * `commandLineParams proc <#commandLineParams>`_

result = @[]
var i = 0
var a = ""
while true:
setLen(a, 0)
# eat all delimiting whitespace
while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
if i >= c.len: break
when defined(windows):
when not defined(windows):
result = parseCmdLineImpl(c)
else:
result = @[]
var i = 0
var a = ""
while true:
setLen(a, 0)
# eat all delimiting whitespace
while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
if i >= c.len: break
# parse a single argument according to the above rules:
var inQuote = false
while i < c.len:
Expand Down Expand Up @@ -2817,20 +2821,7 @@ proc parseCmdLine*(c: string): seq[string] {.
else:
a.add(c[i])
inc(i)
else:
case c[i]
of '\'', '\"':
var delim = c[i]
inc(i) # skip ' or "
while i < c.len and c[i] != delim:
add a, c[i]
inc(i)
if i < c.len: inc(i)
else:
while i < c.len and c[i] > ' ':
add(a, c[i])
inc(i)
add(result, a)
add(result, a)

when defined(nimdoc):
# Common forward declaration docstring block for parameter retrieval procs.
Expand Down
89 changes: 89 additions & 0 deletions lib/std/private/shlexutils.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#[
KEY shlex
parseCmdLine D20200513T195153
]#

type State = enum
sInStart
sInRegular
sInSpace
sInSingleQuote
sInDoubleQuote
sFinished

type ShlexError = enum
seOk
seMissingDoubleQuote
seMissingSingleQuote

iterator shlex*(a: openArray[char], error: var ShlexError): string =
var i = 0
var buf: string
var state = sInStart
var ready = false
error = seOk
while true:
# echo (i, state, buf)
if i >= a.len:
case state
of sInSingleQuote:
error = seMissingSingleQuote
of sInDoubleQuote:
error = seMissingDoubleQuote
else:
ready = true
state = sFinished
var c: char
if i < a.len:
c = a[i]
i.inc
case state
of sFinished: discard
of sInStart:
case c
of ' ': discard
of '\'': state = sInSingleQuote
of '\"': state = sInDoubleQuote
else:
state = sInRegular
buf.add c
of sInRegular:
case c
of ' ': ready = true
of '\'': state = sInSingleQuote
of '\"': state = sInDoubleQuote
else: buf.add c
of sInSingleQuote:
case c
of '\'': state = sInRegular
else: buf.add c
of sInDoubleQuote:
case c
of '\"': state = sInRegular
# of '\'': state = sInRegular
else: buf.add c
of sInSpace:
case c
of ' ': discard
of '\'': state = sInSingleQuote
of '\"': state = sInDoubleQuote
else:
state = sInRegular
buf.add c
if ready:
ready = false
# echo (buf,)
yield buf
buf.setLen 0
if state != sFinished:
state = sInStart
if state == sFinished:
break

proc parseCmdLineImpl*(a: string): seq[string] {.inline.} =
var err: ShlexError
for val in shlex(a, err):
if err == seOk:
result.add val
else:
raise newException(ValueError, $(a, err))
14 changes: 14 additions & 0 deletions tests/stdlib/tos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -696,3 +696,17 @@ block: # isAdmin
if isAzure and defined(windows): doAssert isAdmin()
# In Azure on POSIX tests run as a normal user
if isAzure and defined(posix): doAssert not isAdmin()

# import std/strutils
# from std/sequtils import toSeq
# from std/os import commandLineParams, quoteShellCommand, parseCmdLine
template main =
# xxx move all tests under here so they get tested in VM, for ones which can
block: # parseCmdLine, bug #14343
let s = ["foo", "ba'r", "b\"az", "", "'", "''", "\"\'"]
let s2 = s.quoteShellCommand
let s3 = s2.parseCmdLine
doAssert s3 == s, $(s, s3, s2)

static: main()
main()

0 comments on commit 1ee9221

Please sign in to comment.