Bug #1251 Quotes in $0 not handled properly on Windows XP
Submitted: 11 Sep 2003 9:23 Modified: 17 Dec 2003 10:08
Reporter: Derek Price Email Updates:
Status: Won't fix Impact on me:
None 
Category:MySQL Server: Command-line Clients Severity:S3 (Non-critical)
Version:3.23.52 OS:Windows (Windows XP)
Assigned to: MySQL Verification Team CPU Architecture:Any

[11 Sep 2003 9:23] Derek Price
Description:
When calling the MySQL client executable with quotes in the name, they are not always handled properly.  It appears that the client is parsing its argument list for quotes itself and interpreting anything after a quoted partial string as the next argument.

For example:

     "c:\mysql"\bin\mysql -uroot

generates the following error message:

    C:\cygwin\home\oberon\work\sisyphus>"c:\mysql"\bin\mysql -u root
    c:\mysql  Ver 11.18 Distrib 3.23.52, for Win95/Win98 (i32)
    Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
    This software comes with ABSOLUTELY NO WARRANTY. This is free software,
    and you are welcome to modify and redistribute it under the GPL license

    Usage: c:\mysql [OPTIONS] [database]

      -?, --help            Display this help and exit.
      -A, --no-auto-rehash  No automatic rehashing. One has to use 'rehash' to

    . . .

Note that in the error message above, the client reports its name as `c:\mysql' rather than `c:\mysql\bin\mysql'.

Similarly, simply calling the client as "c:\mysql"\bin\mysql generates a warning that \bin\mysql is not a valid database:

    C:\cygwin\home\oberon\work\sisyphus>"c:\mysql"\bin\mysql
    ERROR 1102: Incorrect database name '\bin\mysql'

How to repeat:
See above.

Suggested fix:
I can't be sure of all the details without looking at the source and I am not a Windows programmer, but it looks like argv is being reparsed by the client executable and it shouldn't be, except possibly to simply remove the extraneous quotes in each element of argv.
[11 Sep 2003 16:03] MySQL Verification Team
Thank you for the bug report. I was able to repeat.
[17 Dec 2003 10:08] MySQL Verification Team
The mysql client just uses the path that Windows interprets
from the command prompt that belongs to argv[0]. Like you
see in the below small program:

C:\test>type test.c
#include <stdlib.h>

int main(int argc, char *argv[])
{
  printf("argv[0]: %s\n", argv[0]);
  return 0;
}

C:\test>"c:\test"\test
argv[0]: c:\test

C:\test>"c:\test\test"
argv[0]: c:\test\test

The syntax example I found from Microsoft documentation only
makes reference in the use of double quotes enclosing the full
path not like you have reported.
[17 Dec 2003 10:19] Derek Price
Documented or not, I would like to submit that the program _can_ be run that way, as demonstrated by the test.c program.  The Windows shell obviously interprets the quotes as I've noted, and calls the correct program, passing the quotes in as the user specified, which mysql then chokes on.

Therefore, this should probably be looked at as either a bug in the Windows documentation, in which case mysql has a bug too, or a bug in the Windows shell, in which case mysql could perhaps be expected to provide a work around until the Windows shell is fixed.

Incidentally, bash accepts a similar quotation syntax, but I believe removes the quotes before filling argv if you want to go to the trouble of reporting a bug and suggesting a solution to Microsoft.

Derek
[17 Dec 2003 10:27] Derek Price
Hrm.  My apologies.  That definitely looks like a bug in the Windows shell.  I misread the test.c output last time and failed to note that argv[0] was really only c:\test in the "c:\test"\test invocation.

I concur with your decision and analysis.  Anyone there know how to report a bug to Micros~1?

Thanks for taking a look and my apologies again for arguing with your correct assessment.

Regards,

Derek
[17 Dec 2003 10:40] Paul DuBois
I'd like to see what happens if the small test
program is modified to loop through all the
arguments and print them.  Printing just
argv[0] is inconclusive.  Printing all the arguments
would indicate whether the shell is actually
passing a bad argv[1] when the program
is invoked like this:

C:\test>"c:\test"\test

It may be that the Windows shell is passing
\test as a *separate* argument.  This would
account for why mysql believes it is being
passed a bad database name.
[17 Dec 2003 11:08] Derek Price
I had assumed that argv[1] (and other args depending on the number of quoted strings) would be set incorrectly, but here is the test program that proves it:

C:\test>type test.c
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int i;
  for (i=0; i < argc; i++)
  {
    printf("argv[%d]: %s\n", i, argv[i]);
  }
  return 0;
}

C:\test>"c:\test"\test.exe
argv[0]: c:\test
argv[1]: \test.exe

C:\test>"c:\test\test.exe"
argv[0]: c:\test\test.exe
[17 Dec 2003 12:23] Derek Price
Actually, come to think of it, I think a program could detect this - in pseudocode, and discounting the fact that myname should be dynamically allocated to avoid buffer overflow issues:

myname[0] = '\0';
for (i=0; i < argc; i++)
{
    /* concatenate our arguments until we find our executable */
    strcat (myname, argv[i]);
    if (!isdirectory(myname)) break;
}

/* Now myname contains a path to the executable and i+1 will be the first
 * argument that should be used (what should have been argv[1]).
 */

Incidentally, not that it matters for the solution above, but quotes in other places are not munged by the Windows shell, so you won't need to worry about compensating for this sort of behavior for any arguments other than argv[0].  Using my last test.c program:

c:\test>test "a long"arg"with quotes"
argv[0]: test
argv[1]: a longargwith quotes
[17 Dec 2003 12:35] Paul DuBois
Would that really work? If you're on the C drive, you
could invoke your program as:

"\test"\test

in which case, both of the first two arguments
would be identical.
[17 Dec 2003 12:40] Derek Price
It should.  It doesn't matter if the two arguments are identical.  In your example:

First pass: myname = "" + argv[0] = "\test".
            isdirectory(myname) returns true.
Next pass:  myname = "\test" + argv[1] = "\test\test".
            isdirectory(myname) returns false, breaks out of loop
              with myname = "\test\test" && i = 1.

???