Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
40b01c1b
Commit
40b01c1b
authored
Oct 09, 2012
by
Francois Gouget
Committed by
Alexandre Julliard
Oct 09, 2012
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
shell32: Fix CommandLineToArgvW()'s handling of the executable path and consecutive quotes.
parent
35004f84
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
140 additions
and
58 deletions
+140
-58
shell32_main.c
dlls/shell32/shell32_main.c
+108
-26
shlexec.c
dlls/shell32/tests/shlexec.c
+32
-32
No files found.
dlls/shell32/shell32_main.c
View file @
40b01c1b
...
...
@@ -64,18 +64,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell);
* '"a b"' -> 'a b'
* - escaped quotes must be converted back to '"'
* '\"' -> '"'
* - an odd number of '\'s followed by '"' correspond to half that number
* of '\' followed by a '"' (extension of the above)
* '\\\"' -> '\"'
* '\\\\\"' -> '\\"'
* - an even number of '\'s followed by a '"' correspond to half that number
* of '\', plus a regular quote serving as an argument delimiter (which
* means it does not appear in the result)
* 'a\\"b c"' -> 'a\b c'
* 'a\\\\"b c"' -> 'a\\b c'
* - '\' that are not followed by a '"' are copied literally
* - consecutive backslashes preceding a quote see their number halved with
* the remainder escaping the quote:
* 2n backslashes + quote -> n backslashes + quote as an argument delimiter
* 2n+1 backslashes + quote -> n backslashes + literal quote
* - backslashes that are not followed by a quote are copied literally:
* 'a\b' -> 'a\b'
* 'a\\b' -> 'a\\b'
* - in quoted strings, consecutive quotes see their number divided by three
* with the remainder modulo 3 deciding whether to close the string or not.
* Note that the opening quote must be counted in the consecutive quotes,
* that's the (1+) below:
* (1+) 3n quotes -> n quotes
* (1+) 3n+1 quotes -> n quotes plus closes the quoted string
* (1+) 3n+2 quotes -> n+1 quotes plus closes the quoted string
* - in unquoted strings, the first quote opens the quoted string and the
* remaining consecutive quotes follow the above rule.
*/
LPWSTR
*
WINAPI
CommandLineToArgvW
(
LPCWSTR
lpCmdline
,
int
*
numargs
)
{
...
...
@@ -84,7 +88,7 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
LPCWSTR
s
;
LPWSTR
d
;
LPWSTR
cmdline
;
int
in_quotes
,
bcount
;
int
qcount
,
bcount
;
if
(
!
numargs
)
{
...
...
@@ -120,12 +124,33 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
/* --- First count the arguments */
argc
=
1
;
bcount
=
0
;
in_quotes
=
0
;
s
=
lpCmdline
;
/* The first argument, the executable path, follows special rules */
if
(
*
s
==
'"'
)
{
/* The executable path ends at the next quote, no matter what */
s
++
;
while
(
*
s
)
if
(
*
s
++==
'"'
)
break
;
}
else
{
/* The executable path ends at the next space, no matter what */
while
(
*
s
&&
*
s
!=
' '
&&
*
s
!=
'\t'
)
s
++
;
}
/* skip to the first argument, if any */
while
(
*
s
==
' '
||
*
s
==
'\t'
)
s
++
;
if
(
*
s
)
argc
++
;
/* Analyze the remaining arguments */
qcount
=
bcount
=
0
;
while
(
*
s
)
{
if
((
(
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
)
)
if
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
qcount
==
0
)
{
/* skip to the next argument and count it if any */
while
(
*
s
==
' '
||
*
s
==
'\t'
)
...
...
@@ -133,25 +158,36 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
if
(
*
s
)
argc
++
;
bcount
=
0
;
continue
;
}
else
if
(
*
s
==
'\\'
)
{
/* '\', count them */
bcount
++
;
s
++
;
}
else
if
(
(
*
s
==
'"'
)
&&
((
bcount
&
1
)
==
0
)
)
else
if
(
*
s
==
'"'
)
{
/* unescaped '"' */
in_quotes
=!
in_quotes
;
/* '"' */
if
((
bcount
&
1
)
==
0
)
qcount
++
;
/* unescaped '"' */
s
++
;
bcount
=
0
;
/* consecutive quotes, see comment in copying code below */
while
(
*
s
==
'"'
)
{
qcount
++
;
s
++
;
}
qcount
=
qcount
%
3
;
if
(
qcount
==
2
)
qcount
=
0
;
}
else
{
/* a regular character */
bcount
=
0
;
s
++
;
}
s
++
;
}
/* Allocate in a single lump, the string array, and the strings that go
...
...
@@ -165,13 +201,45 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
strcpyW
(
cmdline
,
lpCmdline
);
/* --- Then split and copy the arguments */
argv
[
0
]
=
d
=
cmdline
;
argc
=
1
;
bcount
=
0
;
in_quotes
=
0
;
s
=
argv
[
0
]
=
d
=
cmdline
;
/* The first argument, the executable path, follows special rules */
if
(
*
d
==
'"'
)
{
/* The executable path ends at the next quote, no matter what */
s
=
d
+
1
;
while
(
*
s
)
{
if
(
*
s
==
'"'
)
{
s
++
;
break
;
}
*
d
++=*
s
++
;
}
}
else
{
/* The executable path ends at the next space, no matter what */
while
(
*
d
&&
*
d
!=
' '
&&
*
d
!=
'\t'
)
d
++
;
s
=
d
;
if
(
*
s
)
s
++
;
}
/* close the argument */
*
d
++=
0
;
/* skip to the first argument and initialize it if any */
while
(
*
s
==
' '
||
*
s
==
'\t'
)
s
++
;
if
(
*
s
)
argv
[
argc
++
]
=
d
;
/* Split and copy the remaining arguments */
qcount
=
bcount
=
0
;
while
(
*
s
)
{
if
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
)
if
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
qcount
==
0
)
{
/* close the argument */
*
d
++=
0
;
...
...
@@ -197,8 +265,7 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
* number of '\', plus a quote which we erase.
*/
d
-=
bcount
/
2
;
in_quotes
=!
in_quotes
;
s
++
;
qcount
++
;
}
else
{
...
...
@@ -207,9 +274,24 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
*/
d
=
d
-
bcount
/
2
-
1
;
*
d
++=
'"'
;
s
++
;
}
s
++
;
bcount
=
0
;
/* Now count the number of consecutive quotes. Note that qcount
* already takes into account the opening quote if any, as well as
* the quote that lead us here.
*/
while
(
*
s
==
'"'
)
{
if
(
++
qcount
==
3
)
{
*
d
++=
'"'
;
qcount
=
0
;
}
s
++
;
}
if
(
qcount
==
2
)
qcount
=
0
;
}
else
{
...
...
dlls/shell32/tests/shlexec.c
View file @
40b01c1b
...
...
@@ -1033,25 +1033,25 @@ static const cmdline_tests_t cmdline_tests[] =
{
"exe"
,
"twoquotes"
,
"next"
,
NULL
},
0
},
{
"exe three
\"\"\"
quotes next"
,
{
"exe"
,
"three
\"
quotes"
,
"next"
,
NULL
},
0
x21
},
{
"exe"
,
"three
\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe four
\"\"\"\"
quotes
\"
next 4%3=1"
,
{
"exe"
,
"four
\"
quotes"
,
"next"
,
"4%3=1"
,
NULL
},
0
x61
},
{
"exe"
,
"four
\"
quotes"
,
"next"
,
"4%3=1"
,
NULL
},
0
},
{
"exe five
\"\"\"\"\"
quotes next"
,
{
"exe"
,
"five
\"
quotes"
,
"next"
,
NULL
},
0
x21
},
{
"exe"
,
"five
\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe six
\"\"\"\"\"\"
quotes next"
,
{
"exe"
,
"six
\"\"
quotes"
,
"next"
,
NULL
},
0
x20
},
{
"exe"
,
"six
\"\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe seven
\"\"\"\"\"\"\"
quotes
\"
next 7%3=1"
,
{
"exe"
,
"seven
\"\"
quotes"
,
"next"
,
"7%3=1"
,
NULL
},
0
x20
},
{
"exe"
,
"seven
\"\"
quotes"
,
"next"
,
"7%3=1"
,
NULL
},
0
},
{
"exe twelve
\"\"\"\"\"\"\"\"\"\"\"\"
quotes next"
,
{
"exe"
,
"twelve
\"\"\"\"
quotes"
,
"next"
,
NULL
},
0
x20
},
{
"exe"
,
"twelve
\"\"\"\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe thirteen
\"\"\"\"\"\"\"\"\"\"\"\"\"
quotes
\"
next 13%3=1"
,
{
"exe"
,
"thirteen
\"\"\"\"
quotes"
,
"next"
,
"13%3=1"
,
NULL
},
0
x20
},
{
"exe"
,
"thirteen
\"\"\"\"
quotes"
,
"next"
,
"13%3=1"
,
NULL
},
0
},
/* Inside a quoted string the opening quote is added to the set of
* consecutive quotes to get the effective quotes count. This gives:
...
...
@@ -1060,32 +1060,32 @@ static const cmdline_tests_t cmdline_tests[] =
* 1+3n+2 quotes -> n+1 quotes plus closes the quoted string
*/
{
"exe
\"
two
\"\"
quotes next"
,
{
"exe"
,
"two
\"
quotes"
,
"next"
,
NULL
},
0
x21
},
{
"exe"
,
"two
\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe
\"
two
\"\"
next"
,
{
"exe"
,
"two
\"
"
,
"next"
,
NULL
},
0
x21
},
{
"exe"
,
"two
\"
"
,
"next"
,
NULL
},
0
},
{
"exe
\"
three
\"\"\"
quotes
\"
next 4%3=1"
,
{
"exe"
,
"three
\"
quotes"
,
"next"
,
"4%3=1"
,
NULL
},
0
x61
},
{
"exe"
,
"three
\"
quotes"
,
"next"
,
"4%3=1"
,
NULL
},
0
},
{
"exe
\"
four
\"\"\"\"
quotes next"
,
{
"exe"
,
"four
\"
quotes"
,
"next"
,
NULL
},
0
x21
},
{
"exe"
,
"four
\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe
\"
five
\"\"\"\"\"
quotes next"
,
{
"exe"
,
"five
\"\"
quotes"
,
"next"
,
NULL
},
0
x20
},
{
"exe"
,
"five
\"\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe
\"
six
\"\"\"\"\"\"
quotes
\"
next 7%3=1"
,
{
"exe"
,
"six
\"\"
quotes"
,
"next"
,
"7%3=1"
,
NULL
},
0
x20
},
{
"exe"
,
"six
\"\"
quotes"
,
"next"
,
"7%3=1"
,
NULL
},
0
},
{
"exe
\"
eleven
\"\"\"\"\"\"\"\"\"\"\"
quotes next"
,
{
"exe"
,
"eleven
\"\"\"\"
quotes"
,
"next"
,
NULL
},
0
x20
},
{
"exe"
,
"eleven
\"\"\"\"
quotes"
,
"next"
,
NULL
},
0
},
{
"exe
\"
twelve
\"\"\"\"\"\"\"\"\"\"\"\"
quotes
\"
next 13%3=1"
,
{
"exe"
,
"twelve
\"\"\"\"
quotes"
,
"next"
,
"13%3=1"
,
NULL
},
0
x20
},
{
"exe"
,
"twelve
\"\"\"\"
quotes"
,
"next"
,
"13%3=1"
,
NULL
},
0
},
/* Escaped consecutive quotes are fun */
{
"exe
\"
the crazy
\\\\\"\"\"\\\\\"
quotes"
,
{
"exe"
,
"the crazy
\\\"\\
"
,
"quotes"
,
NULL
},
0
x21
},
{
"exe"
,
"the crazy
\\\"\\
"
,
"quotes"
,
NULL
},
0
},
/* The executable path has its own rules!!!
* - Backslashes have no special meaning.
...
...
@@ -1099,16 +1099,16 @@ static const cmdline_tests_t cmdline_tests[] =
* argument, the latter is parsed using the regular rules.
*/
{
"exe
\"
file
\"
path arg1"
,
{
"exe
\"
file
\"
path"
,
"arg1"
,
NULL
},
0
x10
},
{
"exe
\"
file
\"
path"
,
"arg1"
,
NULL
},
0
},
{
"exe
\"
file
\"
path
\t
arg1"
,
{
"exe
\"
file
\"
path"
,
"arg1"
,
NULL
},
0
x10
},
{
"exe
\"
file
\"
path"
,
"arg1"
,
NULL
},
0
},
{
"exe
\"
path
\\
arg1"
,
{
"exe
\"
path
\\
"
,
"arg1"
,
NULL
},
0
x31
},
{
"exe
\"
path
\\
"
,
"arg1"
,
NULL
},
0
},
{
"
\\\"
exe
\"
arg one
\"
"
,
{
"
\\\"
exe"
,
"arg one"
,
NULL
},
0
x10
},
{
"
\\\"
exe"
,
"arg one"
,
NULL
},
0
},
{
"
\"
spaced exe
\"
\"
next arg
\"
"
,
{
"spaced exe"
,
"next arg"
,
NULL
},
0
},
...
...
@@ -1117,19 +1117,19 @@ static const cmdline_tests_t cmdline_tests[] =
{
"spaced exe"
,
"next arg"
,
NULL
},
0
},
{
"
\"
exe
\"
arg
\"
one
\"
argtwo"
,
{
"exe"
,
"arg one"
,
"argtwo"
,
NULL
},
0
x31
},
{
"exe"
,
"arg one"
,
"argtwo"
,
NULL
},
0
},
{
"
\"
spaced exe
\\\"
arg1 arg2"
,
{
"spaced exe
\\
"
,
"arg1"
,
"arg2"
,
NULL
},
0
x11
},
{
"spaced exe
\\
"
,
"arg1"
,
"arg2"
,
NULL
},
0
},
{
"
\"
two
\"\"
arg1 "
,
{
"two"
,
" arg1 "
,
NULL
},
0
x11
},
{
"two"
,
" arg1 "
,
NULL
},
0
},
{
"
\"
three
\"\"\"
arg2"
,
{
"three"
,
""
,
"arg2"
,
NULL
},
0
x61
},
{
"three"
,
""
,
"arg2"
,
NULL
},
0
},
{
"
\"
four
\"\"\"\"
arg1"
,
{
"four"
,
"
\"
arg1"
,
NULL
},
0
x11
},
{
"four"
,
"
\"
arg1"
,
NULL
},
0
},
/* If the first character is a space then the executable path is empty */
{
"
\"
arg
\"
one argtwo"
,
...
...
@@ -1270,7 +1270,7 @@ static const argify_tests_t argify_tests[] =
/* Only (double-)quotes have a special meaning. */
{
"Params23456"
,
"'p2 p3` p4
\\
$even"
,
0x40
,
{
"
\"
'p2
\"
\"
p3`
\"
\"
p4
\\\"
\"
$even
\"
\"\"
"
,
{
""
,
"'p2"
,
"p3`"
,
"p4
\"
$even
\"
"
,
NULL
},
0
x80
}},
{
""
,
"'p2"
,
"p3`"
,
"p4
\"
$even
\"
"
,
NULL
},
0
}},
{
"Params23456"
,
"p=2 p-3 p4
\t
p4
\r
p4
\n
p4"
,
0x1c2
,
{
"
\"
p=2
\"
\"
p-3
\"
\"
p4
\t
p4
\r
p4
\n
p4
\"
\"\"
\"\"
"
,
...
...
@@ -1292,11 +1292,11 @@ static const argify_tests_t argify_tests[] =
{
"Params23456789"
,
"three
\"\"\"
quotes
\"
p four
\"
three
\"\"\"
quotes p6"
,
0xff3
,
{
"
\"
three
\"\"
\"
quotes
\"
\"
p four
\"
\"
three
\"\"
\"
quotes
\"
\"
p6
\"
\"\"
\"\"
"
,
{
""
,
"three
\"
"
,
"quotes"
,
"p four"
,
"three
\"
"
,
"quotes"
,
"p6"
,
""
,
""
,
NULL
},
0
x7e1
}},
{
""
,
"three
\"
"
,
"quotes"
,
"p four"
,
"three
\"
"
,
"quotes"
,
"p6"
,
""
,
""
,
NULL
},
0
}},
{
"Params23456789"
,
"four
\"\"\"\"
quotes
\"
p three
\"
four
\"\"\"\"
quotes p5"
,
0xf3
,
{
"
\"
four
\"\"
quotes
\"
\"
p three
\"
\"
four
\"\"
quotes
\"
\"
p5
\"
\"\"
\"\"
\"\"
\"\"
"
,
{
""
,
"four
\"
quotes p"
,
"three fourquotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
xde1
}},
{
""
,
"four
\"
quotes p"
,
"three fourquotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
}},
/* Quoted strings cannot be continued by tacking on a non space character
* either.
...
...
@@ -1320,11 +1320,11 @@ static const argify_tests_t argify_tests[] =
{
"Params23456789"
,
"
\"
three q
\"\"\"
uotes
\"
p four
\"
\"
three q
\"\"\"
uotes p7"
,
0xff3
,
{
"
\"
three q
\"\"
\"
uotes
\"
\"
p four
\"
\"
three q
\"\"
\"
uotes
\"
\"
p7
\"
\"\"
\"\"
"
,
{
""
,
"three q
\"
"
,
"uotes"
,
"p four"
,
"three q
\"
"
,
"uotes"
,
"p7"
,
""
,
""
,
NULL
},
0
x7e1
}},
{
""
,
"three q
\"
"
,
"uotes"
,
"p four"
,
"three q
\"
"
,
"uotes"
,
"p7"
,
""
,
""
,
NULL
},
0
}},
{
"Params23456789"
,
"
\"
four
\"\"\"\"
quotes
\"
\"
p three
\"
\"
four
\"\"\"\"
quotes
\"
p5"
,
0xff3
,
{
"
\"
four
\"\"
quotes
\"
\"
p three
\"
\"
four
\"\"
quotes
\"
\"
p5
\"
\"\"
\"\"
\"\"
\"\"
"
,
{
""
,
"four
\"
"
,
"quotes p"
,
"three four"
,
""
,
"quotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
x3e0
}},
{
""
,
"four
\"
"
,
"quotes p"
,
"three four"
,
""
,
"quotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
}},
/* The quoted string rules also apply to consecutive quotes at the start
* of a parameter but don't count the opening quote!
...
...
@@ -1335,11 +1335,11 @@ static const argify_tests_t argify_tests[] =
{
"Params23456789"
,
"
\"\"\"
three quotes
\"
\"
p three
\"
\"\"\"
three quotes
\"
p5"
,
0x6f3
,
{
"
\"\"
three quotes
\"
\"
p three
\"
\"\"
three quotes
\"
\"
p5
\"
\"\"
\"\"
\"\"
\"\"
"
,
{
""
,
"three"
,
"quotes p"
,
"three
\"
three"
,
"quotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
x181
}},
{
""
,
"three"
,
"quotes p"
,
"three
\"
three"
,
"quotes p5
\"
"
,
""
,
""
,
""
,
NULL
},
0
}},
{
"Params23456789"
,
"
\"\"\"\"
fourquotes
\"
p four
\"
\"\"\"\"
fourquotes p7"
,
0xbf3
,
{
"
\"\"\"
\"
fourquotes
\"
\"
p four
\"
\"\"\"
\"
fourquotes
\"
\"
p7
\"
\"\"
\"\"
"
,
{
""
,
"
\"
"
,
"fourquotes"
,
"p four"
,
"
\"
"
,
"fourquotes"
,
"p7"
,
""
,
""
,
NULL
},
0
x7e1
}},
{
""
,
"
\"
"
,
"fourquotes"
,
"p four"
,
"
\"
"
,
"fourquotes"
,
"p7"
,
""
,
""
,
NULL
},
0
}},
/* An unclosed quoted string gets lost! */
{
"Params23456"
,
"p2
\"
p3
\"
\"
p4 is lost"
,
0x1c3
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment