[Distutils] install w/o build, spaces in directory names

Thomas Heller thomas.heller@ion-tof.com
Wed, 16 Feb 2000 19:27:00 +0100

Greg said:
> On 11 February 2000, Thomas Heller said:
> > I tried quoting the parameters for _spawn_nt in the following way,
> > which worked for me (after changing some paths to include spaces,
> > which I usually avoid). Not sure if cmd[0] should also be quoted:
> Well, this patch looks better than nothing.  But I have a couple
> questions for the Windows crowd:
>   * when I use 'os.spawnv()', just how many levels of software are
>     involved (C library? kernel? the child program being run?) in
>     parsing the command line -- i.e. in splitting the
>     Python-list-of-strings up into the argv[] seen by the child program?
>     In other words, whose rules do we have to work with here to properly
>     handle filenames with spaces in them?

Eventually os.spawnv() calls the win32 API funtion CreateProcess.
This is somewhat comparable to a unix-kernel call.
Before, the argument list is concatenated into in single string, with
all the arguments separated by blanks. This explains at least, that
arguments containing blanks _must_ be quoted somehow, otherwise
there would be no way to separate them again.

>   * regardless of who's involved, what *are* the rules?  Ie. what
>     characters in a command-line argument will cause that argument to
>     need quoting?  What are the allowed quote characters?  How
>     do you deal with arguments that contain the quote characters (ie.
>     what if I had a directory named
>          Program "Files"
>     ?
The following characters are not allowed in filenames and directory names:
\ / : * ? " < > |
This is not only imposed by the GUI or the shell (cmd.exe resp. command.com),
you get a windows error:

>>> open ("\"", "a")
Traceback (innermost last):
  File "<stdin>", line 1, in ?
IOError: [Errno 22] Invalid argument: '"'

>   * is any of this standard or documented, or does it vary from
>     application to application?
It is the responsibility of the application or the C-startup code in the
runtime library.

Assume the following program:
  #include <stdio.h>

  int main (int argc, char **argv)
      while (argc--)
          printf ("%s\n", *argv++);
compiled into test.exe.
Playing with this gives some interesting insights about the way the commandline
is parsed in the startup code of the runtime library:

  Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
  Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
  >>> import os
  >>> # First, a simple one
  >>> os.spawnv (os.P_WAIT, "test.exe", ["a", "b", "c"])
  0   #<-- This is the return code of os.spawnv().
  >>> # Would you have expected the following?
  >>> os.spawnv (os.P_WAIT, "test.exe", ["a b c", "d", "e"])
  >>> # Quoted an argument
  >>> os.spawnv (os.P_WAIT, "test.exe", ["\"a b c\"", "d", "e"])
  a b c
  >>> #Incompletely quoted argument
  >>> os.spawnv (os.P_WAIT, "test.exe", ["\"a b c", "d", "e"])
  a b c d e
  >>> os.spawnv (os.P_WAIT, "test.exe", ["a b c\"", "d", "e"])
  c d e
  >>> os.spawnv (os.P_WAIT, "test.exe", ["a b c\" \"d e"])
  c d
Interesting, isn't it?
It seems that
-I"c:\Program files" can be used as well as "-Ic:\Program Files",
as well as -Ic:\Program" "files
So these rules are trivial, assuming that VC uses this standard
argument parsing, my patch should work (except that every arg should
be _quote_if_needed().

     cmd = map (_quote_if_needed, cmd)
> (BTW Thomas, your patch was backwards.  This is easy to workaround -- I
> just have to run "patch -R" -- but it's easier to read patches if you
> say "diff <old_file> <new_file>" rather than "diff <new_file>
> <old_file>", which is what I suspect you did.)
Thanks for that, I noted it shortly after the mail was out :-)