views:

222

answers:

3

Why do Windows programs parse command-line switches out of their executable's path? (The latter being what is commonly known as argv[0].)

For example, xcopy:

C:\Temp\foo>c:/windows/system32/xcopy.exe /f /r /i /d /y * ..\bar\
Invalid number of parameters

C:\Temp\foo>c:\windows\system32\xcopy.exe /f /r /i /d /y * ..\bar\
C:\Temp\foo\blah -> C:\Temp\bar\blah
1 File(s) copied

What behavior should I follow in my own programs?

Are there many users that expect to type command-line switches without a space (e.g. program/? instead of program /?), and should I try to support this, or should I just report an error and exit immediately?

What other caveats do I need to be aware of? (In addition to Anon.'s comment below that "debug/program" runs debug.exe from PATH even if "debug\program.exe" exists.)

+1  A: 

I expect that any program doing this would be using GetCommandLine() instead of argv[] to access the command line arguments, and failing to account for the fungibility of / and \ in user-mode paths on Windows.

Slothman
I think this points to the key issue: the Windows API uses GetCommandLine rather than argv-style parameters.
Roger Pate
+1  A: 

I think it's actually the DOS shell doing this:

My understanding is that DOS chose to use the forward slash (/) for command-line options (i.e., "DIR /s"), even before DOS supported sub-directories. Later, at the point that they introduced sub-directories, they realized they couldn't use forward slashes as the path separator (as was the standard on UNIX), so they had to use the backslash instead.

Also a factor is that DOS doesn't require a space between the command name and the first parameter. (I.e., "CD\" is the same as "CD \".)

Because of the above, my guess is that it isn't the program that is parsing the command line "incorrectly"-- instead it is the DOS shell that is using "C:" as the executable / command name, and the rest as the command line argument(s). (Of course, a quite test app could verify this, but I'm away from my compiler at the moment.)

Eric Pi
This is correct. If you want an example, try taking `cmd` to a Visual Studio project directory and then running `debug/myapp.exe`. Instead of running your app, you'll end up running `debug`.
Anon.
It isn't the DOS shell interpreting it incorrectly (@Anon.: if it was, your program would never execute), it is the program itself---or the C runtime library ("CRT") specifically in this case. Windows passes the full command-line as a single string, and the program is responsible for breaking it up, this happens before main() begins.
Roger Pate
@Roger: Have you tried my example? You will find that `debug` is executed, and *your program never gets touched*.
Anon.
Roger Pate
@Roger: Ah, that makes sense. Thanks for the correction.
Anon.
+1  A: 

I have a couple of suggestions that might help. These are the result of a class that I made (and use) just to handle parameters and switches.

  • I check the argument array to see which delimiter is being used for the path and which is being used for the switches / parameters.

  • I personally differentiate between switches and parameters using a slash for one and a hyphen for the other.

  • If a switch is passed that doesn't match any of the expected parameters or switches, I check to see if multiple switches were passed with only one slash. This one has caused and will cause more issues for users if they mistype something. For instance, if I were looking for /d /e /l -or- /del SomeThing and the user inputs /del with the intent of passing the d e and l switches.

  • In the object, I stuff the switches in a std:: container and the parameter and parameter values in another std:: container which are then made available to the consumer application to process as it sees fit.

ISDi
@ISDi: What is the difference between a parameter and a switch, in your eyes?
Roger Pate
Ola Roger, For me, a parameter is data that is being passed to an app. I consider a switch to be a toggle. For example, I would treat "-?" as a switch. It is either there or it is not. I would treat "-pd '/Some/Path/ToProj'" as a parameter.
ISDi