mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 04:48:02 +02:00
1095 lines
28 KiB
ObjectPascal
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.
|