{ This file is part of the Free Pascal run time library. Copyright (c) 2009 by Sven Barth FPC Pascal system unit for the WinNT API. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} unit System; interface {$IFNDEF FPC_DISABLE_MONITOR} {$DEFINE SYSTEM_HAS_FEATURE_MONITOR} {$ENDIF} {$define FPC_IS_SYSTEM} {$ifdef SYSTEMDEBUG} {$define SYSTEMEXCEPTIONDEBUG} {$endif SYSTEMDEBUG} {$ifdef cpui386} {$define Set_i386_Exception_handler} {$endif cpui386} {$define DISABLE_NO_THREAD_MANAGER} {$ifdef KMODE} // in KernelMode we need use a memory manager that just wraps the routines // provided by the NT Executive and allows to select whether we want to use // paged or non-paged (use sparely!) memory {$define HAS_MEMORYMANAGER} {$endif KMODE} { include system-independent routine headers } {$I systemh.inc} var CurrentPeb: Pointer; IsDeviceDriver: Boolean = False; const LineEnding = #13#10; LFNSupport = true; DirectorySeparator = '\'; DriveSeparator = '\'; ExtensionSeparator = '.'; PathSeparator = ';'; AllowDirectorySeparators : set of AnsiChar = ['\']; AllowDriveSeparators : set of AnsiChar = []; { FileNameCaseSensitive and FileNameCasePreserving are defined separately below!!! } maxExitCode = High(ErrorCode); MaxPathLen = High(Word); AllFilesMask = '*'; type PEXCEPTION_FRAME = ^TEXCEPTION_FRAME; TEXCEPTION_FRAME = record next : PEXCEPTION_FRAME; handler : pointer; end; var { C compatible arguments } argc: LongWord; argvw: PPWideChar; argv: PPAnsiChar; const { Default filehandles } UnusedHandle : THandle = 0; StdInputHandle : THandle = 0; StdOutputHandle : THandle = 0; StdErrorHandle : THandle = 0; {$ifndef kmode} type TDLL_Entry_Hook = procedure (dllparam : longint); const Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil; {$endif} const // NT is case sensitive FileNameCaseSensitive : boolean = true; FileNameCasePreserving: boolean = true; // todo: check whether this is really the case on NT CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *) sLineBreak = LineEnding; DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF; System_exception_frame : PEXCEPTION_FRAME =nil; implementation { include system independent routines } {$I system.inc} function fpc_pwidechar_length(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH'; { based on setup_arguments from Win32 RTL } procedure setup_arguments; var i,len, arglen, count : longint; argstart, pc,arg : pwidechar; pc2 : PAnsiChar; quote : Boolean; argvlen : longint; params : PRTLUserProcessParameters; procedure allocarg(idx,len:longint); var oldargvlen : longint; begin if idx>=argvlen then begin oldargvlen:=argvlen; argvlen:=(idx+8) and (not 7); sysreallocmem(argvw,argvlen*sizeof(pointer)); fillchar(argvw[oldargvlen],(argvlen-oldargvlen)*sizeof(pointer),0); end; { use realloc to reuse already existing memory } { always allocate, even if length is zero, since } { the arg. is still present! } sysreallocmem(argvw[idx],len*sizeof(widechar)+2); end; begin { create commandline, it starts with the executed filename which is argvw[0] } { NativeNT passes inside the PEB which is passed on startup } argvw:=nil; argv:=nil; argvlen:=0; params:=PSimplePEB(CurrentPEB)^.ProcessParameters; ArgLen:=params^.ImagePathName.Length + 1; allocarg(0,arglen); move(params^.ImagePathName.Buffer^,argvw[0]^,arglen*sizeof(widechar)+1); { Setup cmdline variable } { cmdline is a PAnsiChar, but NT uses PWideChar... don't set cmdline for now } {$message warning 'cmdline is not set'} // cmdline:=GetCommandLine; { the first argument isn't the image file name, so start at 1 } count:=1; { process arguments } pc:=params^.CommandLine.Buffer; while pc^<>#0 do begin { skip leading spaces } while (Ord(pc^) >= 1) and (Ord(pc^) <= 32) {pc^ in [#1..#32]} do inc(pc); if pc^=#0 then break; { calc argument length } quote:=False; argstart:=pc; arglen:=0; while pc^<>#0 do begin case pc^ of #1..#32 : begin if quote then inc(arglen) else break; end; '"' : if pc[1]<>'"' then quote := not quote else inc(pc); else inc(arglen); end; inc(pc); end; { copy argument } { Don't copy the first one, it is already there.} If Count<>0 then begin allocarg(count,arglen); quote:=False; pc:=argstart; arg:=argvw[count]; while (pc^<>#0) do begin case pc^ of #1..#32 : begin if quote then begin arg^:=pc^; inc(arg); end else break; end; '"' : if pc[1]<>'"' then quote := not quote else inc(pc); else begin arg^:=pc^; inc(arg); end; end; inc(pc); end; arg^:=#0; end; inc(count); end; { get argc } argc:=count; { free unused memory, leaving a nil entry at the end } sysreallocmem(argvw,(count+1)*sizeof(pointer)); argvw[count] := nil; { now we need to fill argv with UTF8 encoded arguments } sysreallocmem(argv,(count+1)*sizeof(pointer)); fillchar(argv^,(count+1)*sizeof(pointer),0); for i := 0 to count - 1 do begin len := fpc_pwidechar_length(argvw[i]); pc := argvw[i]; argv[i]:=nil; sysreallocmem(argv[i],len+1); pc2 := argv[i]; {$message warning 'Use UnicodeToUTF8 for argument conversion'} while Ord(pc^) > 0 do begin if word(pc^) < 127 then pc2^ := AnsiChar(word(pc^)) else pc2^ := '?'; Inc(pc); Inc(pc2); end; pc2^ := #0; end; end; function paramcount : longint; begin paramcount := argc - 1; end; function paramstr(l : longint) : shortstring; begin if (l>=0) and (l Nil then Dll_Thread_Attach_Hook(aDllParam); end; DLL_THREAD_DETACH: begin if Dll_Thread_Detach_Hook <> Nil then Dll_Thread_Detach_Hook(aDllParam); end; DLL_PROCESS_DETACH: begin if Dll_Process_Detach_Hook <> Nil then Dll_Process_Detach_Hook(aDllParam); // finalize units internal_do_exit; end; end; end; procedure FPCProcessStartup(aArgument: Pointer);[public, alias: 'FPC_ProcessStartup']; begin IsConsole := True; IsLibrary := False; CurrentPeb := aArgument; PASCALMAIN; system_exit; end; {$endif} {$ifdef kmode} // Kernel Mode Entry Point function NtDriverEntry( aDriverObject: Pointer; aRegistryPath: Pointer ): LongInt; stdcall; [public, alias: '_NtDriverEntry']; begin NtDriverEntry := FPCDriverStartup(aDriverObject, aRegistryPath); end; {$else} // User Mode Entry Points procedure NtProcessStartup( aArgument: Pointer ); stdcall; [public, alias: '_NtProcessStartup']; begin FPCProcessStartup(aArgument); end; function DLLMainStartup( aHInstance: Pointer; aDLLReason, aDLLParam: LongInt ): LongBool; stdcall; [public, alias: '_DLLMainStartup']; begin DLLMainStartup := FPCDLLEntry(aHInstance, aDLLReason, aDLLParam); end; {$endif} procedure SysInitStdIO; begin { This function is currently only called if the RTL is compiled for Usermode; one could think about adding a text driver that outputs using DbgPrint } {$ifndef KMODE} with PSimplePEB(CurrentPEB)^.ProcessParameters^ do begin StdInputHandle := StandardInput; StdOutputHandle := StandardOutput; StdErrorHandle := StandardError; end; if StdInputHandle <> 0 then OpenStdIO(Input, fmInput, StdInputHandle) else Assign(Input, ''); if StdOutputHandle <> 0 then begin OpenStdIO(Output, fmOutput, StdOutputHandle); OpenStdIO(StdOut, fmOutput, StdOutputHandle); end else begin Assign(Output, ''); Assign(StdOut, ''); end; if StdErrorHandle <> 0 then begin OpenStdIO(ErrOutput, fmOutput, StdErrorHandle); OpenStdIO(StdErr, fmOutput, StdErrorHandle); end else begin Assign(ErrOutput, ''); Assign(StdErr, ''); end; {$endif} end; function GetProcessID: SizeUInt; begin {$ifdef kmode} // it might be that we can detect the user process that called us, // but that needs to be checked... so for now just return 0 Result := 0; {$else} Result := NtCurrentTEB^.ClientID.UniqueProcess; {$endif} end; begin {$if not defined(KMODE) and not defined(HAS_MEMORYMANAGER)} { Setup heap } InitHeap; {$endif ndef KMODE and ndef HAS_MEMORYMANAGER} SysInitExceptions; { we do not use winlike widestrings and also the RTL can't be compiled with 2.2, so we can savely use the UnicodeString manager only. } initunicodestringmanager; {$ifndef KMODE} SysInitStdIO; { Arguments } setup_arguments; {$endif} InOutRes := 0; InitSystemThreads; errno := 0; end.