|
@@ -213,149 +213,144 @@ end;
|
|
|
Parameter Handling
|
|
|
*****************************************************************************}
|
|
|
|
|
|
-var
|
|
|
- argvw: PPWideChar;
|
|
|
+function WideCharToMultiByte(CodePage:UINT; dwFlags:DWORD; lpWideCharStr:PWideChar; cchWideChar:longint;
|
|
|
+ lpMultiByteStr:LPSTR;cchMultiByte:longint; lpDefaultChar:PAnsiChar; lpUsedDefaultChar:PLongBool):longint; stdcall; external 'kernel32' name 'WideCharToMultiByte';
|
|
|
+function GetCommandLineA : pansichar; stdcall;external KernelDLL name 'GetCommandLineA';
|
|
|
|
|
|
-procedure setup_arguments;
|
|
|
+type
|
|
|
+ { nargs — argument count (without first and without null terminator),
|
|
|
+ nchars — total widechar count in arguments (with null terminators),
|
|
|
+ nachars — total ansichar count in arguments (with null terminators), counted only if args = chars = nil. }
|
|
|
+ ParseCommandLineResult = record
|
|
|
+ nargs, nchars, nachars: SizeInt;
|
|
|
+ end;
|
|
|
+
|
|
|
+function ParseCommandLine(cmdLine: PWideChar; args: PPWideChar; chars: PWideChar): ParseCommandLineResult;
|
|
|
var
|
|
|
- CmdLineW, pw: PWideChar;
|
|
|
- c: WideChar;
|
|
|
- buf: array[0..MaxPathLen] of WideChar;
|
|
|
- i, len, argvw_size: longint;
|
|
|
- s: RawByteString;
|
|
|
- quote: char;
|
|
|
+ argStartInCmdLine: PWideChar;
|
|
|
+ nArgChars, nArgCharsPlusQuotes, nachars: SizeInt;
|
|
|
+ c, quote: WideChar;
|
|
|
+ skippingFirstArg: boolean;
|
|
|
begin
|
|
|
- // 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;
|
|
|
+ argStartInCmdLine:=cmdLine;
|
|
|
+ nArgChars:=0;
|
|
|
+ FillChar(result,sizeof(result),0);
|
|
|
+ skippingFirstArg:=true;
|
|
|
quote:=' ';
|
|
|
- while True do
|
|
|
- begin
|
|
|
- c:=CmdLineW^;
|
|
|
- Inc(CmdLineW);
|
|
|
- case c of
|
|
|
- #0..#32:
|
|
|
- if (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;
|
|
|
- '"':
|
|
|
+ repeat
|
|
|
+ c:=cmdLine^;
|
|
|
+ inc(cmdLine);
|
|
|
+ case c of
|
|
|
+ #0..#32:
|
|
|
+ if (quote=' ') or (c=#0) then
|
|
|
begin
|
|
|
- if quote<>'''' then
|
|
|
+ if (nArgChars<>0) then
|
|
|
begin
|
|
|
- if CmdLineW^<>'"' then
|
|
|
- begin
|
|
|
- if quote='"' then
|
|
|
- quote:=' '
|
|
|
- else
|
|
|
- quote:='"';
|
|
|
- continue;
|
|
|
- end
|
|
|
+ // End of an argument found
|
|
|
+ if Assigned(chars) then
|
|
|
+ chars[result.nchars]:=#0
|
|
|
else
|
|
|
- Inc(CmdLineW);
|
|
|
- end;
|
|
|
- end;
|
|
|
- '''':
|
|
|
- begin
|
|
|
- if quote<>'"' then
|
|
|
- begin
|
|
|
- if CmdLineW^<>'''' then
|
|
|
begin
|
|
|
- if quote='''' then
|
|
|
- quote:=' '
|
|
|
- else
|
|
|
- quote:='''';
|
|
|
- continue;
|
|
|
- end
|
|
|
- else
|
|
|
- Inc(CmdLineW);
|
|
|
+ { Number of widechars in the argument, including quotes: cmdLine - 1 - argStartInCmdLine. Avoid implicit signed div. }
|
|
|
+ nArgCharsPlusQuotes:=SizeUint(pointer(cmdLine-1)-pointer(argStartInCmdLine)) div sizeof(widechar);
|
|
|
+ nachars:=
|
|
|
+ { Count of ANSI characters in the argument, including quotes. }
|
|
|
+ WideCharToMultiByte(DefaultSystemCodePage, 0, argStartInCmdLine, nArgCharsPlusQuotes, nil, 0, nil, nil)
|
|
|
+ { Assume each quote is 1 ANSI character. Subtract the amount of quotes. }
|
|
|
+ -(nArgCharsPlusQuotes-nArgChars);
|
|
|
+ if nachars<0 then
|
|
|
+ nachars:=0; { Paranoia WideCharToMultiByte fail. }
|
|
|
+ inc(result.nachars, nachars+1{null terminator});
|
|
|
+ end;
|
|
|
+ inc(result.nchars); { Null terminator. }
|
|
|
+ nArgChars:=0;
|
|
|
end;
|
|
|
+ skippingFirstArg:=false;
|
|
|
+ if c = #0 then
|
|
|
+ break;
|
|
|
+ continue; // Skip whitespace
|
|
|
end;
|
|
|
- end;
|
|
|
- // Ignore the first argument, it is already copied
|
|
|
- if argc <> 0 then
|
|
|
- begin
|
|
|
- // Copy the argument's AnsiChar
|
|
|
- pw^:=c;
|
|
|
- Inc(pw);
|
|
|
- end;
|
|
|
+ '"', '''':
|
|
|
+ if (c='"') and (quote<>'''') or (c='''') and (quote<>'"') then
|
|
|
+ if cmdLine^<>c then
|
|
|
+ begin
|
|
|
+ if quote=c then
|
|
|
+ quote:=' '
|
|
|
+ else
|
|
|
+ quote:=c;
|
|
|
+ continue;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ inc(cmdLine);
|
|
|
end;
|
|
|
+ if skippingFirstArg then
|
|
|
+ continue;
|
|
|
+ if nArgChars=0 then
|
|
|
+ begin
|
|
|
+ if Assigned(args) then
|
|
|
+ args[result.nargs]:=chars+result.nchars;
|
|
|
+ inc(result.nargs);
|
|
|
+ argStartInCmdLine:=cmdLine-1;
|
|
|
+ end;
|
|
|
+ if Assigned(chars) then
|
|
|
+ chars[result.nchars]:=c;
|
|
|
+ inc(nArgChars);
|
|
|
+ inc(result.nchars);
|
|
|
+ until false;
|
|
|
+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]));
|
|
|
+var
|
|
|
+ argvw: PPWideChar; { Start of the memory region. Should very preferably be private as argv can (and WILL, by LazUTF8) be changed from outside. }
|
|
|
|
|
|
- // Construct the ansi argv
|
|
|
- argv:=SysGetMem((argc + 1)*SizeOf(pointer));
|
|
|
- for i:=0 to argc - 1 do
|
|
|
+procedure setup_arguments;
|
|
|
+var
|
|
|
+ CmdLineW, wchars: PWideChar;
|
|
|
+ buf: array[0..MaxPathLen] of WideChar;
|
|
|
+ iarg, nArg0W, nArg0A: SizeInt;
|
|
|
+ pc: ParseCommandLineResult;
|
|
|
+ achars, acharse: PAnsiChar;
|
|
|
+begin
|
|
|
+ CmdLine:=GetCommandLineA;
|
|
|
+ CmdLineW:=GetCommandLineW;
|
|
|
+ nArg0W:=GetModuleFileNameW(0, PWideChar(buf), Length(buf));
|
|
|
+ nArg0A:=WideCharToMultiByte(DefaultSystemCodePage, 0, PWideChar(buf), nArg0W, nil, 0, nil, nil);
|
|
|
+ pc:=ParseCommandLine(CmdLineW, nil, nil);
|
|
|
+ argc:=pc.nargs+1;
|
|
|
+
|
|
|
+ { Memory region layout:
|
|
|
+ argc × PWideChar: argvw (internal, not terminated with nil).
|
|
|
+ (argc + 1) × PAnsiChar: argv (terminated with nil).
|
|
|
+ Nw × widechar: chars for argvw.
|
|
|
+ Na × ansichar: chars for argv. }
|
|
|
+ argvw:=nil;
|
|
|
+ repeat { First iteration calculates region size (by adding to nil). Second iteration calculates pointers to region parts (by adding to region start). }
|
|
|
+ argv:=PPAnsiChar(argvw+argc);
|
|
|
+ wchars:=PWideChar(argv+argc+1);
|
|
|
+ achars:=PAnsiChar(wchars+nArg0W+1+pc.nchars);
|
|
|
+ if Assigned(argvw) then
|
|
|
+ break;
|
|
|
+ argvw:=SysGetMem(PtrUint(achars+nArg0A+1+pc.nachars));
|
|
|
+ until false;
|
|
|
+
|
|
|
+ Move(PWideChar(buf)^, wchars^, nArg0W*sizeof(widechar));
|
|
|
+ wchars[nArg0W]:=#0;
|
|
|
+ argvw[0]:=wchars;
|
|
|
+ ParseCommandLine(CmdLineW, argvw+1, wchars+nArg0W+1);
|
|
|
+
|
|
|
+ { Convert argvw to argv. }
|
|
|
+ acharse:=achars+nArg0A+1+pc.nachars;
|
|
|
+ for iarg:=0 to pc.nargs do
|
|
|
begin
|
|
|
- // Convert argvw[i] to argv[i]
|
|
|
- s:=ansistring(argvw[i]);
|
|
|
- len:=Length(s) + 1;
|
|
|
- argv[i]:=SysGetMem(len);
|
|
|
- Move(s[1], argv[i]^, len);
|
|
|
+ argv[iarg]:=achars;
|
|
|
+ inc(achars, WideCharToMultiByte(DefaultSystemCodePage, 0, argvw[iarg], length(argvw[iarg]), achars, acharse-achars, nil, nil)+1);
|
|
|
+ achars[-1]:=#0;
|
|
|
end;
|
|
|
- // argv is terminated by nil
|
|
|
argv[argc]:=nil;
|
|
|
end;
|
|
|
|
|
|
procedure finalize_arguments;
|
|
|
-var
|
|
|
- i: longint;
|
|
|
begin
|
|
|
- SysFreeMem(CmdLine);
|
|
|
- // Free unicode arguments
|
|
|
- SysFreeMem(argvw[0]);
|
|
|
SysFreeMem(argvw);
|
|
|
- // Free ansi arguments
|
|
|
- for i:=0 to argc - 1 do
|
|
|
- SysFreeMem(argv[i]);
|
|
|
- SysFreeMem(argv);
|
|
|
end;
|
|
|
|
|
|
function paramcount : longint;
|