Fix the conversions of a command line to/from an argv array.

This commit is contained in:
Francois Gouget 2001-09-20 19:05:11 +00:00 committed by Alexandre Julliard
parent 45e9cea3d1
commit 5ee3879ce0
3 changed files with 333 additions and 98 deletions

View File

@ -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;
}
/*************************************************************************

View File

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

View File

@ -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;
}