fpc/rtl/os2/sysutils.pp
2023-08-04 12:28:46 +02:00

1095 lines
28 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 1999-2000 by Florian Klaempfl
member of the Free Pascal development team
Sysutils unit for OS/2
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.
**********************************************************************}
{$IFNDEF FPC_DOTTEDUNITS}
unit sysutils;
{$ENDIF FPC_DOTTEDUNITS}
interface
{$MODE objfpc}
{$MODESWITCH OUT}
{$IFDEF UNICODERTL}
{$MODESWITCH UNICODESTRINGS}
{$ELSE}
{$H+}
{$ENDIF}
{$modeswitch typehelpers}
{$modeswitch advancedrecords}
{$DEFINE HAS_SLEEP}
{$DEFINE HAS_OSERROR}
{ used OS file system APIs use ansistring }
{$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL}
{ OS has an ansistring/single byte environment variable API }
{$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
{ OS has an ansistring/single byte API for executing other processes }
{$DEFINE EXECUTEPROCUNI}
{ Include platform independent interface part }
{$i sysutilh.inc}
implementation
{$IFDEF FPC_DOTTEDUNITS}
uses
System.SysConst, OS2Api.doscalls;
{$ELSE FPC_DOTTEDUNITS}
uses
sysconst, DosCalls;
{$ENDIF FPC_DOTTEDUNITS}
type
(* Necessary here due to a different definition of TDateTime in DosCalls. *)
TDateTime = System.TDateTime;
threadvar
LastOSError: cardinal;
{$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *)
{$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *)
{$DEFINE FPC_FEXPAND_GETENV_PCHAR}
{$DEFINE HAS_GETTICKCOUNT}
{$DEFINE HAS_GETTICKCOUNT64}
{$DEFINE HAS_LOCALTIMEZONEOFFSET}
{ Include platform independent implementation part }
{$i sysutils.inc}
{****************************************************************************
File Functions
****************************************************************************}
const
ofRead = $0000; {Open for reading}
ofWrite = $0001; {Open for writing}
ofReadWrite = $0002; {Open for reading/writing}
doDenyRW = $0010; {DenyAll (no sharing)}
faCreateNew = $00010000; {Create if file does not exist}
faOpenReplace = $00040000; {Truncate if file exists}
faCreate = $00050000; {Create if file does not exist, truncate otherwise}
FindResvdMask = $00003737 {Allowed bits for DosFindFirst parameter Attribute}
and $000000FF; {combined with a mask for allowed attributes only}
function FileOpen (const FileName: rawbytestring; Mode: integer): THandle;
Var
SystemFileName: RawByteString;
Handle: THandle;
Rc, Action: cardinal;
begin
SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
(* DenyReadWrite if sharing not specified. *)
if (Mode and 112 = 0) or (Mode and 112 > 64) then
Mode := Mode or doDenyRW;
Rc:=Sys_DosOpenL(PAnsiChar (SystemFileName), Handle, Action, 0, 0, 1, Mode, nil);
If Rc=0 then
FileOpen:=Handle
else
begin
FileOpen:=feInvalidHandle; //FileOpen:=-RC;
//should return feInvalidHandle(=-1) if fail, other negative returned value are no more errors
OSErrorWatch (RC);
end;
end;
function FileCreate (const FileName: RawByteString): THandle;
begin
FileCreate := FileCreate (FileName, doDenyRW, 777); (* Sharing to DenyAll *)
end;
function FileCreate (const FileName: RawByteString; Rights: integer): THandle;
begin
FileCreate := FileCreate (FileName, doDenyRW, Rights);
(* Sharing to DenyAll *)
end;
function FileCreate (const FileName: RawByteString; ShareMode: integer;
Rights: integer): THandle;
var
SystemFileName: RawByteString;
Handle: THandle;
RC, Action: cardinal;
begin
SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
ShareMode := ShareMode and 112;
(* Sharing to DenyAll as default in case of values not allowed by OS/2. *)
if (ShareMode = 0) or (ShareMode > 64) then
ShareMode := doDenyRW;
RC := Sys_DosOpenL (PAnsiChar (SystemFileName), Handle, Action, 0, 0, $12,
faCreate or ofReadWrite or ShareMode, nil);
if RC = 0 then
FileCreate := Handle
else
begin
FileCreate := feInvalidHandle;
OSErrorWatch (RC);
end;
End;
function FileRead (Handle: THandle; Out Buffer; Count: longint): longint;
Var
T: cardinal;
RC: cardinal;
begin
RC := DosRead (Handle, Buffer, Count, T);
if RC = 0 then
FileRead := longint (T)
else
begin
FileRead := -1;
OSErrorWatch (RC);
end;
end;
function FileWrite (Handle: THandle; const Buffer; Count: longint): longint;
Var
T: cardinal;
RC: cardinal;
begin
RC := DosWrite (Handle, Buffer, Count, T);
if RC = 0 then
FileWrite := longint (T)
else
begin
FileWrite := -1;
OSErrorWatch (RC);
end;
end;
function FileSeek (Handle: THandle; FOffset, Origin: longint): longint;
var
NPos: int64;
RC: cardinal;
begin
RC := Sys_DosSetFilePtrL (Handle, FOffset, Origin, NPos);
if (RC = 0) and (NPos < high (longint)) then
FileSeek:= longint (NPos)
else
begin
FileSeek:=-1;
OSErrorWatch (RC);
end;
end;
function FileSeek (Handle: THandle; FOffset: Int64; Origin: Longint): Int64;
var
NPos: int64;
RC: cardinal;
begin
RC := Sys_DosSetFilePtrL (Handle, FOffset, Origin, NPos);
if RC = 0 then
FileSeek:= NPos
else
begin
FileSeek:=-1;
OSErrorWatch (RC);
end;
end;
procedure FileClose (Handle: THandle);
var
RC: cardinal;
begin
RC := DosClose (Handle);
if RC <> 0 then
OSErrorWatch (RC);
end;
function FileTruncate (Handle: THandle; Size: Int64): boolean;
var
RC: cardinal;
begin
RC := Sys_DosSetFileSizeL(Handle, Size);
FileTruncate := RC = 0;
if RC = 0 then
FileSeek(Handle, 0, 2)
else
OSErrorWatch (RC);
end;
function FileAge (const FileName: RawByteString): Int64;
var Handle: longint;
begin
Handle := FileOpen (FileName, 0);
if Handle <> -1 then
begin
Result := FileGetDate (Handle);
FileClose (Handle);
end
else
Result := -1;
end;
function FileGetSymLinkTarget(const FileName: RawByteString; out SymLinkRec: TRawbyteSymLinkRec): Boolean;
begin
Result := False;
end;
function FileExists (const FileName: RawByteString; FollowLink : Boolean): boolean;
var
L: longint;
begin
{ no need to convert to DefaultFileSystemEncoding, FileGetAttr will do that }
if FileName = '' then
Result := false
else
begin
L := FileGetAttr (FileName);
Result := (L >= 0) and (L and (faDirectory or faVolumeID) = 0);
(* Neither VolumeIDs nor directories are files. *)
end;
end;
type PSearchRec = ^TSearchRec;
Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
var SR: PSearchRec;
FStat: PFileFindBuf3L;
Count: cardinal;
Err: cardinal;
I: cardinal;
SystemEncodedPath: RawByteString;
begin
SystemEncodedPath := ToSingleByteFileSystemEncodedFileName(Path);
New (FStat);
Rslt.FindHandle := THandle ($FFFFFFFF);
Count := 1;
if FSApi64 then
Err := DosFindFirst (PAnsiChar (SystemEncodedPath), Rslt.FindHandle,
Attr and FindResvdMask, FStat, SizeOf (FStat^), Count, ilStandardL)
else
Err := DosFindFirst (PAnsiChar (SystemEncodedPath), Rslt.FindHandle,
Attr and FindResvdMask, FStat, SizeOf (FStat^), Count, ilStandard);
if Err <> 0 then
OSErrorWatch (Err)
else if Count = 0 then
Err := 18;
InternalFindFirst := -Err;
if Err = 0 then
begin
Rslt.ExcludeAttr := 0;
Rslt.Time := cardinal (FStat^.DateLastWrite) shl 16 + FStat^.TimeLastWrite;
if FSApi64 then
begin
Rslt.Size := FStat^.FileSize;
Name := FStat^.Name;
Rslt.Attr := FStat^.AttrFile;
end
else
begin
Rslt.Size := PFileFindBuf3 (FStat)^.FileSize;
Name := PFileFindBuf3 (FStat)^.Name;
Rslt.Attr := PFileFindBuf3 (FStat)^.AttrFile;
end;
SetCodePage (Name, DefaultFileSystemCodePage, false);
end
else
InternalFindClose(Rslt.FindHandle);
Dispose (FStat);
end;
Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
var
SR: PSearchRec;
FStat: PFileFindBuf3L;
Count: cardinal;
Err: cardinal;
begin
New (FStat);
Count := 1;
Err := DosFindNext (Rslt.FindHandle, FStat, SizeOf (FStat^), Count);
if Err <> 0 then
OSErrorWatch (Err)
else if Count = 0 then
Err := 18;
InternalFindNext := -Err;
if Err = 0 then
begin
Rslt.ExcludeAttr := 0;
Rslt.Time := cardinal (FStat^.DateLastWrite) shl 16 + FStat^.TimeLastWrite;
if FSApi64 then
begin
Rslt.Size := FStat^.FileSize;
Name := FStat^.Name;
Rslt.Attr := FStat^.AttrFile;
end
else
begin
Rslt.Size := PFileFindBuf3 (FStat)^.FileSize;
Name := PFileFindBuf3 (FStat)^.Name;
Rslt.Attr := PFileFindBuf3 (FStat)^.AttrFile;
end;
SetCodePage (Name, DefaultFileSystemCodePage, false);
end;
Dispose (FStat);
end;
Procedure InternalFindClose(var Handle: THandle);
var
SR: PSearchRec;
RC: cardinal;
begin
RC := DosFindClose (Handle);
Handle := 0;
if RC <> 0 then
OSErrorWatch (RC);
end;
function FileGetDate (Handle: THandle): Int64;
var
FStat: TFileStatus3;
Time: Longint;
RC: cardinal;
begin
RC := DosQueryFileInfo(Handle, ilStandard, @FStat, SizeOf(FStat));
if RC = 0 then
begin
Time := FStat.TimeLastWrite + dword (FStat.DateLastWrite) shl 16;
if Time = 0 then
Time := FStat.TimeCreation + dword (FStat.DateCreation) shl 16;
end else
begin
Time:=0;
OSErrorWatch (RC);
end;
FileGetDate:=Time;
end;
function FileSetDate (Handle: THandle; Age: Int64): longint;
var
FStat: PFileStatus3;
RC: cardinal;
begin
New (FStat);
RC := DosQueryFileInfo (Handle, ilStandard, FStat, SizeOf (FStat^));
if RC <> 0 then
begin
FileSetDate := -1;
OSErrorWatch (RC);
end
else
begin
FStat^.DateLastAccess := Hi (dword (Age));
FStat^.DateLastWrite := Hi (dword (Age));
FStat^.TimeLastAccess := Lo (dword (Age));
FStat^.TimeLastWrite := Lo (dword (Age));
RC := DosSetFileInfo (Handle, ilStandard, FStat, SizeOf (FStat^));
if RC <> 0 then
begin
FileSetDate := -1;
OSErrorWatch (RC);
end
else
FileSetDate := 0;
end;
Dispose (FStat);
end;
function FileGetAttr (const FileName: RawByteString): longint;
var
FS: PFileStatus3;
SystemFileName: RawByteString;
RC: cardinal;
begin
SystemFileName:=ToSingleByteFileSystemEncodedFileName(Filename);
New(FS);
RC := DosQueryPathInfo(PAnsiChar (SystemFileName), ilStandard, FS, SizeOf(FS^));
if RC = 0 then
Result := FS^.AttrFile
else
begin
Result := - longint (RC);
OSErrorWatch (RC);
end;
Dispose(FS);
end;
function FileSetAttr (const Filename: RawByteString; Attr: longint): longint;
Var
FS: PFileStatus3;
SystemFileName: RawByteString;
RC: cardinal;
Begin
SystemFileName:=ToSingleByteFileSystemEncodedFileName(Filename);
New(FS);
RC := DosQueryPathInfo (PAnsiChar (SystemFileName), ilStandard, FS, SizeOf (FS^));
if RC = 0 then
begin
FS^.AttrFile:=Attr;
RC := DosSetPathInfo(PAnsiChar (SystemFileName), ilStandard, FS, SizeOf(FS^), 0);
if RC <> 0 then
OSErrorWatch (RC);
end
else
OSErrorWatch (RC);
Result := - longint (RC);
Dispose(FS);
end;
function DeleteFile (const FileName: RawByteString): boolean;
var
SystemFileName: RawByteString;
RC: cardinal;
Begin
SystemFileName:=ToSingleByteFileSystemEncodedFileName(Filename);
RC := DosDelete (PAnsiChar (SystemFileName));
if RC <> 0 then
begin
Result := false;
OSErrorWatch (RC);
end
else
Result := true;
End;
function RenameFile (const OldName, NewName: RawByteString): boolean;
var
OldSystemFileName, NewSystemFileName: RawByteString;
RC: cardinal;
Begin
OldSystemFileName:=ToSingleByteFileSystemEncodedFileName(OldName);
NewSystemFileName:=ToSingleByteFileSystemEncodedFileName(NewName);
RC := DosMove (PAnsiChar (OldSystemFileName), PAnsiChar (NewSystemFileName));
if RC <> 0 then
begin
Result := false;
OSErrorWatch (RC);
end
else
Result := true;
End;
{****************************************************************************
Disk Functions
****************************************************************************}
function DiskFree (Drive: byte): int64;
var FI: TFSinfo;
RC: cardinal;
begin
{In OS/2, we use the filesystem information.}
RC := DosQueryFSInfo (Drive, 1, FI, SizeOf (FI));
if RC = 0 then
DiskFree := int64 (FI.Free_Clusters) *
int64 (FI.Sectors_Per_Cluster) * int64 (FI.Bytes_Per_Sector)
else
begin
DiskFree := -1;
OSErrorWatch (RC);
end;
end;
function DiskSize (Drive: byte): int64;
var FI: TFSinfo;
RC: cardinal;
begin
{In OS/2, we use the filesystem information.}
RC := DosQueryFSinfo (Drive, 1, FI, SizeOf (FI));
if RC = 0 then
DiskSize := int64 (FI.Total_Clusters) *
int64 (FI.Sectors_Per_Cluster) * int64 (FI.Bytes_Per_Sector)
else
begin
DiskSize := -1;
OSErrorWatch (RC);
end;
end;
function DirectoryExists (const Directory: RawByteString; FollowLink : Boolean): boolean;
var
L: longint;
begin
{ no need to convert to DefaultFileSystemEncoding, FileGetAttr will do that }
if Directory = '' then
Result := false
else
begin
if ((Length (Directory) = 2) or
(Length (Directory) = 3) and
(Directory [3] in AllowDirectorySeparators)) and
(Directory [2] in AllowDriveSeparators) and
(UpCase (Directory [1]) in ['A'..'Z']) then
(* Checking attributes for 'x:' is not possible but for 'x:.' it is. *)
L := FileGetAttr (Directory + '.')
else if (Directory [Length (Directory)] in AllowDirectorySeparators) and
(Length (Directory) > 1) and
(* Do not remove '\' in '\\' (invalid path, possibly broken UNC path). *)
not (Directory [Length (Directory) - 1] in AllowDirectorySeparators) then
L := FileGetAttr (Copy (Directory, 1, Length (Directory) - 1))
else
L := FileGetAttr (Directory);
Result := (L > 0) and (L and faDirectory = faDirectory);
end;
end;
{****************************************************************************
Time Functions
****************************************************************************}
{$DEFINE HAS_DUAL_TZHANDLING}
{$I tzenv.inc}
var
TZAlwaysFromEnv: boolean;
procedure InitTZ2; inline;
var
DT: {$IFDEF FPC_DOTTEDUNITS}OS2Api.{$endif}DosCalls.TDateTime;
begin
DosGetDateTime (DT);
TZAlwaysFromEnv := DT.TimeZone = -1;
end;
procedure GetLocalTime (var SystemTime: TSystemTime);
var
DT: {$IFDEF FPC_DOTTEDUNITS}OS2Api.{$endif}DosCalls.TDateTime;
begin
DosGetDateTime(DT);
with SystemTime do
begin
Year:=DT.Year;
Month:=DT.Month;
Day:=DT.Day;
DayOfWeek:=DT.WeekDay;
Hour:=DT.Hour;
Minute:=DT.Minute;
Second:=DT.Second;
MilliSecond:=DT.Sec100 * 10;
end;
end;
function GetUniversalTime (var SystemTime: TSystemTime): boolean;
var
DT: {$IFDEF FPC_DOTTEDUNITS}OS2Api.{$endif}DosCalls.TDateTime;
Offset: longint;
begin
if TZAlwaysFromEnv then
begin
GetLocalTime (SystemTime);
Offset := GetLocalTimeOffset;
end
else
begin
DosGetDateTime (DT);
with SystemTime do
begin
Year := DT.Year;
Month := DT.Month;
Day := DT.Day;
DayOfWeek := DT.WeekDay;
Hour := DT.Hour;
Minute := DT.Minute;
Second := DT.Second;
MilliSecond := DT.Sec100 * 10;
end;
if DT.TimeZone = -1 then
Offset := GetLocalTimeOffset
else
Offset := DT.TimeZone;
end;
UpdateTimeWithOffset (SystemTime, Offset);
GetUniversalTime := true;
end;
function GetLocalTimeOffset: integer;
var
DT: {$IFDEF FPC_DOTTEDUNITS}OS2Api.{$endif}DosCalls.TDateTime;
begin
if TZAlwaysFromEnv then
begin
if InDST then
GetLocalTimeOffset := DSTOffsetMin
else
GetLocalTimeOffset := TZOffsetMin;
end
else
begin
DosGetDateTime (DT);
if DT.TimeZone <> -1 then
GetLocalTimeOffset := DT.TimeZone
else
begin
if InDST then
GetLocalTimeOffset := DSTOffsetMin
else
GetLocalTimeOffset := TZOffsetMin;
end;
end;
end;
{****************************************************************************
Misc Functions
****************************************************************************}
procedure sysbeep;
begin
DosBeep (800, 250);
end;
{****************************************************************************
Locale Functions
****************************************************************************}
var
Country: TCountryCode;
CtryInfo: TCountryInfo;
procedure InitAnsi;
var
I: byte;
RC: cardinal;
begin
for I := 0 to 255 do
UpperCaseTable [I] := Chr (I);
Move (UpperCaseTable, LowerCaseTable, SizeOf (UpperCaseTable));
FillChar (Country, SizeOf (Country), 0);
DosMapCase (SizeOf (UpperCaseTable), Country, @UpperCaseTable);
for I := 0 to 255 do
if UpperCaseTable [I] <> Chr (I) then
LowerCaseTable [Ord (UpperCaseTable [I])] := Chr (I);
end;
procedure InitInternational;
var
Size: cardinal;
RC: cardinal;
begin
Size := 0;
FillChar (Country, SizeOf (Country), 0);
FillChar (CtryInfo, SizeOf (CtryInfo), 0);
RC := DosQueryCtryInfo (SizeOf (CtryInfo), Country, CtryInfo, Size);
if RC = 0 then
begin
DateSeparator := CtryInfo.DateSeparator;
case CtryInfo.DateFormat of
1: begin
ShortDateFormat := 'd/m/y';
LongDateFormat := 'dd" "mmmm" "yyyy';
end;
2: begin
ShortDateFormat := 'y/m/d';
LongDateFormat := 'yyyy" "mmmm" "dd';
end;
3: begin
ShortDateFormat := 'm/d/y';
LongDateFormat := 'mmmm" "dd" "yyyy';
end;
end;
TimeSeparator := CtryInfo.TimeSeparator;
DecimalSeparator := CtryInfo.DecimalSeparator;
ThousandSeparator := CtryInfo.ThousandSeparator;
CurrencyFormat := CtryInfo.CurrencyFormat;
CurrencyString := PAnsiChar (CtryInfo.CurrencyUnit);
end
else
OSErrorWatch (RC);
InitAnsi;
InitInternationalGeneric;
end;
function SysErrorMessage(ErrorCode: Integer): String;
const
SysMsgFile: array [0..10] of AnsiChar = 'OSO001.MSG'#0;
var
OutBuf: array [0..999] of AnsiChar;
RetMsgSize: cardinal;
RC: cardinal;
begin
RC := DosGetMessage (nil, 0, @OutBuf [0], SizeOf (OutBuf),
ErrorCode, @SysMsgFile [0], RetMsgSize);
if RC = 0 then
begin
SetLength (Result, RetMsgSize);
Move (OutBuf [0], Result [1], RetMsgSize);
end
else
begin
Result:=Format(SUnknownErrorCode,[ErrorCode]);
OSErrorWatch (RC);
end;
end;
{****************************************************************************
OS Utils
****************************************************************************}
function GetEnvPChar (EnvVar: shortstring): PAnsiChar;
(* The assembler version is more than three times as fast as Pascal. *)
var
P: PAnsiChar;
begin
EnvVar := UpCase (EnvVar);
{$ASMMODE INTEL}
asm
cld
mov edi, Environment
lea esi, EnvVar
xor eax, eax
lodsb
@NewVar:
cmp byte ptr [edi], 0
jz @Stop
push eax { eax contains length of searched variable name }
push esi { esi points to the beginning of the variable name }
mov ecx, -1 { our character ('=' - see below) _must_ be found }
mov edx, edi { pointer to beginning of variable name saved in edx }
mov al, '=' { searching until '=' (end of variable name) }
repne
scasb { scan until '=' not found }
neg ecx { what was the name length? }
dec ecx { corrected }
dec ecx { exclude the '=' character }
pop esi { restore pointer to beginning of variable name }
pop eax { restore length of searched variable name }
push eax { and save both of them again for later use }
push esi
cmp ecx, eax { compare length of searched variable name with name }
jnz @NotEqual { ... of currently found variable, jump if different }
xchg edx, edi { pointer to current variable name restored in edi }
repe
cmpsb { compare till the end of variable name }
xchg edx, edi { pointer to beginning of variable contents in edi }
jz @Equal { finish if they're equal }
@NotEqual:
xor eax, eax { look for 00h }
mov ecx, -1 { it _must_ be found }
repne
scasb { scan until found }
pop esi { restore pointer to beginning of variable name }
pop eax { restore length of searched variable name }
jmp @NewVar { ... or continue with new variable otherwise }
@Stop:
xor eax, eax
mov P, eax { Not found - return nil }
jmp @End
@Equal:
pop esi { restore the stack position }
pop eax
mov P, edi { place pointer to variable contents in P }
@End:
end ['eax','ecx','edx','esi','edi'];
GetEnvPChar := P;
end;
{$ASMMODE ATT}
Function GetEnvironmentVariable(Const EnvVar : String) : String;
begin
GetEnvironmentVariable := GetEnvPChar (EnvVar);
end;
Function GetEnvironmentVariableCount : Integer;
begin
(* Result:=FPCCountEnvVar(EnvP); - the amount is already known... *)
GetEnvironmentVariableCount := EnvC;
end;
Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
begin
Result:=FPCGetEnvStrFromP (EnvP, Index);
end;
procedure Sleep (Milliseconds: cardinal);
begin
DosSleep (Milliseconds);
end;
function SysTimerTick: QWord;
var
L: cardinal;
begin
DosQuerySysInfo (svMsCount, svMsCount, L, 4);
SysTimerTick := L;
end;
function ExecuteProcess (const Path: RawByteString;
const ComLine: RawByteString;Flags:TExecuteFlags=[]): integer;
var
E: EOSError;
CommandLine: RawByteString;
Args0, Args: {$IFDEF FPC_DOTTEDUNITS}OS2Api.{$endif}DosCalls.PByteArray;
ObjNameBuf: PAnsiChar;
ArgSize: word;
Res: TResultCodes;
ObjName: shortstring;
RC: cardinal;
ExecAppType: cardinal;
MaxArgsSize: PtrUInt; (* Amount of memory reserved for arguments in bytes. *)
MaxArgsSizeInc: word;
const
ObjBufSize = 512;
function StartSession: cardinal;
var
HQ: THandle;
SPID, STID, QName: shortstring;
SID, PID: cardinal;
SD: TStartData;
RD: TRequestData;
PCI: PChildInfo;
CISize: cardinal;
Prio: byte;
begin
Result := $FFFFFFFF;
FillChar (SD, SizeOf (SD), 0);
SD.Length := SizeOf (SD);
SD.Related := ssf_Related_Child;
if FileExists (Path) then
(* Full path necessary for starting different executable files from current *)
(* directory. *)
CommandLine := ExpandFileName (Path)
else
CommandLine := Path;
SD.PgmName := PAnsiChar (CommandLine);
if ComLine <> '' then
SD.PgmInputs := PAnsiChar (ComLine);
if ExecInheritsHandles in Flags then
SD.InheritOpt := ssf_InhertOpt_Parent;
Str (GetProcessID, SPID);
Str (ThreadID, STID);
QName := '\QUEUES\FPC_ExecuteProcess_p' + SPID + 't' + STID + '.QUE'#0;
SD.TermQ := @QName [1];
SD.ObjectBuffer := ObjNameBuf;
SD.ObjectBuffLen := ObjBufSize;
RC := DosCreateQueue (HQ, quFIFO or quConvert_Address, @QName [1]);
if RC <> 0 then
begin
Move (QName [1], ObjNameBuf^, Length (QName));
OSErrorWatch (RC);
end
else
begin
RC := DosStartSession (SD, SID, PID);
if (RC = 0) or (RC = 457) then
begin
RC := DosReadQueue (HQ, RD, CISize, PCI, 0, 0, Prio, 0);
if RC = 0 then
begin
Result := PCI^.Return;
RC := DosCloseQueue (HQ);
if RC <> 0 then
OSErrorWatch (RC);
RC := DosFreeMem (PCI);
if RC <> 0 then
OSErrorWatch (RC);
FreeMem (ObjNameBuf, ObjBufSize);
end
else
begin
OSErrorWatch (RC);
RC := DosCloseQueue (HQ);
OSErrorWatch (RC);
end;
end
else
begin
OSErrorWatch (RC);
RC := DosCloseQueue (HQ);
if RC <> 0 then
OSErrorWatch (RC);
end;
end;
end;
begin
Result := integer ($FFFFFFFF);
ObjName := '';
GetMem (ObjNameBuf, ObjBufSize);
FillChar (ObjNameBuf^, ObjBufSize, 0);
RC := DosQueryAppType (PAnsiChar (Path), ExecAppType);
if RC <> 0 then
begin
OSErrorWatch (RC);
if (RC = 190) or (RC = 191) then
Result := StartSession;
end
else
begin
if (ApplicationType and 3 = ExecAppType and 3) then
(* DosExecPgm should work... *)
begin
MaxArgsSize := Length (ComLine) + Length (Path) + 256; (* More than enough *)
if MaxArgsSize > high (word) then
Exit;
if ComLine = '' then
begin
Args0 := nil;
Args := nil;
end
else
begin
GetMem (Args0, MaxArgsSize);
Args := Args0;
(* Work around a bug in OS/2 - argument to DosExecPgm *)
(* should not cross 64K boundary. *)
while ((PtrUInt (Args) + MaxArgsSize) and $FFFF) < MaxArgsSize do
begin
MaxArgsSizeInc := MaxArgsSize -
((PtrUInt (Args) + MaxArgsSize) and $FFFF);
Inc (MaxArgsSize, MaxArgsSizeInc);
if MaxArgsSize > high (word) then
Exit;
ReallocMem (Args0, MaxArgsSize);
Inc (pointer (Args), MaxArgsSizeInc);
end;
ArgSize := 0;
Move (Path [1], Args^ [ArgSize], Length (Path));
Inc (ArgSize, Length (Path));
Args^ [ArgSize] := 0;
Inc (ArgSize);
{Now do the real arguments.}
Move (ComLine [1], Args^ [ArgSize], Length (ComLine));
Inc (ArgSize, Length (ComLine));
Args^ [ArgSize] := 0;
Inc (ArgSize);
Args^ [ArgSize] := 0;
end;
Res.ExitCode := $FFFFFFFF;
RC := DosExecPgm (ObjNameBuf, ObjBufSize, 0, Args, nil, Res,
PAnsiChar (Path));
if RC <> 0 then
OSErrorWatch (RC);
if Args0 <> nil then
FreeMem (Args0, MaxArgsSize);
if RC = 0 then
begin
Result := Res.ExitCode;
FreeMem (ObjNameBuf, ObjBufSize);
end
end
end;
if RC <> 0 then
begin
ObjName := StrPas (ObjNameBuf);
FreeMem (ObjNameBuf, ObjBufSize);
if ComLine = '' then
CommandLine := Path
else
CommandLine := Path + ' ' + ComLine;
if ObjName = '' then
E := EOSError.CreateFmt (SExecuteProcessFailed, [CommandLine, RC])
else
E := EOSError.CreateFmt (SExecuteProcessFailed + ' (' + ObjName + ')', [CommandLine, RC]);
E.ErrorCode := Result;
raise E;
end;
end;
function ExecuteProcess (const Path: RawByteString;
const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer;
var
CommandLine: AnsiString;
I: integer;
begin
Commandline := '';
for I := 0 to High (ComLine) do
if Pos (' ', ComLine [I]) <> 0 then
CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"'
else
CommandLine := CommandLine + ' ' + Comline [I];
ExecuteProcess := ExecuteProcess (Path, CommandLine);
end;
function GetTickCount: LongWord;
var
L: cardinal;
begin
DosQuerySysInfo (svMsCount, svMsCount, L, 4);
GetTickCount := L;
end;
function GetTickCount64: QWord;
var
Freq2: cardinal;
T: QWord;
begin
DosTmrQueryFreq (Freq2);
DosTmrQueryTime (T);
GetTickCount64 := T div (QWord (Freq2) div 1000);
{$NOTE GetTickCount64 takes 20 microseconds on 1GHz CPU, GetTickCount not measurable}
end;
const
OrigOSErrorWatch: TOSErrorWatch = nil;
procedure TrackLastOSError (Error: cardinal);
begin
LastOSError := Error;
OrigOSErrorWatch (Error);
end;
function GetLastOSError: Integer;
begin
GetLastOSError := Integer (LastOSError);
end;
{****************************************************************************
Initialization code
****************************************************************************}
Initialization
InitExceptions; { Initialize exceptions. OS independent }
InitInternational; { Initialize internationalization settings }
OnBeep:=@SysBeep;
LastOSError := 0;
OrigOSErrorWatch := TOSErrorWatch (SetOSErrorTracking (@TrackLastOSError));
InitTZ;
InitTZ2;
Finalization
FreeTerminateProcs;
DoneExceptions;
end.