mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 20:08:52 +02:00
978 lines
26 KiB
ObjectPascal
978 lines
26 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2014 by Free Pascal development team
|
|
|
|
Sysutils unit for AmigaOS & clones
|
|
|
|
Based on Amiga 1.x version by Carl Eric Codere, and other
|
|
parts of the RTL
|
|
|
|
AmigaOS and MorphOS support by Karoly Balogh
|
|
AROS support by Marcus Sackrow
|
|
|
|
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 OS_FILESETDATEBYNAME}
|
|
{$DEFINE HAS_SLEEP}
|
|
{$DEFINE HAS_OSERROR}
|
|
{$DEFINE HAS_TEMPDIR}
|
|
|
|
{OS has only 1 byte version for ExecuteProcess}
|
|
{$define executeprocuni}
|
|
|
|
{ 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}
|
|
|
|
{ Include platform independent interface part }
|
|
{$i sysutilh.inc}
|
|
|
|
{ Platform dependent calls }
|
|
|
|
function DeviceByIdx(Idx: Integer): string;
|
|
function AddDisk(Const Path: string): Integer;
|
|
function RefreshDeviceList: Integer;
|
|
function DiskSize(Drive: AnsiString): Int64;
|
|
function DiskFree(Drive: AnsiString): Int64;
|
|
|
|
|
|
implementation
|
|
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
uses
|
|
TP.DOS, System.SysConst;
|
|
{$ELSE FPC_DOTTEDUNITS}
|
|
uses
|
|
dos, sysconst;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
{$DEFINE FPC_FEXPAND_VOLUMES} (* Full paths begin with drive specification *)
|
|
{$DEFINE FPC_FEXPAND_DRIVESEP_IS_ROOT}
|
|
{$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
|
|
{$DEFINE FPC_FEXPAND_DIRSEP_IS_UPDIR}
|
|
|
|
{ Include platform independent implementation part }
|
|
{$i sysutils.inc}
|
|
|
|
|
|
{ * Include system specific includes * }
|
|
{$include execd.inc}
|
|
{$include execf.inc}
|
|
{$include timerd.inc}
|
|
{$include doslibd.inc}
|
|
{$include doslibf.inc}
|
|
{$include utilf.inc}
|
|
|
|
{$ifdef cpum68k}
|
|
{$if defined(amiga_v1_0_only) or defined(amiga_v1_2_only)}
|
|
{$include legacyexech.inc}
|
|
{$include legacydosh.inc}
|
|
{$include legacyutilh.inc}
|
|
{$endif}
|
|
{$endif}
|
|
|
|
{ * Followings are implemented in the system unit! * }
|
|
function PathConv(path: shortstring): shortstring; external name 'PATHCONV';
|
|
function PathConv(path: RawByteString): RawByteString; external name 'PATHCONVRBS';
|
|
procedure AddToList(var l: Pointer; h: THandle); external name 'ADDTOLIST';
|
|
function RemoveFromList(var l: Pointer; h: THandle): boolean; external name 'REMOVEFROMLIST';
|
|
function CheckInList(var l: Pointer; h: THandle): pointer; external name 'CHECKINLIST';
|
|
|
|
var
|
|
ASYS_FileList: Pointer; external name 'ASYS_FILELIST';
|
|
|
|
|
|
function BADDR(bval: BPTR): Pointer; Inline;
|
|
begin
|
|
{$if defined(AROS)} // deactivated for now //and (not defined(AROS_BINCOMPAT))}
|
|
BADDR := Pointer(bval);
|
|
{$else}
|
|
BADDR:=Pointer(bval Shl 2);
|
|
{$endif}
|
|
end;
|
|
|
|
function BSTR2STRING(s : Pointer): PAnsiChar; Inline;
|
|
begin
|
|
{$if defined(AROS)} // deactivated for now //and (not defined(AROS_BINCOMPAT))}
|
|
BSTR2STRING:=PAnsiChar(s);
|
|
{$else}
|
|
BSTR2STRING:=PAnsiChar(BADDR(PtrInt(s)))+1;
|
|
{$endif}
|
|
end;
|
|
|
|
function BSTR2STRING(s : BPTR): PAnsiChar; Inline;
|
|
begin
|
|
{$if defined(AROS)} // deactivated for now //and (not defined(AROS_BINCOMPAT))}
|
|
BSTR2STRING:=PAnsiChar(s);
|
|
{$else}
|
|
BSTR2STRING:=PAnsiChar(BADDR(s))+1;
|
|
{$endif}
|
|
end;
|
|
|
|
function AmigaFileDateToDateTime(aDate: TDateStamp; out success: boolean): TDateTime;
|
|
var
|
|
tmpSecs: DWord;
|
|
tmpDate: TDateTime;
|
|
tmpTime: TDateTime;
|
|
clockData: TClockData;
|
|
begin
|
|
with aDate do
|
|
tmpSecs:=(ds_Days * (24 * 60 * 60)) + (ds_Minute * 60) + (ds_Tick div TICKS_PER_SECOND);
|
|
|
|
Amiga2Date(tmpSecs,@clockData);
|
|
{$HINT TODO: implement msec values, if possible}
|
|
with clockData do begin
|
|
success:=TryEncodeDate(year,month,mday,tmpDate) and
|
|
TryEncodeTime(hour,min,sec,0,tmpTime);
|
|
end;
|
|
|
|
result:=ComposeDateTime(tmpDate,tmpTime);
|
|
end;
|
|
|
|
function DateTimeToAmigaDateStamp(dateTime: TDateTime): TDateStamp;
|
|
var
|
|
tmpSecs: DWord;
|
|
clockData: TClockData;
|
|
tmpMSec: Word;
|
|
begin
|
|
{$HINT TODO: implement msec values, if possible}
|
|
with clockData do begin
|
|
DecodeDate(dateTime,year,month,mday);
|
|
DecodeTime(dateTime,hour,min,sec,tmpMSec);
|
|
end;
|
|
|
|
tmpSecs:=Date2Amiga(@clockData);
|
|
|
|
with result do begin
|
|
ds_Days:= tmpSecs div (24 * 60 * 60);
|
|
ds_Minute:= (tmpSecs div 60) mod ds_Days;
|
|
ds_Tick:= (((tmpSecs mod 60) mod ds_Minute) mod ds_Days) * TICKS_PER_SECOND;
|
|
end;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
File Functions
|
|
****************************************************************************}
|
|
{$I-}{ Required for correct usage of these routines }
|
|
|
|
|
|
(****** non portable routines ******)
|
|
|
|
function FileOpen(const FileName: rawbytestring; Mode: Integer): THandle;
|
|
var
|
|
SystemFileName: RawByteString;
|
|
dosResult: THandle;
|
|
begin
|
|
SystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
{$WARNING FIX ME! To do: FileOpen Access Modes}
|
|
dosResult:=Open(PAnsiChar(SystemFileName),MODE_OLDFILE);
|
|
if dosResult=0 then
|
|
dosResult:=-1
|
|
else
|
|
AddToList(ASYS_fileList,dosResult);
|
|
|
|
FileOpen:=dosResult;
|
|
end;
|
|
|
|
|
|
function FileGetDate(Handle: THandle) : Int64;
|
|
var
|
|
tmpFIB : PFileInfoBlock;
|
|
tmpDateTime: TDateTime;
|
|
validFile: boolean;
|
|
begin
|
|
validFile:=false;
|
|
|
|
if (Handle <> 0) then begin
|
|
new(tmpFIB);
|
|
if ExamineFH(BPTR(Handle),tmpFIB) then begin
|
|
tmpDateTime:=AmigaFileDateToDateTime(tmpFIB^.fib_Date,validFile);
|
|
end;
|
|
dispose(tmpFIB);
|
|
end;
|
|
|
|
if validFile then
|
|
result:=DateTimeToFileDate(tmpDateTime)
|
|
else
|
|
result:=-1;
|
|
end;
|
|
|
|
|
|
function FileSetDate(Handle: THandle; Age: Int64) : LongInt;
|
|
var
|
|
tmpDateStamp: TDateStamp;
|
|
tmpName: array[0..255] of AnsiChar;
|
|
begin
|
|
result:=0;
|
|
if (Handle <> 0) then begin
|
|
if NameFromFH(BPTR(Handle), @tmpName, 256) then begin
|
|
tmpDateStamp:=DateTimeToAmigaDateStamp(FileDateToDateTime(Age));
|
|
if not SetFileDate(@tmpName,@tmpDateStamp) then begin
|
|
IoErr(); // dump the error code for now (TODO)
|
|
result:=-1;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function FileSetDate(const FileName: RawByteString; Age: Int64) : LongInt;
|
|
var
|
|
tmpDateStamp: TDateStamp;
|
|
SystemFileName: RawByteString;
|
|
begin
|
|
result:=0;
|
|
SystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
tmpDateStamp:=DateTimeToAmigaDateStamp(FileDateToDateTime(Age));
|
|
if not SetFileDate(PAnsiChar(SystemFileName),@tmpDateStamp) then begin
|
|
IoErr(); // dump the error code for now (TODO)
|
|
result:=-1;
|
|
end;
|
|
end;
|
|
|
|
|
|
function FileCreate(const FileName: RawByteString) : THandle;
|
|
var
|
|
SystemFileName: RawByteString;
|
|
dosResult: THandle;
|
|
begin
|
|
dosResult:=-1;
|
|
|
|
{ Open file in MODDE_READWRITE, then truncate it by hand rather than
|
|
opening it in MODE_NEWFILE, because that returns an exclusive lock
|
|
so some operations might fail with it (KB) }
|
|
SystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
dosResult:=Open(PAnsiChar(SystemFileName),MODE_READWRITE);
|
|
if dosResult = 0 then exit;
|
|
|
|
if SetFileSize(dosResult, 0, OFFSET_BEGINNING) = 0 then
|
|
AddToList(ASYS_fileList,dosResult)
|
|
else begin
|
|
dosClose(dosResult);
|
|
dosResult:=-1;
|
|
end;
|
|
|
|
FileCreate:=dosResult;
|
|
end;
|
|
|
|
function FileCreate(const FileName: RawByteString; Rights: integer): THandle;
|
|
begin
|
|
{$WARNING FIX ME! To do: FileCreate Access Modes}
|
|
FileCreate:=FileCreate(FileName);
|
|
end;
|
|
|
|
function FileCreate(const FileName: RawByteString; ShareMode: integer; Rights : integer): THandle;
|
|
begin
|
|
{$WARNING FIX ME! To do: FileCreate Access Modes}
|
|
FileCreate:=FileCreate(FileName);
|
|
end;
|
|
|
|
|
|
function FileRead(Handle: THandle; out Buffer; Count: LongInt): LongInt;
|
|
begin
|
|
FileRead:=-1;
|
|
if (Count<=0) or (Handle=0) or (Handle=-1) then exit;
|
|
|
|
FileRead:=dosRead(Handle,@Buffer,Count);
|
|
end;
|
|
|
|
|
|
function FileWrite(Handle: THandle; const Buffer; Count: LongInt): LongInt;
|
|
begin
|
|
FileWrite:=-1;
|
|
if (Count<=0) or (Handle=0) or (Handle=-1) then exit;
|
|
|
|
FileWrite:=dosWrite(Handle,@Buffer,Count);
|
|
end;
|
|
|
|
|
|
function FileSeek(Handle: THandle; FOffset, Origin: LongInt) : LongInt;
|
|
var
|
|
seekMode: LongInt;
|
|
begin
|
|
FileSeek:=-1;
|
|
if (Handle=0) or (Handle=-1) then exit;
|
|
|
|
case Origin of
|
|
fsFromBeginning: seekMode:=OFFSET_BEGINNING;
|
|
fsFromCurrent : seekMode:=OFFSET_CURRENT;
|
|
fsFromEnd : seekMode:=OFFSET_END;
|
|
end;
|
|
|
|
dosSeek(Handle, FOffset, seekMode);
|
|
{ get the current position when FileSeek ends, which should return
|
|
the *NEW* position, while Amiga Seek() returns the old one }
|
|
FileSeek:=dosSeek(Handle, 0, OFFSET_CURRENT);
|
|
end;
|
|
|
|
function FileSeek(Handle: THandle; FOffset: Int64; Origin: Longint): Int64;
|
|
begin
|
|
{$WARNING Need to add 64bit call }
|
|
FileSeek:=FileSeek(Handle,LongInt(FOffset),LongInt(Origin));
|
|
end;
|
|
|
|
|
|
procedure FileClose(Handle: THandle);
|
|
begin
|
|
if (Handle=0) or (Handle=-1) then exit;
|
|
|
|
dosClose(Handle);
|
|
RemoveFromList(ASYS_fileList,Handle);
|
|
end;
|
|
|
|
|
|
function FileTruncate(Handle: THandle; Size: Int64): Boolean;
|
|
var
|
|
dosResult: LongInt;
|
|
begin
|
|
FileTruncate:=False;
|
|
|
|
if Size > high (longint) then exit;
|
|
{$WARNING Possible support for 64-bit FS to be checked!}
|
|
|
|
if (Handle=0) or (Handle=-1) then exit;
|
|
|
|
dosResult:=SetFileSize(Handle, Size, OFFSET_BEGINNING);
|
|
if (dosResult<0) then exit;
|
|
|
|
FileTruncate:=True;
|
|
end;
|
|
|
|
|
|
function DeleteFile(const FileName: RawByteString) : Boolean;
|
|
var
|
|
SystemFileName: RawByteString;
|
|
begin
|
|
SystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
DeleteFile:=dosDeleteFile(PAnsiChar(SystemFileName));
|
|
end;
|
|
|
|
|
|
function RenameFile(const OldName, NewName: RawByteString): Boolean;
|
|
var
|
|
OldSystemFileName, NewSystemFileName: RawByteString;
|
|
begin
|
|
OldSystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(OldName));
|
|
NewSystemFileName:=PathConv(ToSingleByteFileSystemEncodedFileName(NewName));
|
|
RenameFile:=dosRename(PAnsiChar(OldSystemFileName), PAnsiChar(NewSystemFileName)) <> 0;
|
|
end;
|
|
|
|
|
|
(****** end of non portable routines ******)
|
|
|
|
|
|
function FileAge (const FileName : RawByteString): Int64;
|
|
var
|
|
tmpLock: BPTR;
|
|
tmpFIB : PFileInfoBlock;
|
|
tmpDateTime: TDateTime;
|
|
validFile: boolean;
|
|
SystemFileName: RawByteString;
|
|
begin
|
|
validFile:=false;
|
|
SystemFileName := PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
tmpLock := Lock(PAnsiChar(SystemFileName), SHARED_LOCK);
|
|
|
|
if (tmpLock <> 0) then begin
|
|
new(tmpFIB);
|
|
if Examine(tmpLock,tmpFIB) <> 0 then begin
|
|
tmpDateTime:=AmigaFileDateToDateTime(tmpFIB^.fib_Date,validFile);
|
|
end;
|
|
Unlock(tmpLock);
|
|
dispose(tmpFIB);
|
|
end;
|
|
|
|
if validFile then
|
|
result:=DateTimeToFileDate(tmpDateTime)
|
|
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
|
|
tmpLock: BPTR;
|
|
tmpFIB : PFileInfoBlock;
|
|
SystemFileName: RawByteString;
|
|
begin
|
|
result:=false;
|
|
SystemFileName := PathConv(ToSingleByteFileSystemEncodedFileName(FileName));
|
|
tmpLock := Lock(PAnsiChar(SystemFileName), SHARED_LOCK);
|
|
|
|
if (tmpLock <> 0) then begin
|
|
new(tmpFIB);
|
|
if (Examine(tmpLock,tmpFIB) <> 0) and (tmpFIB^.fib_DirEntryType <= 0) then
|
|
result:=true;
|
|
Unlock(tmpLock);
|
|
dispose(tmpFIB);
|
|
end;
|
|
end;
|
|
|
|
|
|
Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
|
|
var
|
|
tmpStr: RawByteString;
|
|
Anchor: PAnchorPath;
|
|
tmpDateTime: TDateTime;
|
|
validDate: boolean;
|
|
begin
|
|
result:=-1; { We emulate Linux/Unix behaviour, and return -1 on errors. }
|
|
|
|
tmpStr:=PathConv(ToSingleByteFileSystemEncodedFileName(Path));
|
|
|
|
{ $1e = faHidden or faSysFile or faVolumeID or faDirectory }
|
|
Rslt.ExcludeAttr := (not Attr) and ($1e);
|
|
Rslt.FindHandle := nil;
|
|
|
|
new(Anchor);
|
|
FillChar(Anchor^,sizeof(TAnchorPath),#0);
|
|
Rslt.FindHandle := Anchor;
|
|
|
|
if MatchFirst(PAnsiChar(tmpStr),Anchor)<>0 then
|
|
begin
|
|
InternalFindClose(Rslt.FindHandle);
|
|
exit;
|
|
end;
|
|
|
|
with Anchor^.ap_Info do begin
|
|
Name := fib_FileName;
|
|
SetCodePage(Name,DefaultFileSystemCodePage,false);
|
|
|
|
Rslt.Size := fib_Size;
|
|
Rslt.Time := DateTimeToFileDate(AmigaFileDateToDateTime(fib_Date,validDate));
|
|
if not validDate then
|
|
begin
|
|
InternalFindClose(Rslt.FindHandle);
|
|
exit;
|
|
end;
|
|
|
|
{ "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
|
|
Rslt.Attr := 128;
|
|
|
|
if fib_DirEntryType > 0 then Rslt.Attr:=Rslt.Attr or faDirectory;
|
|
if ((fib_Protection and FIBF_READ) <> 0) and
|
|
((fib_Protection and FIBF_WRITE) = 0) then Rslt.Attr:=Rslt.Attr or faReadOnly;
|
|
|
|
result:=0; { Return zero if everything went OK }
|
|
end;
|
|
end;
|
|
|
|
|
|
Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
|
|
var
|
|
Anchor: PAnchorPath;
|
|
validDate: boolean;
|
|
begin
|
|
result:=-1;
|
|
|
|
Anchor:=PAnchorPath(Rslt.FindHandle);
|
|
if not assigned(Anchor) then exit;
|
|
if MatchNext(Anchor) <> 0 then exit;
|
|
|
|
with Anchor^.ap_Info do begin
|
|
Name := fib_FileName;
|
|
SetCodePage(Name,DefaultFileSystemCodePage,false);
|
|
Rslt.Size := fib_Size;
|
|
Rslt.Time := DateTimeToFileDate(AmigaFileDateToDateTime(fib_Date,validDate));
|
|
if not validDate then exit;
|
|
|
|
{ "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
|
|
Rslt.Attr := 128;
|
|
if fib_DirEntryType > 0 then Rslt.Attr:=Rslt.Attr or faDirectory;
|
|
if ((fib_Protection and FIBF_READ) <> 0) and
|
|
((fib_Protection and FIBF_WRITE) = 0) then Rslt.Attr:=Rslt.Attr or faReadOnly;
|
|
|
|
result:=0; { Return zero if everything went OK }
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure InternalFindClose(var Handle: Pointer);
|
|
var
|
|
Anchor: PAnchorPath absolute Handle;
|
|
begin
|
|
if not assigned(Anchor) then
|
|
exit;
|
|
MatchEnd(Anchor);
|
|
Dispose(Anchor);
|
|
Handle:=nil;
|
|
end;
|
|
|
|
|
|
(****** end of non portable routines ******)
|
|
|
|
Function FileGetAttr (Const FileName : RawByteString) : Longint;
|
|
var
|
|
F: file;
|
|
attr: word;
|
|
begin
|
|
Assign(F,FileName);
|
|
{$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}dos.GetFAttr(F,attr);
|
|
if DosError <> 0 then
|
|
FileGetAttr := -1
|
|
else
|
|
FileGetAttr := Attr;
|
|
end;
|
|
|
|
|
|
Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint;
|
|
var
|
|
F: file;
|
|
begin
|
|
Assign(F, FileName);
|
|
{$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}Dos.SetFAttr(F, Attr and $ffff);
|
|
FileSetAttr := DosError;
|
|
end;
|
|
|
|
|
|
|
|
{****************************************************************************
|
|
Disk Functions
|
|
****************************************************************************}
|
|
|
|
{
|
|
The Diskfree and Disksize functions need a file on the specified drive, since this
|
|
is required for the statfs system call.
|
|
These filenames are set in drivestr[0..26], and have been preset to :
|
|
0 - ':' (default drive - hence current dir is ok.)
|
|
1 - 'DF0:' (floppy drive 1 - should be adapted to local system )
|
|
2 - 'DF1:' (floppy drive 2 - should be adapted to local system )
|
|
3 - 'SYS:' (C: equivalent of dos is the SYS: partition)
|
|
4..26 (can be set by you're own applications)
|
|
! Use AddDisk() to Add new drives !
|
|
They both return -1 when a failure occurs.
|
|
}
|
|
var
|
|
DeviceList: array[0..26] of string[20];
|
|
NumDevices: Integer = 0;
|
|
|
|
const
|
|
IllegalDevices: array[0..12] of string =(
|
|
'PED:',
|
|
'PRJ:',
|
|
'PIPE:', // Pipes
|
|
'XPIPE:', // Extended Pipe
|
|
'CON:', // Console
|
|
'RAW:', // RAW: Console
|
|
'KCON:', // KingCON Console
|
|
'KRAW:', // KingCON RAW
|
|
'SER:', // serial Ports
|
|
'SER0:',
|
|
'SER1:',
|
|
'PAR:', // Parallel Port
|
|
'PRT:'); // Printer
|
|
|
|
function IsIllegalDevice(DeviceName: string): Boolean;
|
|
var
|
|
i: Integer;
|
|
Str: AnsiString;
|
|
begin
|
|
IsIllegalDevice := False;
|
|
Str := UpperCase(DeviceName);
|
|
for i := Low(IllegalDevices) to High(IllegalDevices) do
|
|
begin
|
|
if Str = IllegalDevices[i] then
|
|
begin
|
|
IsIllegalDevice := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function DeviceByIdx(Idx: Integer): string;
|
|
begin
|
|
DeviceByIdx := '';
|
|
if (Idx < 0) or (Idx >= NumDevices) then
|
|
Exit;
|
|
DeviceByIdx := DeviceList[Idx];
|
|
end;
|
|
|
|
function AddDisk(const Path: string): Integer;
|
|
begin
|
|
// if hit border, restart at 4
|
|
if NumDevices > 26 then
|
|
NumDevices := 4;
|
|
// set the device
|
|
DeviceList[NumDevices] := Copy(Path, 1, 20);
|
|
// return the Index increment for next run
|
|
AddDisk := NumDevices;
|
|
Inc(NumDevices);
|
|
end;
|
|
|
|
function RefreshDeviceList: Integer;
|
|
var
|
|
List: PDosList;
|
|
Temp: PAnsiChar;
|
|
Str: string;
|
|
begin
|
|
NumDevices := 0;
|
|
AddDisk(':'); // Index 0
|
|
AddDisk('DF0:'); // Index 1
|
|
AddDisk('DF1:'); // Index 2
|
|
AddDisk('SYS:'); // Index 3
|
|
// Lock the List
|
|
List := LockDosList(LDF_DEVICES or LDF_READ);
|
|
// Inspect the List
|
|
repeat
|
|
List := NextDosEntry(List, LDF_DEVICES);
|
|
if List <> nil then
|
|
begin
|
|
Temp := BSTR2STRING(List^.dol_Name);
|
|
Str := strpas(Temp) + ':';
|
|
if not IsIllegalDevice(str) then
|
|
AddDisk(Str);
|
|
end;
|
|
until List = nil;
|
|
UnLockDosList(LDF_DEVICES or LDF_READ);
|
|
RefreshDeviceList := NumDevices;
|
|
end;
|
|
|
|
// New easier DiskSize()
|
|
//
|
|
function DiskSize(Drive: AnsiString): Int64;
|
|
var
|
|
DirLock: BPTR;
|
|
Inf: TInfoData;
|
|
MyProc: PProcess;
|
|
OldWinPtr: Pointer;
|
|
begin
|
|
DiskSize := -1;
|
|
//
|
|
MyProc := PProcess(FindTask(Nil));
|
|
OldWinPtr := MyProc^.pr_WindowPtr;
|
|
MyProc^.pr_WindowPtr := Pointer(-1);
|
|
//
|
|
DirLock := Lock(PAnsiChar(Drive), SHARED_LOCK);
|
|
if DirLock <> 0 then
|
|
begin
|
|
if Info(DirLock, @Inf) <> 0 then
|
|
DiskSize := Int64(Inf.id_NumBlocks) * Inf.id_BytesPerBlock;
|
|
UnLock(DirLock);
|
|
end;
|
|
if OldWinPtr <> Pointer(-1) then
|
|
MyProc^.pr_WindowPtr := OldWinPtr;
|
|
end;
|
|
|
|
function DiskSize(Drive: Byte): Int64;
|
|
begin
|
|
DiskSize := -1;
|
|
if (Drive < 0) or (Drive >= NumDevices) then
|
|
Exit;
|
|
DiskSize := DiskSize(DeviceList[Drive]);
|
|
end;
|
|
|
|
// New easier DiskFree()
|
|
//
|
|
function DiskFree(Drive: AnsiString): Int64;
|
|
var
|
|
DirLock: BPTR;
|
|
Inf: TInfoData;
|
|
MyProc: PProcess;
|
|
OldWinPtr: Pointer;
|
|
begin
|
|
DiskFree := -1;
|
|
//
|
|
MyProc := PProcess(FindTask(Nil));
|
|
OldWinPtr := MyProc^.pr_WindowPtr;
|
|
MyProc^.pr_WindowPtr := Pointer(-1);
|
|
//
|
|
DirLock := Lock(PAnsiChar(Drive), SHARED_LOCK);
|
|
if DirLock <> 0 then
|
|
begin
|
|
if Info(DirLock, @Inf) <> 0 then
|
|
DiskFree := Int64(Inf.id_NumBlocks - Inf.id_NumBlocksUsed) * Inf.id_BytesPerBlock;
|
|
UnLock(DirLock);
|
|
end;
|
|
if OldWinPtr <> Pointer(-1) then
|
|
MyProc^.pr_WindowPtr := OldWinPtr;
|
|
end;
|
|
|
|
function DiskFree(Drive: Byte): Int64;
|
|
begin
|
|
DiskFree := -1;
|
|
if (Drive < 0) or (Drive >= NumDevices) then
|
|
Exit;
|
|
DiskFree := DiskFree(DeviceList[Drive]);
|
|
end;
|
|
|
|
function DirectoryExists(const Directory: RawByteString; FollowLink : Boolean): Boolean;
|
|
var
|
|
tmpLock: BPTR;
|
|
FIB : PFileInfoBlock;
|
|
SystemDirName: RawByteString;
|
|
begin
|
|
result:=false;
|
|
if (Directory='') or (InOutRes<>0) then exit;
|
|
|
|
SystemDirName:=PathConv(ToSingleByteFileSystemEncodedFileName(Directory));
|
|
tmpLock:=Lock(PAnsiChar(SystemDirName),SHARED_LOCK);
|
|
if tmpLock=0 then exit;
|
|
|
|
FIB:=nil; new(FIB);
|
|
|
|
if (Examine(tmpLock,FIB) <> 0) and (FIB^.fib_DirEntryType>0) then
|
|
result:=True;
|
|
|
|
if tmpLock<>0 then Unlock(tmpLock);
|
|
if assigned(FIB) then dispose(FIB);
|
|
end;
|
|
|
|
|
|
|
|
{****************************************************************************
|
|
Locale Functions
|
|
****************************************************************************}
|
|
|
|
Procedure GetLocalTime(var SystemTime: TSystemTime);
|
|
var
|
|
dayOfWeek: word;
|
|
Sec100: Word;
|
|
begin
|
|
{$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}dos.GetTime(SystemTime.Hour, SystemTime.Minute, SystemTime.Second, Sec100);
|
|
SystemTime.Millisecond := Sec100 * 10;
|
|
{$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}dos.GetDate(SystemTime.Year, SystemTime.Month, SystemTime.Day, DayOfWeek);
|
|
end;
|
|
|
|
|
|
Procedure InitAnsi;
|
|
Var
|
|
i : longint;
|
|
begin
|
|
{ Fill table entries 0 to 127 }
|
|
for i := 0 to 96 do
|
|
UpperCaseTable[i] := chr(i);
|
|
for i := 97 to 122 do
|
|
UpperCaseTable[i] := chr(i - 32);
|
|
for i := 123 to 191 do
|
|
UpperCaseTable[i] := chr(i);
|
|
Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
|
|
|
|
for i := 0 to 64 do
|
|
LowerCaseTable[i] := chr(i);
|
|
for i := 65 to 90 do
|
|
LowerCaseTable[i] := chr(i + 32);
|
|
for i := 91 to 191 do
|
|
LowerCaseTable[i] := chr(i);
|
|
Move (CPISO88591LCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
|
|
end;
|
|
|
|
|
|
Procedure InitInternational;
|
|
begin
|
|
InitInternationalGeneric;
|
|
InitAnsi;
|
|
end;
|
|
|
|
function SysErrorMessage(ErrorCode: Integer): String;
|
|
|
|
begin
|
|
Result:=Format(SUnknownErrorCode,[ErrorCode]);
|
|
end;
|
|
|
|
function GetLastOSError: Integer;
|
|
begin
|
|
result:=-1;
|
|
end;
|
|
|
|
{****************************************************************************
|
|
OS utility functions
|
|
****************************************************************************}
|
|
|
|
var
|
|
StrOfPaths: String;
|
|
|
|
function SystemTags(const command: PAnsiChar; const tags: array of PtrUInt): LongInt;
|
|
begin
|
|
SystemTags:=SystemTagList(command,@tags);
|
|
end;
|
|
|
|
function GetPathString: String;
|
|
var
|
|
f : text;
|
|
s : string;
|
|
begin
|
|
s := '';
|
|
result := '';
|
|
|
|
{ Alternatively, this could use PIPE: handler on systems which
|
|
have this by default (not the case on classic Amiga), but then
|
|
the child process should be started async, which for a simple
|
|
Path command probably isn't worth the trouble. (KB) }
|
|
assign(f,'T:'+HexStr(FindTask(nil))+'_path.tmp');
|
|
rewrite(f);
|
|
{ This is a pretty ugly stunt, combining Pascal and Amiga system
|
|
functions, but works... }
|
|
SystemTags('C:Path',[SYS_Input, 0, SYS_Output, TextRec(f).Handle, TAG_END]);
|
|
close(f);
|
|
|
|
reset(f);
|
|
{ skip the first line, garbage }
|
|
if not eof(f) then readln(f,s);
|
|
while not eof(f) do begin
|
|
readln(f,s);
|
|
if result = '' then
|
|
result := s
|
|
else
|
|
result := result + ';' + s;
|
|
end;
|
|
close(f);
|
|
erase(f);
|
|
end;
|
|
|
|
Function GetEnvironmentVariable(Const EnvVar : String) : String;
|
|
begin
|
|
if UpCase(envvar) = 'PATH' then begin
|
|
if StrOfpaths = '' then StrOfPaths := GetPathString;
|
|
Result:=StrOfPaths;
|
|
end else
|
|
Result:={$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}Dos.Getenv(shortstring(EnvVar));
|
|
end;
|
|
|
|
Function GetEnvironmentVariableCount : Integer;
|
|
|
|
begin
|
|
// Result:=FPCCountEnvVar(EnvP);
|
|
Result:={$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}Dos.envCount;
|
|
end;
|
|
|
|
Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
|
|
|
|
begin
|
|
// Result:=FPCGetEnvStrFromP(Envp,Index);
|
|
Result:={$IFDEF FPC_DOTTEDUNITS}TP.{$ENDIF}Dos.EnvStr(Index);
|
|
end;
|
|
|
|
function ExecuteProcess (const Path: RawByteString; const ComLine: RawByteString;Flags:TExecuteFlags=[]):
|
|
integer;
|
|
var
|
|
tmpPath,
|
|
convPath: RawByteString;
|
|
CommandLine: AnsiString;
|
|
tmpLock: BPTR;
|
|
|
|
E: EOSError;
|
|
begin
|
|
DosError:= 0;
|
|
|
|
convPath:=PathConv(ToSingleByteFileSystemEncodedFileName(Path));
|
|
tmpPath:=convPath+' '+ToSingleByteFileSystemEncodedFileName(ComLine);
|
|
|
|
{ Here we must first check if the command we wish to execute }
|
|
{ actually exists, because this is NOT handled by the }
|
|
{ _SystemTagList call (program will abort!!) }
|
|
|
|
{ Try to open with shared lock }
|
|
tmpLock:=Lock(PAnsiChar(convPath),SHARED_LOCK);
|
|
if tmpLock<>0 then
|
|
begin
|
|
{ File exists - therefore unlock it }
|
|
Unlock(tmpLock);
|
|
result:=SystemTagList(PAnsiChar(tmpPath),nil);
|
|
{ on return of -1 the shell could not be executed }
|
|
{ probably because there was not enough memory }
|
|
if result = -1 then
|
|
DosError:=8;
|
|
end
|
|
else
|
|
DosError:=3;
|
|
|
|
if DosError <> 0 then begin
|
|
if ComLine = '' then
|
|
CommandLine := Path
|
|
else
|
|
CommandLine := Path + ' ' + ComLine;
|
|
|
|
E := EOSError.CreateFmt (SExecuteProcessFailed, [CommandLine, DosError]);
|
|
E.ErrorCode := DosError;
|
|
raise E;
|
|
end;
|
|
end;
|
|
|
|
function ExecuteProcess (const Path: RawByteString;
|
|
const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer;
|
|
var
|
|
CommandLine: RawByteString;
|
|
I: integer;
|
|
|
|
begin
|
|
Commandline := '';
|
|
for I := 0 to High (ComLine) do
|
|
if Pos (' ', ComLine [I]) <> 0 then
|
|
CommandLine := CommandLine + ' ' + '"' + ToSingleByteFileSystemEncodedFileName(ComLine [I]) + '"'
|
|
else
|
|
CommandLine := CommandLine + ' ' + ToSingleByteFileSystemEncodedFileName(Comline [I]);
|
|
ExecuteProcess := ExecuteProcess (Path, CommandLine);
|
|
end;
|
|
|
|
procedure Sleep(Milliseconds: cardinal);
|
|
begin
|
|
// Amiga dos.library Delay() has precision of 1/50 seconds
|
|
DOSDelay(Milliseconds div 20);
|
|
end;
|
|
|
|
|
|
function GetTempDir(Global: Boolean): string;
|
|
begin
|
|
if Assigned(OnGetTempDir) then
|
|
Result := OnGetTempDir(Global)
|
|
else
|
|
begin
|
|
Result := GetEnvironmentVariable('TEMP');
|
|
if Result = '' Then
|
|
Result:=GetEnvironmentVariable('TMP');
|
|
if Result = '' then
|
|
Result := 'T:'; // fallback.
|
|
end;
|
|
if Result <> '' then
|
|
Result := IncludeTrailingPathDelimiter(Result);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Initialization code
|
|
****************************************************************************}
|
|
|
|
Initialization
|
|
InitExceptions;
|
|
InitInternational; { Initialize internationalization settings }
|
|
OnBeep:=Nil; { No SysBeep() on Amiga, for now. Figure out if we want
|
|
to use intuition.library/DisplayBeep() for this (KB) }
|
|
StrOfPaths:='';
|
|
|
|
RefreshDeviceList;
|
|
Finalization
|
|
FreeTerminateProcs;
|
|
DoneExceptions;
|
|
end.
|