Browse Source

* Windows: Do not use CommandLineToArgvW for parsing command line parameters, because it treats \" as an escape sequence for the double quote.
- Implemented parsing of the command line which is backward compatible.
- Do not use GetCommandLineA.

git-svn-id: trunk@45227 -

yury 5 years ago
parent
commit
187cc46b32
2 changed files with 87 additions and 20 deletions
  1. 0 6
      rtl/win/sysos.inc
  2. 87 14
      rtl/win/syswin.inc

+ 0 - 6
rtl/win/sysos.inc

@@ -253,10 +253,6 @@ type
 
   function GetCommandLineW : pwidechar;
      stdcall;external KernelDLL name 'GetCommandLineW';
-  function GetCommandLineA : pansichar;
-     stdcall;external KernelDLL name 'GetCommandLineA';
-  function CommandLineToArgvW(lpCmdLine: PWideChar; out pNumArgs: longint): PPWideChar;
-     stdcall; external 'shell32.dll' name 'CommandLineToArgvW';
 
   function GetCurrentProcessId:DWORD;
     stdcall; external KernelDLL name 'GetCurrentProcessId';
@@ -361,8 +357,6 @@ type
      stdcall; external 'oleaut32.dll' name 'SysFreeString';
    function SysReAllocStringLen(var bstr:pointer;psz: pointer;
      len:dword): Integer; stdcall;external 'oleaut32.dll' name 'SysReAllocStringLen';
-   function GlobalFree(hMem: pointer): pointer;
-     stdcall; external KernelDLL name 'GlobalFree';
 {$endif WINCE}
 
    Procedure Errno2InOutRes(oserror: longword);

+ 87 - 14
rtl/win/syswin.inc

@@ -218,16 +218,91 @@ var
 
 procedure setup_arguments;
 var
+  CmdLineW, pw: PWideChar;
+  c: WideChar;
   buf: array[0..MaxPathLen] of WideChar;
-  i, len: longint;
-  s: ansistring;
+  i, len, argvw_size: longint;
+  s: RawByteString;
+  quote: boolean;
 begin
-  // Get argvw
-  argvw:=CommandLineToArgvW(GetCommandLineW, argc);
-  // Get the full module name for argvw[0]
-  len:=(GetModuleFileNameW(0, @buf, Length(buf)) + 1)*SizeOf(WideChar);
-  argvw[0]:=SysGetMem(len);
-  Move(buf, argvw[0]^, len);
+  // Get the unicode command line
+  CmdLineW:=GetCommandLineW;
+  // Create the ansi command line
+  s:=ansistring(CmdLineW);
+  len:=Length(s) + 1;
+  CmdLine:=SysGetMem(len);
+  Move(PAnsiChar(s)^, CmdLine^, len);
+  // Alloc initial space for argvw
+  if CmdLineW^ = #0 then
+    argvw_size:=2
+  else
+    argvw_size:=10;
+  argvw:=SysGetMem(argvw_size*SizeOf(pointer));
+  // Get the full module name to be used as the first argument
+  len:=GetModuleFileNameW(0, @buf, Length(buf));
+  // Alloc maximum possible space for all arguments
+  pw:=SysGetMem((len + IndexWord(CmdLineW^, High(longint), 0) + 2)*SizeOf(WideChar));
+  // Copy the module name as the first argument. It will be nil terminated later
+  Move(buf, pw^, len*SizeOf(WideChar));
+  argvw[0]:=pw;
+  Inc(pw, len);
+  // Parse the command line
+  argc:=0;
+  quote:=False;
+  while True do
+    begin
+      c:=CmdLineW^;
+      Inc(CmdLineW);
+      case c of
+        #0..#32:
+          if not quote or (c = #0) then
+            begin
+              // Are there any chars of an argument?
+              if argvw[argc] <> pw then
+                begin
+                  // End of an argument found
+                  pw^:=#0;
+                  Inc(pw);
+                  Inc(argc);
+                  if argc = argvw_size then
+                    begin
+                      // Increase the argvw space
+                      Inc(argvw_size, argvw_size shr 1);
+                      SysReAllocMem(argvw, argvw_size*SizeOf(pointer));
+                    end;
+                  if c = #0 then
+                    break;
+                  argvw[argc]:=pw;
+                  continue;
+                end
+              else
+                if c = #0 then
+                  break
+                else
+                  continue; // Skip whitespace
+            end;
+        '"':
+          begin
+            quote:=not quote;
+            continue;
+          end;
+      end;
+      // Ignore the first argument, it is already copied
+      if argc <> 0 then
+        begin
+          // Copy the argument's char
+          pw^:=c;
+          Inc(pw);
+        end;
+    end;
+
+  // Finalization
+  // argvw is terminated by nil
+  argvw[argc]:=nil;
+  // Trim the memory
+  SysReAllocMem(argvw, (argc + 1)*SizeOf(pointer));
+  SysReAllocMem(argvw[0], ptruint(pw) - ptruint(argvw[0]));
+
   // Construct the ansi argv
   argv:=SysGetMem((argc + 1)*SizeOf(pointer));
   for i:=0 to argc - 1 do
@@ -240,19 +315,17 @@ begin
     end;
   // argv is terminated by nil
   argv[argc]:=nil;
-  // Get the ansi CmdLine
-  CmdLine:=GetCommandLineA;
 end;
 
 procedure finalize_arguments;
 var
   i: longint;
 begin
-  // Free the module name
+  SysFreeMem(CmdLine);
+  // Free unicode arguments
   SysFreeMem(argvw[0]);
-  // Use GlobalFree to free the buffer returned by CommandLineToArgvW
-  GlobalFree(argvw);
-  // Free argv
+  SysFreeMem(argvw);
+  // Free ansi arguments
   for i:=0 to argc - 1 do
     SysFreeMem(argv[i]);
   SysFreeMem(argv);