Fix the conversions of a command line to/from an argv array.
This commit is contained in:
parent
45e9cea3d1
commit
5ee3879ce0
|
@ -34,50 +34,152 @@ DEFAULT_DEBUG_CHANNEL(shell);
|
|||
#define MORE_DEBUG 1
|
||||
/*************************************************************************
|
||||
* CommandLineToArgvW [SHELL32.@]
|
||||
*
|
||||
* We must interpret the quotes in the command line to rebuild the argv
|
||||
* array correctly:
|
||||
* - arguments are separated by spaces or tabs
|
||||
* - quotes serve as optional argument delimiters
|
||||
* '"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
|
||||
* 'a\b' -> 'a\b'
|
||||
* 'a\\b' -> 'a\\b'
|
||||
*
|
||||
* Note:
|
||||
* '\t' == 0x0009
|
||||
* ' ' == 0x0020
|
||||
* '"' == 0x0022
|
||||
* '\\' == 0x005c
|
||||
*/
|
||||
LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
|
||||
{ LPWSTR *argv,s,t;
|
||||
LPWSTR cmdline;
|
||||
int i;
|
||||
TRACE("\n");
|
||||
{
|
||||
DWORD argc;
|
||||
LPWSTR *argv;
|
||||
LPWSTR arg,s,d;
|
||||
LPWSTR cmdline;
|
||||
int in_quotes,bcount;
|
||||
|
||||
/* to get writeable copy */
|
||||
if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR) )))
|
||||
return NULL;
|
||||
strcpyW( cmdline, lpCmdline );
|
||||
s=cmdline;
|
||||
i=0;
|
||||
while (*s)
|
||||
{ /* space */
|
||||
if (*s==0x0020)
|
||||
{ i++;
|
||||
s++;
|
||||
while (*s && *s==0x0020)
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
argv=(LPWSTR*)HeapAlloc( GetProcessHeap(), 0, sizeof(LPWSTR)*(i+1) );
|
||||
s=t=cmdline;
|
||||
i=0;
|
||||
while (*s)
|
||||
{
|
||||
if (*s==0x0020)
|
||||
{
|
||||
argv[i++]=t;
|
||||
while (*s==0x0020) *s++ = 0;
|
||||
t=s;
|
||||
continue;
|
||||
/* FIXME: same thing if we only have spaces */
|
||||
if (*lpCmdline==0) {
|
||||
/* Return the path to the executable */
|
||||
DWORD size;
|
||||
|
||||
argv=HeapAlloc(GetProcessHeap(), 0, 2*sizeof(LPWSTR));
|
||||
argv[0]=NULL;
|
||||
size=16;
|
||||
do {
|
||||
size*=2;
|
||||
argv[0]=HeapReAlloc(GetProcessHeap(), 0, argv[0], size);
|
||||
} while (GetModuleFileNameW((HMODULE)0, argv[0], size) == 0);
|
||||
argv[1]=NULL;
|
||||
if (numargs)
|
||||
*numargs=2;
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
/* to get a writeable copy */
|
||||
cmdline = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR));
|
||||
if (!cmdline)
|
||||
return NULL;
|
||||
strcpyW(cmdline, lpCmdline);
|
||||
argc=0;
|
||||
bcount=0;
|
||||
in_quotes=0;
|
||||
s=cmdline;
|
||||
while (1) {
|
||||
if (*s==0 || ((*s==0x0009 || *s==0x0020) && !in_quotes)) {
|
||||
/* space */
|
||||
argc++;
|
||||
/* skip the remaining spaces */
|
||||
while (*s==0x0009 || *s==0x0020) {
|
||||
s++;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (*t)
|
||||
argv[i++]=t;
|
||||
if (*s==0)
|
||||
break;
|
||||
bcount=0;
|
||||
continue;
|
||||
} else if (*s==0x005c) {
|
||||
/* '\', count them */
|
||||
bcount++;
|
||||
} else if ((*s==0x0022) && ((bcount & 1)==0)) {
|
||||
/* unescaped '"' */
|
||||
in_quotes=!in_quotes;
|
||||
bcount=0;
|
||||
} else {
|
||||
/* a regular character */
|
||||
bcount=0;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
argv=HeapAlloc(GetProcessHeap(), 0, (argc+1)*sizeof(LPWSTR));
|
||||
|
||||
argv[i]=NULL;
|
||||
*numargs=i;
|
||||
return argv;
|
||||
argc=0;
|
||||
bcount=0;
|
||||
in_quotes=0;
|
||||
arg=d=s=cmdline;
|
||||
while (*s) {
|
||||
if ((*s==0x0009 || *s==0x0020) && !in_quotes) {
|
||||
/* Close the argument and copy it */
|
||||
*d=0;
|
||||
argv[argc++]=arg;
|
||||
|
||||
/* skip the remaining spaces */
|
||||
do {
|
||||
s++;
|
||||
} while (*s==0x0009 || *s==0x0020);
|
||||
|
||||
/* Start with a new argument */
|
||||
arg=d=s;
|
||||
bcount=0;
|
||||
} else if (*s==0x005c) {
|
||||
/* '\\' */
|
||||
*d++=*s++;
|
||||
bcount++;
|
||||
} else if (*s==0x0022) {
|
||||
/* '"' */
|
||||
if ((bcount & 1)==0) {
|
||||
/* Preceeded by an even number of '\', this is half that
|
||||
* number of '\', plus a quote which we erase.
|
||||
*/
|
||||
d-=bcount/2;
|
||||
in_quotes=!in_quotes;
|
||||
s++;
|
||||
} else {
|
||||
/* Preceeded by an odd number of '\', this is half that
|
||||
* number of '\' followed by a '"'
|
||||
*/
|
||||
d=d-bcount/2-1;
|
||||
*d++='"';
|
||||
s++;
|
||||
}
|
||||
bcount=0;
|
||||
} else {
|
||||
/* a regular character */
|
||||
*d++=*s++;
|
||||
bcount=0;
|
||||
}
|
||||
}
|
||||
if (*arg) {
|
||||
*d='\0';
|
||||
argv[argc++]=arg;
|
||||
}
|
||||
argv[argc]=NULL;
|
||||
if (numargs)
|
||||
*numargs=argc;
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, cmdline);
|
||||
return argv;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
|
|
125
memory/environ.c
125
memory/environ.c
|
@ -174,34 +174,121 @@ ENVDB *ENV_BuildEnvironment(void)
|
|||
*
|
||||
* Note that it does NOT necessarily include the file name.
|
||||
* Sometimes we don't even have any command line options at all.
|
||||
*
|
||||
* We must quote and escape characters so that the argv array can be rebuilt
|
||||
* from the command line:
|
||||
* - spaces and tabs must be quoted
|
||||
* 'a b' -> '"a b"'
|
||||
* - quotes must be escaped
|
||||
* '"' -> '\"'
|
||||
* - if '\'s are followed by a '"', they must be doubled and followed by '\"',
|
||||
* resulting in an odd number of '\' followed by a '"'
|
||||
* '\"' -> '\\\"'
|
||||
* '\\"' -> '\\\\\"'
|
||||
* - '\'s that are not followed by a '"' can be left as is
|
||||
* 'a\b' == 'a\b'
|
||||
* 'a\\b' == 'a\\b'
|
||||
*/
|
||||
BOOL ENV_BuildCommandLine( char **argv )
|
||||
{
|
||||
int len, quote = 0;
|
||||
int len;
|
||||
char *p, **arg;
|
||||
|
||||
for (arg = argv, len = 0; *arg; arg++) len += strlen(*arg) + 1;
|
||||
if ((argv[0]) && (quote = (strchr( argv[0], ' ' ) != NULL))) len += 2;
|
||||
if (!(p = current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
|
||||
arg = argv;
|
||||
if (quote)
|
||||
len = 0;
|
||||
for (arg = argv; *arg; arg++)
|
||||
{
|
||||
*p++ = '\"';
|
||||
strcpy( p, *arg );
|
||||
p += strlen(p);
|
||||
*p++ = '\"';
|
||||
*p++ = ' ';
|
||||
arg++;
|
||||
int has_space,bcount;
|
||||
char* a;
|
||||
|
||||
has_space=0;
|
||||
bcount=0;
|
||||
a=*arg;
|
||||
while (*a!='\0') {
|
||||
if (*a=='\\') {
|
||||
bcount++;
|
||||
} else {
|
||||
if (*a==' ' || *a=='\t') {
|
||||
has_space=1;
|
||||
} else if (*a=='"') {
|
||||
/* doubling of '\' preceeding a '"',
|
||||
* plus escaping of said '"'
|
||||
*/
|
||||
len+=2*bcount+1;
|
||||
}
|
||||
bcount=0;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
len+=(a-*arg)+1 /* for the separating space */;
|
||||
if (has_space)
|
||||
len+=2; /* for the quotes */
|
||||
}
|
||||
while (*arg)
|
||||
|
||||
if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
|
||||
return FALSE;
|
||||
|
||||
p = current_envdb.cmd_line;
|
||||
for (arg = argv; *arg; arg++)
|
||||
{
|
||||
strcpy( p, *arg );
|
||||
p += strlen(p);
|
||||
*p++ = ' ';
|
||||
arg++;
|
||||
int has_space,has_quote;
|
||||
char* a;
|
||||
|
||||
/* Check for quotes and spaces in this argument */
|
||||
has_space=has_quote=0;
|
||||
a=*arg;
|
||||
while (*a!='\0') {
|
||||
if (*a==' ' || *a=='\t') {
|
||||
has_space=1;
|
||||
if (has_quote)
|
||||
break;
|
||||
} else if (*a=='"') {
|
||||
has_quote=1;
|
||||
if (has_space)
|
||||
break;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
/* Now transfer it to the command line */
|
||||
if (has_space)
|
||||
*p++='"';
|
||||
if (has_quote) {
|
||||
int bcount;
|
||||
char* a;
|
||||
|
||||
bcount=0;
|
||||
a=*arg;
|
||||
while (*a!='\0') {
|
||||
if (*a=='\\') {
|
||||
*p++=*a;
|
||||
bcount++;
|
||||
} else {
|
||||
if (*a=='"') {
|
||||
int i;
|
||||
|
||||
/* Double all the '\\' preceeding this '"', plus one */
|
||||
for (i=0;i<=bcount;i++)
|
||||
*p++='\\';
|
||||
*p++='"';
|
||||
} else {
|
||||
*p++=*a;
|
||||
}
|
||||
bcount=0;
|
||||
}
|
||||
a++;
|
||||
}
|
||||
} else {
|
||||
strcpy(p,*arg);
|
||||
p+=strlen(*arg);
|
||||
}
|
||||
if (has_space)
|
||||
*p++='"';
|
||||
*p++=' ';
|
||||
}
|
||||
if (p > current_envdb.cmd_line) p--; /* remove last space */
|
||||
*p = 0;
|
||||
if (p > current_envdb.cmd_line)
|
||||
p--; /* remove last space */
|
||||
*p = '\0';
|
||||
|
||||
/* now allocate the Unicode version */
|
||||
len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
|
||||
if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
|
||||
|
|
|
@ -531,50 +531,96 @@ void PROCESS_InitWine( int argc, char *argv[], LPSTR win16_exe_name, HANDLE *win
|
|||
*/
|
||||
static char **build_argv( char *cmdline, int reserved )
|
||||
{
|
||||
char **argv;
|
||||
int count = reserved + 1;
|
||||
char *p = cmdline;
|
||||
int argc;
|
||||
char** argv;
|
||||
char *arg,*s,*d;
|
||||
int in_quotes,bcount;
|
||||
|
||||
/* if first word is quoted store it as a single arg */
|
||||
if (*cmdline == '\"')
|
||||
{
|
||||
if ((p = strchr( cmdline + 1, '\"' )))
|
||||
{
|
||||
p++;
|
||||
count++;
|
||||
}
|
||||
else p = cmdline;
|
||||
}
|
||||
while (*p)
|
||||
{
|
||||
while (*p && isspace(*p)) p++;
|
||||
if (!*p) break;
|
||||
count++;
|
||||
while (*p && !isspace(*p)) p++;
|
||||
}
|
||||
|
||||
if ((argv = malloc( count * sizeof(*argv) )))
|
||||
{
|
||||
char **argvptr = argv + reserved;
|
||||
p = cmdline;
|
||||
if (*cmdline == '\"')
|
||||
{
|
||||
if ((p = strchr( cmdline + 1, '\"' )))
|
||||
{
|
||||
*argvptr++ = cmdline + 1;
|
||||
*p++ = 0;
|
||||
argc=reserved+1;
|
||||
bcount=0;
|
||||
in_quotes=0;
|
||||
s=cmdline;
|
||||
while (1) {
|
||||
if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
|
||||
/* space */
|
||||
argc++;
|
||||
/* skip the remaining spaces */
|
||||
while (*s==' ' || *s=='\t') {
|
||||
s++;
|
||||
}
|
||||
else p = cmdline;
|
||||
if (*s=='\0')
|
||||
break;
|
||||
bcount=0;
|
||||
continue;
|
||||
} else if (*s=='\\') {
|
||||
/* '\', count them */
|
||||
bcount++;
|
||||
} else if ((*s=='"') && ((bcount & 1)==0)) {
|
||||
/* unescaped '"' */
|
||||
in_quotes=!in_quotes;
|
||||
bcount=0;
|
||||
} else {
|
||||
/* a regular character */
|
||||
bcount=0;
|
||||
}
|
||||
while (*p)
|
||||
{
|
||||
while (*p && isspace(*p)) *p++ = 0;
|
||||
if (!*p) break;
|
||||
*argvptr++ = p;
|
||||
while (*p && !isspace(*p)) p++;
|
||||
}
|
||||
*argvptr = 0;
|
||||
s++;
|
||||
}
|
||||
argv=malloc(argc*sizeof(*argv));
|
||||
if (!argv)
|
||||
return NULL;
|
||||
|
||||
arg=d=s=cmdline;
|
||||
bcount=0;
|
||||
in_quotes=0;
|
||||
argc=reserved;
|
||||
while (*s) {
|
||||
if ((*s==' ' || *s=='\t') && !in_quotes) {
|
||||
/* Close the argument and copy it */
|
||||
*d=0;
|
||||
argv[argc++]=arg;
|
||||
|
||||
/* skip the remaining spaces */
|
||||
do {
|
||||
s++;
|
||||
} while (*s==' ' || *s=='\t');
|
||||
|
||||
/* Start with a new argument */
|
||||
arg=d=s;
|
||||
bcount=0;
|
||||
} else if (*s=='\\') {
|
||||
/* '\\' */
|
||||
*d++=*s++;
|
||||
bcount++;
|
||||
} else if (*s=='"') {
|
||||
/* '"' */
|
||||
if ((bcount & 1)==0) {
|
||||
/* Preceeded by an even number of '\', this is half that
|
||||
* number of '\', plus a '"' which we discard.
|
||||
*/
|
||||
d-=bcount/2;
|
||||
s++;
|
||||
in_quotes=!in_quotes;
|
||||
} else {
|
||||
/* Preceeded by an odd number of '\', this is half that
|
||||
* number of '\' followed by a '"'
|
||||
*/
|
||||
d=d-bcount/2-1;
|
||||
*d++='"';
|
||||
s++;
|
||||
}
|
||||
bcount=0;
|
||||
} else {
|
||||
/* a regular character */
|
||||
*d++=*s++;
|
||||
bcount=0;
|
||||
}
|
||||
}
|
||||
if (*arg) {
|
||||
*d='\0';
|
||||
argv[argc++]=arg;
|
||||
}
|
||||
argv[argc]=NULL;
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue