{
platform.independent.osservices — модуль с основными сервисами
операционных систем.
Copyright © 2021, 2026 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или изменять
её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
}
unit platform.independent.osservices;
{$MODE OBJFPC}
interface
{%region} uses
{$IFDEF WINDOWS}
windows
{$ELSE}
unixtype,
baseunix
{$ENDIF},
pascalx.lang,
pascalx.lang.table,
pascalx.io,
{$IFNDEF WINDOWS}
pascalx.io.bytearray,
{$ENDIF}
pascalx.io.extension,
platform.independent.filesystem;
{%endregion}
{$TYPEINFO ON}
{$CALLING REGISTER}
const UNIT_NAME = 'platform.independent.osservices';
{%region} type
MemoryRegion = interface;
FileSystemRoot = class;
Environment = class;
Process = class;
TimeBase = class;
MemoryManager = class;
SystemInfo = class;
IllegalProcessStateException = class;
OutOfResourcesError = class;
MemoryRegion_Array1d = specialize DynamicArray<MemoryRegion>;
FileSystemRoot_Array1d = specialize DynamicArray<FileSystemRoot>;
MemoryRegion_Collection1d = specialize Collection<MemoryRegion>;
FileSystemRoot_Collection1d = specialize Collection<FileSystemRoot>;
MemoryRegion = interface(RefCountInterface) ['{FBE08BC1-5205-41C9-A23A-673BE4145C1E}']
procedure setProtectionBits(newProtectionBits: int);
function getProtectionBit(index: int): boolean;
function getProtectionBits(): int;
function getAddress(): long;
function getSize(): long;
function pointerTo(offset: long): Pointer;
property shared: boolean index 3 read getProtectionBit;
property readable: boolean index 0 read getProtectionBit;
property writeable: boolean index 1 read getProtectionBit;
property executable: boolean index 2 read getProtectionBit;
property protectionBits: int read getProtectionBits write setProtectionBits;
property address: long read getAddress;
property size: long read getSize;
end;
FileSystemRoot = class abstract(&Object)
{$IFNDEF WINDOWS}
private
class function deescapeMountPoint(const point: UnicodeString): UnicodeString; static;
class function getRootPathOf(const objectPath: UnicodeString): UnicodeString; static;
{$ENDIF}
public
class function isObjectPathCaseSensitive(): boolean; static;
class function isInternalPathFull(const internalPath: UnicodeString): boolean; static;
class function toInternalPath(const objectPath: UnicodeString): UnicodeString; static;
class function toObjectPath(const internalPath: UnicodeString): UnicodeString; static;
class function getUserCacheDir(): UnicodeString; static;
class function getUserLocalDir(): UnicodeString; static;
class function getUserConfigDir(): UnicodeString; static;
class function enumerate(): FileSystemRoot_Collection1d; static;
class function get(const objectPath: UnicodeString): FileSystemRoot; static; { Внимание! Допустим полный путь к объекту! }
protected
fldPath: UnicodeString;
function getName(): UnicodeString; virtual; abstract;
function getFileSystem(): FileSystem; virtual; abstract;
public
constructor create(const path: UnicodeString);
published
property name: UnicodeString read getName;
property path: UnicodeString read fldPath;
property fileSystem: platform.independent.filesystem.FileSystem read getFileSystem;
end;
Environment = class(&Object)
protected type
HashtableOfUnicodeStringToUnicodeString = specialize HashtableOfUnicodeString<UnicodeString>;
protected
fldMonitor: Mutex;
fldVariables: HashtableOfUnicodeStringToUnicodeString;
procedure setVariable(const name, value: UnicodeString); virtual;
function getLength(): int; virtual;
function getVariable(const name: UnicodeString): UnicodeString; virtual;
public
constructor create();
destructor destroy; override;
procedure clear(); virtual;
procedure assign(anot: Environment); virtual;
function equals(anot: TObject): boolean; override;
function toString(): AnsiString; override;
function isEmulation(): boolean; virtual;
function isCaseSensitive(): boolean; virtual;
function contains(const name: UnicodeString): boolean; virtual;
function variableAt(index: int): UnicodeString; virtual;
property length: int read getLength;
property variable[const name: UnicodeString]: UnicodeString read getVariable write setVariable; default;
end;
Process = class(&Object)
{$IFNDEF WINDOWS}
private type
Pint = ^int;
private
class function getExitCodeProcess(processId: int; status: Pint): boolean; static;
class function safeDup2(oldFD, newFD: int): int; static;
class function safeWaitPid(processId: int; status: Pint; options: int): int; static;
{$ENDIF}
public const
MIN_PRIORITY = int(1);
LOW_PRIORITY = int(3);
NORM_PRIORITY = int(5);
HIGH_PRIORITY = int(7);
MAX_PRIORITY = int(9);
public const
STILL_ACTIVE = int(259);
public
class function hasInstance(): boolean; static;
class function createPipe(): ByteStream; static;
class function current(): Process; static;
private
fldStarting: boolean;
{$IFDEF WINDOWS}
fldProcessHandle: system.THandle;
fldThreadHandle: system.THandle;
{$ELSE}
fldProcessId: int;
{$ENDIF}
fldStandardInput: ByteReader;
fldStandardOutput: ByteWriter;
fldStandardError: ByteWriter;
protected
fldPriority: int;
fldWorkingDirectory: UnicodeString;
fldCommandLine: UnicodeString_Array1d;
fldEnvironment: Environment;
procedure setPriority(newPriority: int); virtual;
procedure setWorkingDirectory(const newWorkingDirectory: UnicodeString); virtual;
procedure setCommandLine(const newCommandLine: UnicodeString_Array1d); virtual;
procedure setEnvironment(newEnvironment: Environment); virtual;
procedure setStandardInput(newStandardInput: ByteReader); virtual;
procedure setStandardOutput(newStandardOutput: ByteWriter); virtual;
procedure setStandardError(newStandardError: ByteWriter); virtual;
procedure clearStarting();
function getPriority(): int; virtual;
function getWorkingDirectory(): UnicodeString; virtual;
function createEnvironment(): Environment; virtual;
function isStarting(): boolean;
function getCommandLine(): UnicodeString_Array1d;
public
constructor create();
destructor destroy; override;
procedure start(); virtual;
procedure join(); virtual;
procedure join(timeInMillis: long); virtual;
procedure join(timeInMillis: long; timeInNanos: int); virtual;
function isTerminated(): boolean; virtual;
function getExitCode(): int; virtual;
published
property priority: int read getPriority write setPriority;
property workingDirectory: UnicodeString read getWorkingDirectory write setWorkingDirectory;
property commandLine: UnicodeString_Array1d read getCommandLine write setCommandLine;
property environment: platform.independent.osservices.Environment read fldEnvironment write setEnvironment;
property standardInput: ByteReader read fldStandardInput write setStandardInput;
property standardOutput: ByteWriter read fldStandardOutput write setStandardOutput;
property standardError: ByteWriter read fldStandardError write setStandardError;
end;
TimeBase = class sealed
public
class function currentOffsetInMillis(): int; static;
class function currentTimeInMillis(): long; static;
end;
MemoryManager = class sealed
private const
MEMORY_START_ADDRESS = long(0);
MEMORY_LIMIT_ADDRESS = long($800000000000);
public const
NOACCESS = int($00);
READABLE = int($01);
WRITEABLE = int($02);
EXECUTABLE = int($04);
SHARED = int($08);
RESERVED = int($10);
DOWN = int($20);
public const
PAGE_SIZE = long($1000);
public
class procedure deallocate(region: MemoryRegion; flags: int = 0); static;
class function enumerate(): MemoryRegion_Collection1d; static;
class function allocate(address, size: long; flags: int): MemoryRegion; static;
end;
SystemInfo = class sealed
private
class procedure initialize(); static;
public
class function getProcessorCodeBits(): int; static;
class function getProcessorNumberOfCores(): int; static;
class function getProcessorBrandString(): AnsiString; static;
class function getOperatingSystemName(): AnsiString; static;
class function getOperatingSystemVersion(): AnsiString; static;
end;
IllegalProcessStateException = class(IllegalStateException);
OutOfResourcesError = class(MemoryError)
protected
procedure freeAllow(); override;
procedure freeDisallow(); override;
end;
{%endregion}
implementation
{$R *.res}
{$IFDEF WINDOWS}{$R *@windows.res}{$ELSE}{$R *@unix.res}{$ENDIF}
{$WARN 7104 OFF} { позволить обращение к локальным переменным через [rbp-<смещение>] }
{$ASMMODE INTEL}
{$TYPEINFO OFF}
{$CALLING REGISTER}
{%region} type
MemoryRegionCollection = class;
MemoryRegionDescriptor = class;
FileSystemRootCollection = class;
FileSystemRootDescriptor = class;
VolumeFileSystem = class;
VolumeRequiredAttributes = class;
VolumeObjectAttributes = class;
VolumeObjectEnumeration = class;
Extensions = class;
HandleInputStream = class;
HandleOutputStream = class;
HandleBidirectStream = class;
FileSeekExtension = class;
FileInputStream = class;
FileOutputStream = class;
FileBidirectStream = class;
FileBidirectStreamReader = class;
FileBidirectStreamWriter = class;
PipeStream = class;
CurrentEnvironment = class;
CurrentProcess = class;
LongRecord = packed record
case int of
0: (
value: long;
);
1: (
slow: int;
shigh: int;
);
2: (
ulow: system.DWord;
uhigh: system.DWord;
);
end;
TimeRecord = packed record
millisecond: system.UInt16;
minute: system.UInt8;
hour: system.UInt8;
day: system.UInt8;
month: system.UInt8;
year: system.UInt16;
end;
HashtableOfUnicodeStringToFileSystemRoot = specialize HashtableOfUnicodeString<FileSystemRoot>;
MemoryRegionCollection = class sealed(RefCountObject, MemoryRegion_Collection1d)
private
fldLength: int;
fldRegions: MemoryRegion_Array1d;
public
constructor create(const regions: MemoryRegion_Array1d; length: int);
procedure copyInto(const dstArray; dstOffset: int);
function getLength(): int;
function componentAt(index: int): MemoryRegion;
function toArray(): MemoryRegion_Array1d;
end;
MemoryRegionDescriptor = class sealed(RefCountObject, MemoryRegion)
private
fldAddress: long;
fldSize: long;
{$IFNDEF WINDOWS}
function getProtectionString(): AnsiString;
{$ENDIF}
public
constructor create(address, size: long);
procedure setProtectionBits(newProtectionBits: int);
function equals(anot: TObject): boolean; override;
function getHashCode(): long; override;
function toString(): AnsiString; override;
function getProtectionBit(index: int): boolean;
function getProtectionBits(): int;
function getAddress(): long;
function getSize(): long;
function pointerTo(offset: long): Pointer;
end;
FileSystemRootCollection = class sealed(RefCountObject, FileSystemRoot_Collection1d)
private
fldLength: int;
fldRoots: FileSystemRoot_Array1d;
public
constructor create(const roots: FileSystemRoot_Array1d; length: int);
procedure copyInto(const dstArray; dstOffset: int);
function getLength(): int;
function componentAt(index: int): FileSystemRoot;
function toArray(): FileSystemRoot_Array1d;
end;
FileSystemRootDescriptor = class sealed(FileSystemRoot)
private
{$IFDEF WINDOWS}
fldCanonical: UnicodeString;
{$ENDIF}
fldFileSystem: VolumeFileSystem;
protected
function getName(): UnicodeString; override;
function getFileSystem(): FileSystem; override;
public
constructor create(const argPath, argCurrentDirectory: UnicodeString);
destructor destroy; override;
end;
VolumeFileSystem = class sealed(&Object, FileSystem)
private const
AT_OBJECT = int(0); { тип аргумента — объект }
AT_FILE = int(1); { тип аргумента — файл }
AT_DIRECTORY = int(2); { тип аргумента — папка }
private const
OBJECT_NAME_MAXIMUM_LENGTH = int({$IFDEF WINDOWS}255{$ELSE}1023{$ENDIF});
{$IFDEF WINDOWS}
private
class function isComponentReserved(const component: UnicodeString): boolean; static;
{$ENDIF}
private
{ поля типа UnicodeString хранят пути во внутреннем формате операционной системы }
{$IFDEF WINDOWS}
fldCurrentUnreliableBlock: boolean;
fldCurrentHandle: long;
{$ELSE}
fldObjectNameMaximumLength: int;
{$ENDIF}
fldCanonicalRootPath: AnsiString;
fldInternalRootPath: UnicodeString;
fldCurrentDirectory: UnicodeString;
{$IFDEF WINDOWS}
fldCurrentMonitor: Mutex;
procedure tryMakeCurrentReliableBlock();
{$ENDIF}
function toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
function makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
function makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
function makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
public
constructor create(const rootPath, currentDirectory: UnicodeString);
{$IFDEF WINDOWS}
destructor destroy; override;
{$ENDIF}
procedure changeCurrentDirectory(const directoryPath: UnicodeString);
procedure readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
procedure writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
procedure move(const objectOldName, objectNewName: UnicodeString);
procedure deleteFile(const fileName: UnicodeString);
procedure deleteDirectory(const directoryName: UnicodeString);
procedure createDirectory(const directoryName: UnicodeString);
function isAttached(): boolean;
function isReadOnly(): boolean;
function isObjectNameCaseSensitive(): boolean;
function isObjectExists(const objectName: UnicodeString): boolean;
function isObjectNameValid(const objectName: UnicodeString): boolean;
function isInternalNameFull(const internalName: UnicodeString): boolean;
function isInternalNameValid(const internalName: UnicodeString): boolean;
function getObjectNameMaximumLength(): int;
function totalSize(): long;
function usedSize(): long;
function availableSize(): long;
function getCurrentDirectory(): UnicodeString;
function toInternalName(const objectName: UnicodeString): UnicodeString;
function toObjectName(const internalName: UnicodeString): UnicodeString;
function findFirst(const objectPath: UnicodeString): ObjectEnumeration;
function createFile(const fileName: UnicodeString): ByteWriter;
function rewriteFile(const fileName: UnicodeString): ByteWriter;
function openFileForAppend(const fileName: UnicodeString): ByteWriter;
function openFileForRead(const fileName: UnicodeString): ByteReader;
function openFile(const fileName: UnicodeString): ByteStream;
function newAttributes(): Attributes;
end;
VolumeRequiredAttributes = class(RequiredAttributes)
public const
{$IFDEF WINDOWS}
B_HIDDEN = AnsiString('bHidden');
B_SYSTEM = AnsiString('bSystem');
B_ARCHIVE = AnsiString('bArchive');
L_CREATION_TIME = AnsiString('lCreationTime');
public
class function toObjectTime(internalTime: long): long; static;
class function toInternalTime(objectTime: long): long; static;
{$ELSE}
B_OWNER_READABLE = AnsiString('bOwnerReadable');
B_OWNER_WRITEABLE = AnsiString('bOwnerWriteable');
B_OWNER_EXECUTABLE = AnsiString('bOwnerExecutable');
B_GROUP_READABLE = AnsiString('bGroupReadable');
B_GROUP_WRITEABLE = AnsiString('bGroupWriteable');
B_GROUP_EXECUTABLE = AnsiString('bGroupExecutable');
B_OTHER_READABLE = AnsiString('bOtherReadable');
B_OTHER_WRITEABLE = AnsiString('bOtherWriteable');
B_OTHER_EXECUTABLE = AnsiString('bOtherExecutable');
{$ENDIF}
end;
VolumeObjectAttributes = class sealed(VolumeRequiredAttributes, Attributes)
private
class var attrHashes: long_Array1d;
class var attrIds: AnsiString_Array1d;
class procedure initialize(); static;
class procedure finalize(); static;
class procedure stringAttributeIdIsInvalid(const attributeId: AnsiString); static;
class function booleanAttributeIdToIndex(const attributeId: AnsiString): int; static;
class function longAttributeIdToIndex(const attributeId: AnsiString): int; static;
private
fldAttributes: int;
fldTimes: long_Array1d;
public
constructor create();
procedure setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
procedure setLongAttribute(const attributeId: AnsiString; attributeValue: long);
procedure setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
function isSupportedAttributeId(const attributeId: AnsiString): boolean;
function getBooleanAttribute(const attributeId: AnsiString): boolean;
function getLongAttribute(const attributeId: AnsiString): long;
function getStringAttribute(const attributeId: AnsiString): UnicodeString;
function displayName(const attributeId: AnsiString): UnicodeString;
function getSupportedAttributeIds(): AnsiString_Array1d;
end;
VolumeObjectEnumeration = class sealed(ObjectEnumeration)
private
{$IFDEF WINDOWS}
fldHandle: long;
{$ELSE}
fldFullDirectoryPath: AnsiString;
fldHandle: baseunix.PDir;
{$ENDIF}
procedure setAttributes(const info);
public
{$IFDEF WINDOWS}
constructor create(handle: long; const info);
{$ELSE}
constructor create(const fullDirectoryPath: AnsiString; handle: baseunix.PDir; const info);
{$ENDIF}
procedure close(); override;
function findNext(): boolean; override;
end;
Extensions = class(&Object, Extendable)
protected
fldExtensions: TObject_Array1d;
public
function getExtensions(): Extension_Array1d;
function getExtension(const typ: ShortString): Extension;
end;
HandleInputStream = class(Extensions, ByteReader)
protected
fldHandle: long;
public
constructor create(handle: long);
procedure close(); virtual;
function skip(bytesQuantity: long): long; virtual;
function read(): int;
function read(const dst: byte_Array1d): int;
function read(const dst: byte_Array1d; offset, length: int): int;
end;
HandleOutputStream = class(Extensions, ByteWriter)
protected
fldHandle: long;
fldRootPath: AnsiString;
public
constructor create(handle: long; const rootPath: AnsiString = '');
procedure close(); virtual;
procedure flush(); virtual;
procedure write(byteData: int);
procedure write(const src: byte_Array1d);
procedure write(const src: byte_Array1d; offset, length: int);
end;
HandleBidirectStream = class(Extensions, ByteStream)
protected
fldHandleForRead: long;
fldHandleForWrite: long;
fldReader: HandleInputStream;
fldWriter: HandleOutputStream;
public
constructor create(handleForRead, handleForWrite: long);
destructor destroy; override;
procedure close();
function getReader(): ByteReader;
function getWriter(): ByteWriter;
end;
FileSeekExtension = class(&Object, Extension, LimitedSizeExtension, SeekExtension)
private
fldHandle: long;
public
constructor create(handle: long);
function available(): long;
function seek(offset: long; from: SeekFrom): long;
function position(): long;
function size(): long;
end;
FileInputStream = class(HandleInputStream, Extension, MarkExtension)
private
fldOwnedSeekable: boolean;
fldMarked: long;
public
constructor create(handle: long; seekable: FileSeekExtension = nil);
destructor destroy; override;
procedure close(); override;
procedure reset();
procedure mark(transferLimit: int);
function skip(bytesQuantity: long): long; override;
end;
FileOutputStream = class(HandleOutputStream, Extension, TruncateExtension, MarkExtension)
private
fldOwnedSeekable: boolean;
fldMarked: long;
fldReader: FileInputStream;
public
constructor create(handle: long; const rootPath: AnsiString = ''; seekable: FileSeekExtension = nil; reader: FileInputStream = nil);
destructor destroy; override;
procedure close(); override;
procedure flush(); override;
procedure truncate();
procedure reset();
procedure mark(transferLimit: int);
end;
FileBidirectStream = class(HandleBidirectStream)
public
constructor create(handle: long; const rootPath: AnsiString = '');
destructor destroy; override;
end;
FileBidirectStreamReader = class sealed(FileInputStream)
public
procedure close(); override;
end;
FileBidirectStreamWriter = class sealed(FileOutputStream)
public
procedure close(); override;
end;
PipeStream = class sealed(HandleBidirectStream)
public
constructor create(handleForRead, handleForWrite: long);
end;
CurrentEnvironment = class sealed(Environment)
private
procedure update();
{$IFDEF WINDOWS}
protected
procedure setVariable(const name, value: UnicodeString); override;
public
constructor create();
procedure clear(); override;
procedure assign(anot: Environment); override;
{$ELSE}
public
constructor create();
function isEmulation(): boolean; override;
{$ENDIF}
end;
CurrentProcess = class sealed(Process)
private
class var instance: CurrentProcess;
class procedure initialize(); static;
class procedure finalize(); static;
class function parseCommandLine(): UnicodeString_Array1d; static;
private
fldStandardInputRef: TObject;
fldStandardOutputRef: TObject;
fldStandardErrorRef: TObject;
{$IFDEF WINDOWS}
fldWorkingDirectoryMonitor: Mutex;
{$ENDIF}
protected
{$IFDEF WINDOWS}
procedure setPriority(newPriority: int); override;
{$ENDIF}
procedure setWorkingDirectory(const newWorkingDirectory: UnicodeString); override;
procedure setCommandLine(const newCommandLine: UnicodeString_Array1d); override;
procedure setStandardInput(newStandardInput: ByteReader); override;
procedure setStandardOutput(newStandardOutput: ByteWriter); override;
procedure setStandardError(newStandardError: ByteWriter); override;
{$IFDEF WINDOWS}
function getPriority(): int; override;
{$ENDIF}
function getWorkingDirectory(): UnicodeString; override;
function createEnvironment(): Environment; override;
public
constructor create();
destructor destroy; override;
function isTerminated(): boolean; override;
function getExitCode(): int; override;
end;
{%endregion}
{%region} var
errorOutOfResources: OutOfResourcesError;
fileSystemRootCount: int = {$IFDEF WINDOWS}int('Z') - int('A') + 1{$ELSE}1{$ENDIF};
fileSystemRootArray: FileSystemRoot_Array1d;
fileSystemRootTable: HashtableOfUnicodeStringToFileSystemRoot;
fileSystemRootMonitor: Mutex;
cpuNumberOfCores: int;
cpuBrandString: AnsiString;
osVersion: AnsiString;
{%endregion}
{%region routines — exception}
procedure exceptionInitialize();
begin
errorOutOfResources := OutOfResourcesError.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, '!machine-error.out-of-resources'));
errorOutOfResources.freeDisallow();
end;
procedure exceptionFinalize();
begin
errorOutOfResources.freeAllow();
errorOutOfResources.free();
end;
{%endregion}
{%region routines — fileSystemRoot}
procedure fileSystemRootInitialize();
begin
fileSystemRootArray := FileSystemRoot_Array1d(&Array.newTObject1d($1f));
fileSystemRootTable := HashtableOfUnicodeStringToFileSystemRoot.create();
fileSystemRootMonitor := Mutex.create();
end;
procedure fileSystemRootFinalize();
var
index: int;
begin
for index := fileSystemRootCount - 1 downto 0 do begin
fileSystemRootArray[index].free();
end;
fileSystemRootArray := nil;
fileSystemRootTable.free();
fileSystemRootMonitor.free();
end;
procedure fileSystemRootRegister(const path: UnicodeString; descriptor: FileSystemRoot);
{$IFDEF WINDOWS}
var
index: int;
begin
index := int(path[2]) - int('A');
fileSystemRootArray[index] := descriptor;
fileSystemRootTable[path] := descriptor;
end
{$ELSE}
var
fileSystemRootCopy: FileSystemRoot_Array1d;
begin
if fileSystemRootCount = system.length(fileSystemRootArray) then begin
fileSystemRootCopy := FileSystemRoot_Array1d(&Array.newTObject1d((fileSystemRootCount shl 1) or 1));
&Array.copyObjects(fileSystemRootArray, 0, fileSystemRootCopy, 0, fileSystemRootCount);
fileSystemRootArray := fileSystemRootCopy;
end;
fileSystemRootArray[fileSystemRootCount] := descriptor;
fileSystemRootTable[path] := descriptor;
inc(fileSystemRootCount);
end
{$ENDIF};
procedure fileSystemRootCreateWorkingRoot();
{$IFDEF WINDOWS}
var
volumeLetter: uchar;
volumeIndex: int;
rootPath: UnicodeString;
workingDirectory: UnicodeString;
volumeDescriptor: FileSystemRoot;
begin
workingDirectory := CurrentProcess.instance.workingDirectory;
if (workingDirectory.length < 4) or (workingDirectory[1] <> '/') or (workingDirectory[3] <> ':') or (workingDirectory[4] <> '/') then exit;
volumeLetter := workingDirectory[2];
if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) then exit;
volumeLetter := CoUChar.toUpperCase(volumeLetter);
volumeIndex := int(volumeLetter) - int('A');
rootPath := '/' + volumeLetter + ':';
volumeDescriptor := FileSystemRootDescriptor.create(rootPath, workingDirectory.substring(4));
fileSystemRootArray[volumeIndex] := volumeDescriptor;
fileSystemRootTable[rootPath] := volumeDescriptor;
end
{$ELSE}
var
position: int;
lastRootLength: int;
currentRootLength: int;
lastRootPath: UnicodeString;
currentRootPath: UnicodeString;
workingDirectory: UnicodeString;
volumeDescriptor: FileSystemRoot;
begin
workingDirectory := CurrentProcess.instance.workingDirectory;
lastRootLength := 0;
lastRootPath := '';
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
currentRootPath := readln();
if currentRootPath.length <= 0 then break;
position := currentRootPath.indexOf(#$0020) + 1;
currentRootPath := FileSystemRoot.deescapeMountPoint(currentRootPath.substring(position, currentRootPath.indexOf(#$0020, position)));
if currentRootPath.endsWith('/') then currentRootPath := currentRootPath.substring(1, currentRootPath.length);
currentRootLength := currentRootPath.length;
if workingDirectory.startsWith(currentRootPath) and (workingDirectory[currentRootLength + 1] = '/') and (currentRootLength > lastRootLength) then begin
lastRootLength := currentRootLength;
lastRootPath := currentRootPath;
end;
until false;
finally
close();
end;
volumeDescriptor := FileSystemRootDescriptor.create(lastRootPath, workingDirectory.substring(lastRootLength + 1));
fileSystemRootArray[0] := volumeDescriptor;
fileSystemRootTable[lastRootPath] := volumeDescriptor;
end
{$ENDIF};
{%endregion}
{%region routines — time}
function timeDaysQuantityIn(year, month: int): int;
var
rem: int;
begin
case month + 1 of
1, 3, 5, 7, 8, 10, 12: begin
result := 31;
end;
4, 6, 9, 11: begin
result := 30;
end;
2: begin
inc(year);
if (year mod 100) = 0 then begin
rem := 400;
end else begin
rem := 4;
end;
if (year mod rem) = 0 then begin
result := 29;
exit;
end;
result := 28;
end;
else
result := 0;
end;
end;
function timeToPackedFields(timeInMillis: long): long;
var
year: int;
month: int;
day: int;
hour: int;
minute: int;
millisecond: int;
days: int;
rem: long;
begin
rem := timeInMillis div 86400000;
year := int((rem div 146097) * 400);
rem := rem mod 146097;
if rem >= 146096 then begin
inc(year, 399);
rem := 365;
end else begin
inc(year, int((rem div 36524) * 100));
rem := rem mod 36524;
inc(year, int((rem div 1461) * 4));
rem := rem mod 1461;
if rem >= 1460 then begin
inc(year, 3);
rem := 365;
end else begin
inc(year, int(rem div 365));
rem := rem mod 365;
end;
end;
month := 0;
repeat
days := timeDaysQuantityIn(year, month);
if rem < long(days) then break;
dec(rem, long(days));
inc(month);
until false;
inc(year);
inc(month);
day := int(rem) + 1;
rem := timeInMillis mod 86400000;
hour := int(rem div 3600000);
rem := rem mod 3600000;
minute := int(rem div 60000);
millisecond := int(rem mod 60000);
result := (long(year) shl 48) or (long(month) shl 40) or (long(day) shl 32) or (long(hour) shl 24) or (long(minute) shl 16) or long(millisecond);
end;
function timeElapsedInMillis(year, month, day, hour, minute, millisecond: int): long;
var
yer: int;
mnt: int;
days: int;
begin
dec(year);
dec(month);
dec(day);
year := year and $ffff;
yer := year;
days := 146097 * (year div 400);
year := year mod 400;
days := days + 36524 * (year div 100);
year := year mod 100;
days := days + 1461 * (year div 4) + 365 * (year mod 4) + day;
for mnt := 0 to month - 1 do days := days + timeDaysQuantityIn(yer, mnt);
result := 86400000 * long(days) + long(3600000 * hour + 60000 * minute + millisecond);
end;
{%endregion}
{%region routines — CPU}
function cpuReadBrandStringTo(dst: system.PAnsiChar): int; assembler; nostackframe;
asm
{$IFDEF WINDOWS}
dd $000010c8
mov qword [rbp-$08], rbx
mov qword [rbp-$10], rdi
mov rdi, rcx
{$ELSE}
dd $000008c8
mov qword [rbp-$08], rbx
{$ENDIF}
mov eax, $80000000
cpuid
test eax, $80000000
jz @00
cmp eax, $80000004
jge @01
@00: mov eax, $00000000
jmp @exit
@01: mov eax, $80000002
cpuid
mov dword [rdi+$00], eax
mov dword [rdi+$04], ebx
mov dword [rdi+$08], ecx
mov dword [rdi+$0c], edx
mov eax, $80000003
cpuid
mov dword [rdi+$10], eax
mov dword [rdi+$14], ebx
mov dword [rdi+$18], ecx
mov dword [rdi+$1c], edx
mov eax, $80000004
cpuid
mov dword [rdi+$20], eax
mov dword [rdi+$24], ebx
mov dword [rdi+$28], ecx
mov dword [rdi+$2c], edx
mov eax, $00000000
mov ecx, $00000030
mov rdx, rdi
cld
repne scasb
je @02
mov eax, $00000030
jmp @exit
@02: lea rax, [rdi-$01]
sub rax, rdx
@exit:
{$IFDEF WINDOWS}
mov rdi, [rbp-$10]
{$ENDIF}
mov rbx, [rbp-$08]
leave
end;
{%endregion}
{$IFDEF WINDOWS}
{%region OS API — additional}
const
TH32CS_SNAPPROCESS = system.DWord($00000002);
const
STATUS_FILE_IS_A_DIRECTORY = system.DWord($c00000ba);
STATUS_NOT_A_DIRECTORY = system.DWord($c0000103);
{$PACKRECORDS C}
type
PProcessEntry32 = ^ProcessEntry32;
ProcessEntry32 = record
dwSize: system.DWord;
cntUsage: system.DWord;
th32ProcessId: system.DWord;
th32DefaultHeapId: system.PtrUInt;
th32ModuleId: system.DWord;
cntThreads: system.DWord;
th32ParentProcessId: system.DWord;
pcPriClassBase: system.LongInt;
dwFlags: system.DWord;
szExeFile: array [0..windows.MAX_PATH - 1] of system.Char;
end;
{$PACKRECORDS DEFAULT}
{$CALLING STDCALL}
function createToolhelp32Snapshot(dwFlags, th32ProcessID: system.DWord): system.THandle; external windows.KERNEL32 name 'CreateToolhelp32Snapshot';
function process32First(hSnapshot: system.THandle; lpProcessEntry: PProcessEntry32): system.LongBool; external windows.KERNEL32 name 'Process32First';
function process32Next(hSnapshot: system.THandle; lpProcessEntry: PProcessEntry32): system.LongBool; external windows.KERNEL32 name 'Process32Next';
function setEnvironmentStringsW(newEnvironment: windows.LPWCH): windows.BOOL; external windows.KERNEL32 name 'SetEnvironmentStringsW';
function getLastStatus(): system.DWord; assembler; nostackframe;
asm
xor eax, eax
mov rax, gs:[rax+$30]
mov eax, [rax+$1250]
end;
{$CALLING REGISTER}
{%endregion}
{%region FileSystemRoot }
class function FileSystemRoot.isObjectPathCaseSensitive(): boolean;
begin
result := false;
end;
class function FileSystemRoot.isInternalPathFull(const internalPath: UnicodeString): boolean;
var
volumeLetter: uchar;
length: int;
begin
length := internalPath.length;
if (length >= 2) and (internalPath[2] = ':') and ((length <= 2) or (internalPath[3] = '\')) then begin
volumeLetter := internalPath[1];
result := (volumeLetter >= 'A') and (volumeLetter <= 'Z') or (volumeLetter >= 'a') and (volumeLetter <= 'z');
exit;
end;
result := false;
end;
class function FileSystemRoot.toInternalPath(const objectPath: UnicodeString): UnicodeString;
var
internalPath: UnicodeString;
begin
internalPath := objectPath.copy();
if internalPath.startsWith('/') then internalPath := internalPath.substring(2);
result := internalPath.replaceAll('/', '\');
end;
class function FileSystemRoot.toObjectPath(const internalPath: UnicodeString): UnicodeString;
var
objectPath: UnicodeString;
begin
objectPath := internalPath.copy();
if not objectPath.startsWith('\') then objectPath := '/' + objectPath;
result := objectPath.replaceAll('\', '/');
end;
class function FileSystemRoot.getUserCacheDir(): UnicodeString;
begin
result := getUserLocalDir();
end;
class function FileSystemRoot.getUserLocalDir(): UnicodeString;
label
break_label0;
var
pos: int;
checkPath: UnicodeString;
subdirPath: UnicodeString;
objectPath: UnicodeString;
internalPath: UnicodeString;
begin
begin
with CurrentProcess.instance.environment do begin
internalPath := variable['LocalAppData'];
if internalPath.length <= 0 then begin
internalPath := variable['UserProfile'];
if not internalPath.endsWith('\') then internalPath := internalPath + '\';
internalPath := internalPath + 'AppData\Local\';
goto break_label0;
end;
end;
if not internalPath.endsWith('\') then internalPath := internalPath + '\';
end;
break_label0:
objectPath := toObjectPath(internalPath);
with get(objectPath), fileSystem do begin
subdirPath := objectPath.substring(path.length + 1);
pos := 1;
repeat
pos := subdirPath.indexOf('/', pos + 1);
if pos < 1 then break;
checkPath := subdirPath.substring(1, pos);
if not isObjectExists(checkPath) then createDirectory(checkPath);
until false;
end;
result := objectPath;
end;
class function FileSystemRoot.getUserConfigDir(): UnicodeString;
label
break_label0;
var
pos: int;
checkPath: UnicodeString;
subdirPath: UnicodeString;
objectPath: UnicodeString;
internalPath: UnicodeString;
begin
begin
with CurrentProcess.instance.environment do begin
internalPath := variable['AppData'];
if internalPath.length <= 0 then begin
internalPath := variable['UserProfile'];
if not internalPath.endsWith('\') then internalPath := internalPath + '\';
internalPath := internalPath + 'AppData\Roaming\';
goto break_label0;
end;
end;
if not internalPath.endsWith('\') then internalPath := internalPath + '\';
end;
break_label0:
objectPath := toObjectPath(internalPath);
with get(objectPath), fileSystem do begin
subdirPath := objectPath.substring(path.length + 1);
pos := 1;
repeat
pos := subdirPath.indexOf('/', pos + 1);
if pos < 1 then break;
checkPath := subdirPath.substring(1, pos);
if not isObjectExists(checkPath) then createDirectory(checkPath);
until false;
end;
result := objectPath;
end;
class function FileSystemRoot.enumerate(): FileSystemRoot_Collection1d;
var
volumeLetter: uchar;
volumeIndex: int;
rootsLength: int;
internalRootPath: UnicodeString;
standardRootPath: UnicodeString;
rootsArray: FileSystemRoot_Array1d;
volumeDescriptor: FileSystemRoot;
begin
internalRootPath := UnicodeString(#0':\').copy();
volumeIndex := 0;
rootsLength := 0;
rootsArray := FileSystemRoot_Array1d(&Array.newTObject1d(int('Z') - int('A') + 1));
fileSystemRootMonitor.beginSynchronized();
try
for volumeLetter := 'A' to 'Z' do begin
internalRootPath[1] := volumeLetter;
if windows.getDriveTypeW(system.PWideChar(internalRootPath)) > windows.DRIVE_NO_ROOT_DIR then begin
volumeDescriptor := fileSystemRootArray[volumeIndex];
if volumeDescriptor = nil then begin
standardRootPath := '/' + volumeLetter + ':';
volumeDescriptor := FileSystemRootDescriptor.create(standardRootPath, '/');
fileSystemRootRegister(standardRootPath, volumeDescriptor);
end;
rootsArray[rootsLength] := volumeDescriptor;
inc(rootsLength);
end;
inc(volumeIndex);
end;
finally
fileSystemRootMonitor.endSynchronized();
end;
result := FileSystemRootCollection.create(rootsArray, rootsLength);
end;
class function FileSystemRoot.get(const objectPath: UnicodeString): FileSystemRoot;
var
volumeLetter: uchar;
volumeIndex: int;
length: int;
internalRootPath: UnicodeString;
standardRootPath: UnicodeString;
volumeDescriptor: FileSystemRoot;
begin
length := objectPath.length;
if (length < 3) or (objectPath[1] <> '/') then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectPath')
]));
end;
volumeLetter := objectPath[2];
if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) or (objectPath[3] <> ':') or (length >= 4) and (objectPath[4] <> '/') then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectPath')
]));
end;
volumeLetter := CoUChar.toUpperCase(volumeLetter);
volumeIndex := int(volumeLetter) - int('A');
fileSystemRootMonitor.beginSynchronized();
try
volumeDescriptor := fileSystemRootArray[volumeIndex];
if volumeDescriptor = nil then begin
standardRootPath := '/' + volumeLetter + ':';
internalRootPath := volumeLetter + ':\';
if not windows.getDriveTypeW(system.PWideChar(internalRootPath)) <= windows.DRIVE_NO_ROOT_DIR then begin
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalRootPath.toUTF8())
]));
end;
volumeDescriptor := FileSystemRootDescriptor.create(standardRootPath, '/');
fileSystemRootRegister(standardRootPath, volumeDescriptor);
end;
finally
fileSystemRootMonitor.endSynchronized();
end;
result := volumeDescriptor;
end;
constructor FileSystemRoot.create(const path: UnicodeString);
begin
inherited create();
fldPath := path;
end;
{%endregion}
{%region Environment }
procedure Environment.setVariable(const name, value: UnicodeString);
var
index: int;
vname: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
end;
if value.indexOf(#0) > 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
end;
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
if vars.contains(name) then begin
vars[name] := value;
exit;
end;
for index := vars.length - 1 downto 0 do begin
vname := vars.keyAt(index);
if name.equalsIgnoreCase(vname) then begin
vars[vname] := value;
exit;
end;
end;
vars[name] := value;
finally
emon.endSynchronized();
end;
end;
function Environment.getLength(): int;
begin
result := fldVariables.length;
end;
function Environment.getVariable(const name: UnicodeString): UnicodeString;
var
index: int;
value: UnicodeString;
vname: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
end;
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
value := vars[name];
if value.length > 0 then begin
result := value;
exit;
end;
for index := vars.length - 1 downto 0 do begin
vname := vars.keyAt(index);
if name.equalsIgnoreCase(vname) then begin
result := vars[vname];
exit;
end;
end;
finally
emon.endSynchronized();
end;
result := '';
end;
constructor Environment.create();
begin
inherited create();
fldMonitor := Mutex.create();
fldVariables := HashtableOfUnicodeStringToUnicodeString.create();
end;
destructor Environment.destroy;
begin
fldMonitor.free();
fldVariables.free();
inherited destroy;
end;
procedure Environment.clear();
var
emon: Mutex;
begin
emon := fldMonitor;
emon.beginSynchronized();
try
fldVariables.clear();
finally
emon.endSynchronized();
end;
end;
procedure Environment.assign(anot: Environment);
var
index: int;
vname: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
if anot = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
end;
if anot = self then exit;
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
vars.clear();
for index := 0 to anot.length - 1 do begin
vname := anot.variableAt(index);
vars[vname] := anot[vname];
end;
finally
emon.endSynchronized();
end;
end;
function Environment.equals(anot: TObject): boolean;
label
break_label0;
var
count: int;
aindex: int;
vindex: int;
aname: UnicodeString;
vname: UnicodeString;
avalue: UnicodeString;
vvalue: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
aenv: Environment;
emon: Mutex;
begin
if not(anot is Environment) then begin
result := false;
exit;
end;
if anot = self then begin
result := true;
exit;
end;
aenv := Environment(anot);
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
count := aenv.length;
if vars.length <> count then begin
result := false;
exit;
end;
dec(count);
for aindex := count downto 0 do begin
aname := aenv.variableAt(aindex);
avalue := aenv[aname];
vvalue := vars[aname];
if vvalue.length > 0 then begin
if vvalue.equalsIgnoreCase(avalue) then continue;
result := false;
exit;
end;
begin
for vindex := count downto 0 do begin
vname := vars.keyAt(vindex);
if vname.equalsIgnoreCase(aname) then goto break_label0;
end;
result := false;
exit;
end;
break_label0:
if not vars[vname].equalsIgnoreCase(avalue) then begin
result := false;
exit;
end;
end;
finally
emon.endSynchronized();
end;
result := true;
end;
function Environment.toString(): AnsiString;
var
index: int;
text: UnicodeString;
name: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
text := '';
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
for index := 0 to vars.length - 1 do begin
name := vars.keyAt(index);
text := text + name + '=' + vars[name] + CoUnicodeString.LINE_ENDING;
end;
finally
emon.endSynchronized();
end;
result := text.toUTF8();
end;
function Environment.isEmulation(): boolean;
begin
result := false;
end;
function Environment.isCaseSensitive(): boolean;
begin
result := false;
end;
function Environment.contains(const name: UnicodeString): boolean;
var
index: int;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
if vars.contains(name) then begin
result := true;
exit;
end;
for index := vars.length - 1 downto 0 do if vars.keyAt(index).equalsIgnoreCase(name) then begin
result := true;
exit;
end;
finally
emon.endSynchronized();
end;
result := false;
end;
function Environment.variableAt(index: int): UnicodeString;
var
emon: Mutex;
begin
emon := fldMonitor;
emon.beginSynchronized();
try
result := fldVariables.keyAt(index);
finally
emon.endSynchronized();
end;
end;
{%endregion}
{%region Process }
class function Process.hasInstance(): boolean;
var
thisModuleFullPath: UnicodeString;
anotModuleFullPath: UnicodeString;
getModuleFileNameExW: function(hProcess, hModule: system.THandle; lpFileName: system.PWideChar; nSize: system.DWord): system.DWord; stdcall;
moduleHandle: system.THandle;
moduleFullPath: system.PWideChar;
thisProcessId: system.DWord;
anotProcessId: system.DWord;
processInfo: ProcessEntry32;
processHandle: system.THandle;
snapshotHandle: system.THandle;
begin
moduleHandle := windows.getModuleHandleW(windows.KERNEL32);
Pointer(getModuleFileNameExW) := windows.getProcAddress(moduleHandle, 'GetModuleFileNameExW');
if getModuleFileNameExW = nil then begin
moduleHandle := windows.loadLibraryW('psapi.dll');
Pointer(getModuleFileNameExW) := windows.getProcAddress(moduleHandle, 'GetModuleFileNameExW');
end;
thisProcessId := windows.getCurrentProcessId();
moduleFullPath := nil;
snapshotHandle := createToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
try
moduleFullPath := system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1));
&Array.zeroRaw(processInfo, sizeof(ProcessEntry32));
processInfo.dwSize := sizeof(ProcessEntry32);
windows.getModuleFileNameW(0, moduleFullPath, windows.MAX_PATH + 1);
thisModuleFullPath := UnicodeString(moduleFullPath);
if process32First(snapshotHandle, @processInfo) then repeat
anotProcessId := processInfo.th32ProcessId;
if thisProcessId = anotProcessId then continue;
processHandle := windows.openProcess(windows.PROCESS_QUERY_INFORMATION or windows.PROCESS_QUERY_LIMITED_INFORMATION or windows.PROCESS_VM_READ, false, anotProcessId);
if processHandle = 0 then continue;
try
moduleFullPath[0] := #$0000;
getModuleFileNameExW(processHandle, 0, moduleFullPath, windows.MAX_PATH + 1);
finally
windows.closeHandle(processHandle);
end;
anotModuleFullPath := UnicodeString(moduleFullPath);
if thisModuleFullPath = anotModuleFullPath then begin
result := true;
exit;
end;
until not process32Next(snapshotHandle, @processInfo);
finally
windows.closeHandle(snapshotHandle);
if moduleFullPath <> nil then system.freeMemory(moduleFullPath);
end;
result := false;
end;
class function Process.createPipe(): ByteStream;
var
handleForRead: system.THandle;
handleForWrite: system.THandle;
begin
handleForRead := 0;
handleForWrite := 0;
if not windows.createPipe(@handleForRead, @handleForWrite, nil, $100000) then begin
raise errorOutOfResources;
end;
result := PipeStream.create(long(handleForRead), long(handleForWrite));
end;
class function Process.current(): Process;
begin
result := CurrentProcess.instance;
end;
procedure Process.setPriority(newPriority: int);
begin
if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
fldPriority := newPriority;
end;
procedure Process.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
begin
fldWorkingDirectory := newWorkingDirectory.copy();
end;
procedure Process.setCommandLine(const newCommandLine: UnicodeString_Array1d);
var
volumeLetter: uchar;
index: int;
count: int;
element: UnicodeString;
locCommandLine: UnicodeString_Array1d;
begin
count := system.length(newCommandLine);
locCommandLine := nil;
for index := 0 to count - 1 do begin
element := newCommandLine[index].copy();
if (element.indexOf('"') > 0) or (element.indexOf(#0) > 0) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
if index = 0 then begin
if (element.length <= 3) or (element[2] <> ':') or (element[3] <> '\') then begin
raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
end;
volumeLetter := element[1];
if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) then begin
raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
end;
locCommandLine := &Array.newUnicodeString1d(count);
end;
locCommandLine[index] := element;
end;
fldCommandLine := locCommandLine;
end;
procedure Process.setEnvironment(newEnvironment: Environment);
begin
fldEnvironment.assign(newEnvironment);
end;
procedure Process.setStandardInput(newStandardInput: ByteReader);
begin
if (newStandardInput <> nil) and not Lang.isInstance(newStandardInput, HandleInputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardInput := newStandardInput;
end;
procedure Process.setStandardOutput(newStandardOutput: ByteWriter);
begin
if (newStandardOutput <> nil) and not Lang.isInstance(newStandardOutput, HandleOutputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardOutput := newStandardOutput;
end;
procedure Process.setStandardError(newStandardError: ByteWriter);
begin
if (newStandardError <> nil) and not Lang.isInstance(newStandardError, HandleOutputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardError := newStandardError;
end;
procedure Process.clearStarting();
begin
fldStarting := false;
end;
function Process.getPriority(): int;
begin
result := fldPriority;
end;
function Process.getWorkingDirectory(): UnicodeString;
begin
result := fldWorkingDirectory.copy();
end;
function Process.createEnvironment(): Environment;
begin
result := nil;
end;
function Process.isStarting(): boolean; assembler; nostackframe;
asm
mov eax, true
xchg byte [rcx+offset fldStarting], al
movzx eax, al
end;
function Process.getCommandLine(): UnicodeString_Array1d;
var
index: int;
count: int;
commandLineData: UnicodeString_Array1d;
commandLineCopy: UnicodeString_Array1d;
begin
commandLineData := fldCommandLine;
count := system.length(commandLineData);
commandLineCopy := &Array.newUnicodeString1d(count);
for index := count - 1 downto 0 do begin
commandLineCopy[index] := commandLineData[index].copy();
end;
result := commandLineCopy;
end;
constructor Process.create();
var
locCurrentProcess: Process;
locEnvironment: Environment;
begin
inherited create();
locEnvironment := createEnvironment();
if locEnvironment = nil then begin
locEnvironment := platform.independent.osservices.Environment.create();
end;
locCurrentProcess := CurrentProcess.instance;
if locCurrentProcess <> nil then begin
locEnvironment.assign(locCurrentProcess.environment);
fldWorkingDirectory := locCurrentProcess.workingDirectory;
fldStandardInput := locCurrentProcess.fldStandardInput;
fldStandardOutput := locCurrentProcess.fldStandardOutput;
fldStandardError := locCurrentProcess.fldStandardError;
end;
fldProcessHandle := windows.INVALID_HANDLE_VALUE;
fldPriority := NORM_PRIORITY;
fldEnvironment := locEnvironment;
end;
destructor Process.destroy;
var
processHandle: system.THandle;
begin
processHandle := fldProcessHandle;
if processHandle <> windows.INVALID_HANDLE_VALUE then begin
windows.closeHandle(processHandle);
windows.closeHandle(fldThreadHandle);
end;
fldEnvironment.free();
inherited destroy;
end;
procedure Process.start();
var
index: int;
count: int;
processExitCode: int;
priorityClass: int;
locWorkingDirectory: UnicodeString;
commandLineString: UnicodeString;
environmentString: UnicodeString;
element: UnicodeString;
commandLineData: UnicodeString_Array1d;
environmentData: Environment;
processHandle: system.THandle;
stdInStream: HandleInputStream;
stdInHandle: system.THandle;
stdOutStream: HandleOutputStream;
stdOutHandle: system.THandle;
stdErrStream: HandleOutputStream;
stdErrHandle: system.THandle;
startupInfo: windows.TStartupInfoW;
processInfo: windows.TProcessInformation;
begin
if isStarting() then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
try
processExitCode := 0;
processHandle := fldProcessHandle;
if (processHandle <> windows.INVALID_HANDLE_VALUE) and windows.getExitCodeProcess(processHandle, @processExitCode) and (processExitCode = STILL_ACTIVE) then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
commandLineData := fldCommandLine;
count := system.length(commandLineData);
if count <= 0 then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process.command-line'));
end;
{ приоритет }
case fldPriority of
MIN_PRIORITY - 0:
priorityClass := windows.IDLE_PRIORITY_CLASS;
LOW_PRIORITY - 1..
LOW_PRIORITY - 0:
priorityClass := windows.BELOW_NORMAL_PRIORITY_CLASS;
NORM_PRIORITY - 1..
NORM_PRIORITY + 1:
priorityClass := windows.NORMAL_PRIORITY_CLASS;
HIGH_PRIORITY + 0..
HIGH_PRIORITY + 1:
priorityClass := windows.ABOVE_NORMAL_PRIORITY_CLASS;
else
priorityClass := windows.HIGH_PRIORITY_CLASS;
end;
{ рабочая папка }
locWorkingDirectory := FileSystemRoot.toInternalPath(fldWorkingDirectory);
{ командная строка }
commandLineString := '';
for index := 0 to count - 1 do begin
if index > 0 then begin
commandLineString := commandLineString + ' ';
end;
element := commandLineData[index];
if (element.length <= 0) or (element.indexOf(' ') > 0) then begin
element := '"' + element + '"';
end;
commandLineString := commandLineString + element;
end;
{ переменные среды }
environmentData := fldEnvironment;
count := environmentData.length;
environmentString := '';
for index := 0 to count - 1 do begin
element := environmentData.variableAt(index);
environmentString := environmentString + element + '=' + environmentData[element] + #0;
end;
environmentString := environmentString + #0;
{ стандартные потоки ввода-вывода }
stdInStream := HandleInputStream(Lang.cast(fldStandardInput, HandleInputStream));
stdInHandle := windows.INVALID_HANDLE_VALUE;
if stdInStream <> nil then begin
stdInHandle := system.THandle(stdInStream.fldHandle);
windows.setHandleInformation(stdInHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
end;
stdOutStream := HandleOutputStream(Lang.cast(fldStandardOutput, HandleOutputStream));
stdOutHandle := windows.INVALID_HANDLE_VALUE;
if stdOutStream <> nil then begin
stdOutHandle := system.THandle(stdOutStream.fldHandle);
windows.setHandleInformation(stdOutHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
end;
stdErrStream := HandleOutputStream(Lang.cast(fldStandardError, HandleOutputStream));
stdErrHandle := windows.INVALID_HANDLE_VALUE;
if stdErrStream <> nil then begin
stdErrHandle := system.THandle(stdErrStream.fldHandle);
windows.setHandleInformation(stdErrHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
end;
{ создание процесса }
with startupInfo do begin
cb := sizeof(windows.TStartupInfoW);
lpReserved := nil;
lpDesktop := nil;
lpTitle := nil;
dwX := 0;
dwY := 0;
dwXSize := 0;
dwYSize := 0;
dwXCountChars := 0;
dwYCountChars := 0;
dwFillAttribute := 0;
dwFlags := windows.STARTF_USESTDHANDLES;
wShowWindow := 0;
cbReserved2 := 0;
lpReserved2 := nil;
hStdInput := stdInHandle;
hStdOutput := stdOutHandle;
hStdError := stdErrHandle;
end;
with processInfo do begin
hProcess := 0;
hThread := 0;
dwProcessId := 0;
dwThreadId := 0;
end;
if not windows.createProcessW(
nil,
system.PWideChar(commandLineString),
nil,
nil,
true,
windows.CREATE_UNICODE_ENVIRONMENT or system.DWord(priorityClass),
system.PWideChar(environmentString),
system.PWideChar(locWorkingDirectory),
@startupInfo,
@processInfo
) then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(commandLineData[0].toUTF8())
]));
windows.ERROR_DIRECTORY:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(locWorkingDirectory.toUTF8())
]));
else
raise errorOutOfResources;
end;
if processHandle <> windows.INVALID_HANDLE_VALUE then begin
windows.closeHandle(processHandle);
windows.closeHandle(fldThreadHandle);
end;
with processInfo do begin
fldProcessHandle := hProcess;
fldThreadHandle := hThread;
end;
finally
clearStarting();
end;
end;
procedure Process.join();
var
processHandle: system.THandle;
begin
processHandle := fldProcessHandle;
if processHandle = windows.INVALID_HANDLE_VALUE then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
windows.waitForSingleObject(processHandle, windows.INFINITE);
end;
procedure Process.join(timeInMillis: long);
var
processHandle: system.THandle;
begin
processHandle := fldProcessHandle;
if processHandle = windows.INVALID_HANDLE_VALUE then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
if timeInMillis < 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
end;
if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
if timeInMillis = 0 then timeInMillis := -1;
windows.waitForSingleObject(processHandle, system.DWord(timeInMillis));
end;
procedure Process.join(timeInMillis: long; timeInNanos: int);
var
processHandle: system.THandle;
begin
processHandle := fldProcessHandle;
if processHandle = windows.INVALID_HANDLE_VALUE then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
if timeInMillis < 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
end;
if (timeInNanos < 0) or (timeInNanos > 999999) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInNanos') ]));
end;
if (timeInMillis < CoLong.MAX_VALUE) and (timeInNanos >= 500000) or (timeInMillis = 0) and (timeInNanos > 0) then inc(timeInMillis);
if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
if timeInMillis = 0 then timeInMillis := -1;
windows.waitForSingleObject(processHandle, system.DWord(timeInMillis));
end;
function Process.isTerminated(): boolean;
var
processHandle: system.THandle;
begin
processHandle := fldProcessHandle;
result := (processHandle <> windows.INVALID_HANDLE_VALUE) and (windows.waitForSingleObject(processHandle, 0) = windows.WAIT_OBJECT_0);
end;
function Process.getExitCode(): int;
var
processExitCode: int;
processHandle: system.THandle;
begin
processExitCode := 0;
processHandle := fldProcessHandle;
if (processHandle = windows.INVALID_HANDLE_VALUE) or not windows.getExitCodeProcess(processHandle, @processExitCode) then begin
result := 0;
exit;
end;
result := processExitCode;
end;
{%endregion}
{%region TimeBase }
class function TimeBase.currentOffsetInMillis(): int;
var
timeZoneInfo: windows.TTimeZoneInformation;
begin
&Array.zeroRaw(timeZoneInfo, sizeof(windows.TTimeZoneInformation));
windows.getTimeZoneInformation(@timeZoneInfo);
result := -60000 * timeZoneInfo.bias;
end;
class function TimeBase.currentTimeInMillis(): long;
var
time: windows.TSystemTime;
begin
&Array.zeroRaw(time, sizeof(windows.TSystemTime));
windows.getSystemTime(@time);
with time do result := timeElapsedInMillis(year, month, day, hour, minute, 1000 * second + millisecond);
end;
{%endregion}
{%region MemoryManager }
class procedure MemoryManager.deallocate(region: MemoryRegion; flags: int);
var
address: long;
size: long;
freeFlags: system.DWord;
reference: Pointer absolute address;
begin
if region = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('region') ]));
end;
if not(region is MemoryRegionDescriptor) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('region') ]));
end;
address := region.address;
size := region.size;
if (flags and RESERVED) <> 0 then begin
freeFlags := windows.MEM_DECOMMIT;
end else begin
size := 0;
freeFlags := windows.MEM_RELEASE;
end;
windows.virtualFree(reference, size, freeFlags);
end;
class function MemoryManager.enumerate(): MemoryRegion_Collection1d;
var
regionsLength: int;
address: long;
size: long;
regionsData: MemoryRegion_Array1d;
regionsCopy: MemoryRegion_Array1d;
memoryInfo: windows.TMemoryBasicInformation;
reference: Pointer absolute address;
begin
address := MEMORY_START_ADDRESS;
regionsLength := 0;
regionsData := MemoryRegion_Array1d(&Array.newIUnknown1d($7f));
&Array.zeroRaw(memoryInfo, sizeof(windows.TMemoryBasicInformation));
while windows.virtualQuery(reference, @memoryInfo, sizeof(windows.TMemoryBasicInformation)) > 0 do begin
reference := memoryInfo.baseAddress;
size := memoryInfo.regionSize;
if memoryInfo.state <> windows.MEM_FREE then begin
if regionsLength = system.length(regionsData) then begin
regionsCopy := MemoryRegion_Array1d(&Array.newIUnknown1d((regionsLength shl 1) or 1));
&Array.copyUnknowns(regionsData, 0, regionsCopy, 0, regionsLength);
regionsData := regionsCopy;
end;
regionsData[regionsLength] := MemoryRegionDescriptor.create(address, size);
inc(regionsLength);
end;
inc(address, size);
if address >= MEMORY_LIMIT_ADDRESS then break;
end;
result := MemoryRegionCollection.create(regionsData, regionsLength);
end;
class function MemoryManager.allocate(address, size: long; flags: int): MemoryRegion;
var
allocationFlags: system.DWord;
allocationProtect: system.DWord;
reference: Pointer absolute address;
begin
if (size < 0) or (size > MEMORY_LIMIT_ADDRESS - MEMORY_START_ADDRESS) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('size') ]));
end;
if (address <> 0) and ((address + size > MEMORY_LIMIT_ADDRESS) or (address < MEMORY_START_ADDRESS)) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('address') ]));
end;
address := address and -PAGE_SIZE;
if size = 0 then size := 1;
size := size + (-size and (PAGE_SIZE - 1));
if (flags and RESERVED) <> 0 then begin
allocationFlags := windows.MEM_RESERVE;
if (flags and (READABLE + WRITEABLE + EXECUTABLE)) <> 0 then inc(allocationFlags, windows.MEM_COMMIT);
end else begin
allocationFlags := windows.MEM_COMMIT;
end;
if (flags and DOWN) <> 0 then begin
inc(allocationFlags, windows.MEM_TOP_DOWN);
end;
case flags and (READABLE + WRITEABLE + EXECUTABLE) of
READABLE:
allocationProtect := windows.PAGE_READONLY;
WRITEABLE,
READABLE + WRITEABLE:
allocationProtect := windows.PAGE_READWRITE;
EXECUTABLE:
allocationProtect := windows.PAGE_EXECUTE;
EXECUTABLE + READABLE:
allocationProtect := windows.PAGE_EXECUTE_READ;
EXECUTABLE + WRITEABLE,
EXECUTABLE + READABLE + WRITEABLE:
allocationProtect := windows.PAGE_EXECUTE_READWRITE;
else
allocationProtect := windows.PAGE_NOACCESS;
end;
reference := windows.virtualAlloc(reference, system.PtrUInt(size), allocationFlags, allocationProtect);
if reference = nil then case windows.getLastError() of
windows.NO_ERROR: ;
windows.ERROR_NOT_ENOUGH_MEMORY:
raise errorOutOfResources;
else
raise IllegalArgumentException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.passed'));
end;
result := MemoryRegionDescriptor.create(address, size);
end;
{%endregion}
{%region SystemInfo }
class procedure SystemInfo.initialize();
var
ntMajorVersion: int absolute $7ffe026c;
ntMinorVersion: int absolute $7ffe0270;
ntBuildNumber: int absolute $7ffe0260;
cpuBrandLength: int;
cpuBrandArray: system.PAnsiChar;
sysInfo: windows.TSystemInfo;
begin
{ количество потоков процессора }
&Array.zeroRaw(sysInfo, sizeof(windows.TSystemInfo));
windows.getSystemInfo(@sysInfo);
cpuNumberOfCores := int(sysInfo.dwNumberOfProcessors);
{ название модели процессора }
cpuBrandArray := system.PAnsiChar(system.getMemory($30 * sizeof(char)));
try
cpuBrandLength := cpuReadBrandStringTo(cpuBrandArray);
cpuBrandString := AnsiString.create(cpuBrandLength);
&Array.copyRaw(cpuBrandArray^, cpuBrandString[1], cpuBrandLength * sizeof(char));
finally
system.freeMemory(cpuBrandArray);
end;
{ версия операционной системы }
osVersion := CoInt.toString(ntMajorVersion) + '.' + CoInt.toString(ntMinorVersion) + '.' + CoInt.toString(ntBuildNumber);
end;
class function SystemInfo.getProcessorCodeBits(): int;
begin
result := 64;
end;
class function SystemInfo.getProcessorNumberOfCores(): int;
begin
result := cpuNumberOfCores;
end;
class function SystemInfo.getProcessorBrandString(): AnsiString;
begin
result := cpuBrandString;
end;
class function SystemInfo.getOperatingSystemName(): AnsiString;
begin
result := 'Microsoft Windows';
end;
class function SystemInfo.getOperatingSystemVersion(): AnsiString;
begin
result := osVersion;
end;
{%endregion}
{%region MemoryRegionDescriptor }
constructor MemoryRegionDescriptor.create(address, size: long);
begin
inherited create();
fldAddress := address;
fldSize := size;
end;
procedure MemoryRegionDescriptor.setProtectionBits(newProtectionBits: int);
var
address: long;
newProtect: system.DWord;
oldProtect: system.DWord;
reference: Pointer absolute address;
begin
address := fldAddress;
case newProtectionBits and (MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.EXECUTABLE) of
MemoryManager.READABLE:
newProtect := windows.PAGE_READONLY;
MemoryManager.WRITEABLE,
MemoryManager.READABLE + MemoryManager.WRITEABLE:
newProtect := windows.PAGE_READWRITE;
MemoryManager.EXECUTABLE:
newProtect := windows.PAGE_EXECUTE;
MemoryManager.EXECUTABLE + MemoryManager.READABLE:
newProtect := windows.PAGE_EXECUTE_READ;
MemoryManager.EXECUTABLE + MemoryManager.WRITEABLE,
MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE:
newProtect := windows.PAGE_EXECUTE_READWRITE;
else
newProtect := windows.PAGE_NOACCESS;
end;
oldProtect := 0;
windows.virtualProtect(reference, system.PtrUInt(fldSize), newProtect, @oldProtect);
end;
function MemoryRegionDescriptor.equals(anot: TObject): boolean;
var
mreg: MemoryRegionDescriptor;
begin
if not(anot is MemoryRegionDescriptor) then begin
result := false;
exit;
end;
if anot = self then begin
result := true;
exit;
end;
mreg := MemoryRegionDescriptor(anot);
result := (fldAddress = mreg.fldAddress) and (fldSize = mreg.fldSize);
end;
function MemoryRegionDescriptor.getHashCode(): long;
begin
result := fldAddress xor CoLong.rol(fldSize, 35);
end;
function MemoryRegionDescriptor.toString(): AnsiString;
var
protect: int;
address: long;
flags: char_Array1d;
begin
protect := getProtectionBits();
address := fldAddress;
flags := &Array.newChar1d(4);
if (protect and MemoryManager.READABLE) <> 0 then begin
flags[0] := 'r';
end else begin
flags[0] := '-';
end;
if (protect and MemoryManager.WRITEABLE) <> 0 then begin
flags[1] := 'w';
end else begin
flags[1] := '-';
end;
if (protect and MemoryManager.EXECUTABLE) <> 0 then begin
flags[2] := 'x';
end else begin
flags[2] := '-';
end;
if (protect and MemoryManager.SHARED) <> 0 then begin
flags[3] := 's';
end else begin
flags[3] := 'p';
end;
result := CoLong.toHexString(address) + '-' + CoLong.toHexString(address + fldSize) + #$20 + AnsiString.create(flags);
end;
function MemoryRegionDescriptor.getProtectionBit(index: int): boolean;
begin
result := (getProtectionBits() and (1 shl index)) <> 0;
end;
function MemoryRegionDescriptor.getProtectionBits(): int;
var
address: long;
memoryInfo: windows.TMemoryBasicInformation;
reference: Pointer absolute address;
begin
address := fldAddress;
&Array.zeroRaw(memoryInfo, sizeof(windows.TMemoryBasicInformation));
if windows.virtualQuery(reference, @memoryInfo, sizeof(windows.TMemoryBasicInformation)) <= 0 then begin
result := 0;
exit;
end;
case memoryInfo.protect of
windows.PAGE_READONLY:
result := MemoryManager.READABLE;
windows.PAGE_READWRITE:
result := MemoryManager.READABLE + MemoryManager.WRITEABLE;
windows.PAGE_WRITECOPY:
result := MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.SHARED;
windows.PAGE_EXECUTE:
result := MemoryManager.EXECUTABLE;
windows.PAGE_EXECUTE_READ:
result := MemoryManager.EXECUTABLE + MemoryManager.READABLE;
windows.PAGE_EXECUTE_READWRITE:
result := MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE;
windows.PAGE_EXECUTE_WRITECOPY:
result := MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.SHARED;
else
result := 0;
end;
end;
function MemoryRegionDescriptor.getAddress(): long;
begin
result := fldAddress;
end;
function MemoryRegionDescriptor.getSize(): long;
begin
result := fldSize;
end;
function MemoryRegionDescriptor.pointerTo(offset: long): Pointer;
var
address: long;
reference: Pointer absolute address;
begin
if (offset < 0) or (offset > fldSize) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('offset') ]));
end;
address := fldAddress + offset;
result := reference;
end;
{%endregion}
{%region FileSystemRootDescriptor }
function FileSystemRootDescriptor.getName(): UnicodeString;
var
locName: UnicodeString;
volName: system.PWideChar;
begin
locName := '';
volName := system.PWideChar(system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1)));
try
if windows.getVolumeInformationW(system.PWideChar(fldCanonical), volName, windows.MAX_PATH + 1, nil, nil, nil, nil, 0) then begin
locName := UnicodeString(volName);
end;
finally
system.freeMemory(volName);
end;
result := locName;
end;
function FileSystemRootDescriptor.getFileSystem(): FileSystem;
begin
result := fldFileSystem;
end;
constructor FileSystemRootDescriptor.create(const argPath, argCurrentDirectory: UnicodeString);
begin
inherited create(argPath);
fldCanonical := argPath.substring(2) + '\';
fldFileSystem := VolumeFileSystem.create(toInternalPath(argPath), argCurrentDirectory.replaceAll('/', '\'));
end;
destructor FileSystemRootDescriptor.destroy;
begin
fldFileSystem.free();
inherited destroy;
end;
{%endregion}
{%region VolumeFileSystem }
class function VolumeFileSystem.isComponentReserved(const component: UnicodeString): boolean;
const
reserved: array [0..27] of UnicodeString = (
'con', 'prn', 'aux', 'nul',
'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9', 'com'#$00b9, 'com'#$00b2, 'com'#$00b3,
'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9', 'lpt'#$00b9, 'lpt'#$00b2, 'lpt'#$00b3
);
var
index: int;
length: int;
name: UnicodeString;
begin
length := component.length;
if length > 0 then case component[length] of
#$0020: begin
result := true;
exit;
end;
'.': begin
result := (length <> 1) and ((length <> 2) or (component[1] <> '.'));
exit;
end;
end;
index := component.indexOf('.');
if index <= 0 then begin
name := component;
end else begin
name := component.substring(1, index);
end;
length := name.length;
if (length < 3) or (length > 4) then begin
result := false;
exit;
end;
for index := 0 to 27 do if reserved[index].equalsIgnoreCase(name) then begin
result := true;
exit;
end;
result := false;
end;
procedure VolumeFileSystem.tryMakeCurrentReliableBlock();
label
break_label0;
var
length: int;
internalFullPath: UnicodeString;
oldHandle: system.THandle;
newHandle: system.THandle;
cmon: Mutex;
begin
cmon := fldCurrentMonitor;
cmon.beginSynchronized();
try
if fldCurrentUnreliableBlock then begin
internalFullPath := fldCurrentDirectory;
length := internalFullPath.length;
if length <= 1 then begin
fldCurrentUnreliableBlock := false;
goto break_label0;
end;
internalFullPath := fldInternalRootPath + internalFullPath.substring(1, length);
newHandle := windows.createFileW(
system.PWideChar(internalFullPath),
{ windows.DELETE } $00010000,
windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
if newHandle <> windows.INVALID_HANDLE_VALUE then begin
fldCurrentUnreliableBlock := false;
end else newHandle := windows.createFileW(
system.PWideChar(internalFullPath),
0,
0,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
oldHandle := system.THandle(fldCurrentHandle);
if oldHandle <> windows.INVALID_HANDLE_VALUE then windows.closeHandle(oldHandle);
fldCurrentHandle := long(newHandle);
end;
break_label0:
finally
cmon.endSynchronized();
end;
end;
function VolumeFileSystem.toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
var
internalName: UnicodeString;
begin
internalName := toInternalName(objectName);
if isInternalNameFull(internalName) then begin
result := internalName;
exit;
end;
result := fldCurrentDirectory + internalName;
end;
function VolumeFileSystem.makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
label
break_label0;
var
attributes: int;
dotPosition: int;
reductionPosition: int;
internalRootPath: UnicodeString;
internalFullPath: UnicodeString;
begin
internalRootPath := fldInternalRootPath;
internalFullPath := volumeFullPath;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('\.\', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('\.') then break;
dotPosition := internalFullPath.length - 1;
end;
internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
until false;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('\..\', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('\..') then break;
dotPosition := internalFullPath.length - 2;
end;
begin
if dotPosition > 1 then begin
attributes := int(windows.getFileAttributesW(system.PWideChar(internalRootPath + internalFullPath.substring(1, dotPosition))));
if (attributes <> int(windows.INVALID_FILE_ATTRIBUTES)) and ((attributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0) then goto break_label0;
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND: ;
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(
AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]
));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
result := '';
exit;
end;
break_label0:
reductionPosition := internalFullPath.lastIndexOf('\', dotPosition - 1);
internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
dotPosition := reductionPosition;
until false;
result := internalRootPath + internalFullPath;
end;
function VolumeFileSystem.makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
var
position: int;
checkFullPath: UnicodeString;
begin
position := volumeFullPath.lastIndexOf('\') + 1;
checkFullPath := makeInternalFullPathAndCheckExist(volumeFullPath.substring(1, position), AT_DIRECTORY) + '.';
if windows.getFileAttributesW(system.PWideChar(checkFullPath)) = windows.INVALID_FILE_ATTRIBUTES then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(checkFullPath.toUTF8())
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := checkFullPath.substring(1, checkFullPath.length) + volumeFullPath.substring(position);
end;
function VolumeFileSystem.makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
label
break_label0;
var
attributes: int;
dotPosition: int;
reductionPosition: int;
internalRootPath: UnicodeString;
internalFullPath: UnicodeString;
begin
internalRootPath := fldInternalRootPath;
internalFullPath := volumeFullPath;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('\.\', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('\.') then break;
dotPosition := internalFullPath.length - 1;
end;
internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
until false;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('\..\', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('\..') then break;
dotPosition := internalFullPath.length - 2;
end;
begin
if dotPosition > 1 then begin
attributes := int(windows.getFileAttributesW(system.PWideChar(internalRootPath + internalFullPath.substring(1, dotPosition))));
if (attributes <> int(windows.INVALID_FILE_ATTRIBUTES)) and ((attributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0) then goto break_label0;
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND: ;
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(
AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]
));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
case argumentType of
AT_FILE:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
AT_DIRECTORY:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
else
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
end;
end;
break_label0:
reductionPosition := internalFullPath.lastIndexOf('\', dotPosition - 1);
internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
dotPosition := reductionPosition;
until false;
result := internalRootPath + internalFullPath;
end;
constructor VolumeFileSystem.create(const rootPath, currentDirectory: UnicodeString);
begin
inherited create();
fldCurrentUnreliableBlock := true;
fldCurrentHandle := long(windows.INVALID_HANDLE_VALUE);
fldCanonicalRootPath := rootPath.toUTF8() + '\';
fldInternalRootPath := rootPath;
fldCurrentDirectory := currentDirectory;
fldCurrentMonitor := Mutex.create();
tryMakeCurrentReliableBlock();
end;
destructor VolumeFileSystem.destroy;
var
currentHandle: system.THandle;
begin
currentHandle := system.THandle(fldCurrentHandle);
if currentHandle <> windows.INVALID_HANDLE_VALUE then begin
windows.closeHandle(currentHandle);
end;
fldCurrentMonitor.free();
inherited destroy;
end;
procedure VolumeFileSystem.changeCurrentDirectory(const directoryPath: UnicodeString);
label
break_label0;
var
unreliableBlock: boolean;
rootPathLength: int;
internalFullPath: UnicodeString;
newCurrentDirectory: UnicodeString;
oldHandle: system.THandle;
newHandle: system.THandle;
cmon: Mutex;
begin
if directoryPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryPath')
]));
end;
if not isObjectNameValid(directoryPath) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryPath')
]));
end;
internalFullPath := toVolumeFullPath(directoryPath);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryPath')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
if internalFullPath.endsWith('\') then internalFullPath := internalFullPath.substring(1, internalFullPath.length);
rootPathLength := fldInternalRootPath.length;
newCurrentDirectory := internalFullPath.substring(rootPathLength + 1) + '\';
cmon := fldCurrentMonitor;
cmon.beginSynchronized();
try
if not fldCurrentDirectory.equalsIgnoreCase(newCurrentDirectory) then begin
unreliableBlock := false;
newHandle := windows.INVALID_HANDLE_VALUE;
if internalFullPath.length > rootPathLength then begin
unreliableBlock := true;
newHandle := windows.createFileW(
system.PWideChar(internalFullPath),
{ windows.DELETE } $00010000,
windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
if newHandle <> windows.INVALID_HANDLE_VALUE then begin
unreliableBlock := false;
end else newHandle := windows.createFileW(
system.PWideChar(internalFullPath),
0,
0,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
begin
if newHandle <> windows.INVALID_HANDLE_VALUE then begin
if (windows.getFileAttributesW(system.PWideChar(internalFullPath)) and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0 then goto break_label0;
windows.closeHandle(newHandle);
end else begin
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND: ;
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(
AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]
));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
break_label0:
end;
oldHandle := system.THandle(fldCurrentHandle);
if oldHandle <> windows.INVALID_HANDLE_VALUE then windows.closeHandle(oldHandle);
fldCurrentUnreliableBlock := unreliableBlock;
fldCurrentDirectory := newCurrentDirectory;
fldCurrentHandle := long(newHandle);
end;
finally
cmon.endSynchronized();
end;
end;
procedure VolumeFileSystem.readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
var
objectReadTime: boolean;
standardNameLength: int;
internalAttributes: int;
internalCreationTime: long;
internalLastWriteTime: long;
internalLastAccessTime: long;
internalFullPath: UnicodeString;
objectHandle: system.THandle;
begin
standardNameLength := objectName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
internalFullPath := toVolumeFullPath(objectName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
objectHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_READ,
windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE or windows.FILE_SHARE_DELETE,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
if objectHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
internalAttributes := windows.getFileAttributesW(system.PWideChar(internalFullPath));
internalCreationTime := 0;
internalLastWriteTime := 0;
internalLastAccessTime := 0;
objectReadTime := windows.getFileTime(objectHandle, @internalCreationTime, @internalLastAccessTime, @internalLastWriteTime);
if not windows.closeHandle(objectHandle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if (internalAttributes = int(windows.INVALID_FILE_ATTRIBUTES)) or not objectReadTime then begin
raise ObjectReadAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.read-attributes'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
if objectAttr = nil then exit;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (internalAttributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (internalAttributes and windows.FILE_ATTRIBUTE_READONLY) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_ARCHIVE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE, (internalAttributes and windows.FILE_ATTRIBUTE_ARCHIVE) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_HIDDEN) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN, (internalAttributes and windows.FILE_ATTRIBUTE_HIDDEN) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_SYSTEM) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM, (internalAttributes and windows.FILE_ATTRIBUTE_SYSTEM) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_CREATION_TIME) then begin
objectAttr.setLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME, VolumeRequiredAttributes.toObjectTime(internalCreationTime));
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, VolumeRequiredAttributes.toObjectTime(internalLastWriteTime));
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, VolumeRequiredAttributes.toObjectTime(internalLastAccessTime));
end;
end;
procedure VolumeFileSystem.writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
var
objectWriteTime: boolean;
objectWriteAttr: boolean;
standardNameLength: int;
internalAttributes: int;
internalCreationTime: long;
internalLastWriteTime: long;
internalLastAccessTime: long;
internalFullPath: UnicodeString;
objectHandle: system.THandle;
begin
standardNameLength := objectName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
internalFullPath := toVolumeFullPath(objectName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if objectAttr = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('objectAttr') ]));
end;
internalAttributes := 0;
internalCreationTime := 0;
internalLastWriteTime := 0;
internalLastAccessTime := 0;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY) then begin
inc(internalAttributes, windows.FILE_ATTRIBUTE_DIRECTORY);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY) then begin
inc(internalAttributes, windows.FILE_ATTRIBUTE_READONLY);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_ARCHIVE) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE) then begin
inc(internalAttributes, windows.FILE_ATTRIBUTE_ARCHIVE);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_HIDDEN) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN) then begin
inc(internalAttributes, windows.FILE_ATTRIBUTE_HIDDEN);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_SYSTEM) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM) then begin
inc(internalAttributes, windows.FILE_ATTRIBUTE_SYSTEM);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_CREATION_TIME) then begin
internalCreationTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME));
if internalCreationTime < 0 then internalCreationTime := 0;
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
internalLastWriteTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME));
if internalLastWriteTime < 0 then internalLastWriteTime := 0;
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
internalLastAccessTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME));
if internalLastAccessTime < 0 then internalLastAccessTime := 0;
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
objectWriteAttr := windows.setFileAttributesW(system.PWideChar(internalFullPath), FILE_ATTRIBUTE_NORMAL);
objectHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_WRITE,
windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE or windows.FILE_SHARE_DELETE,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
0
);
if objectHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
objectWriteTime := windows.setFileTime(objectHandle, @internalCreationTime, @internalLastAccessTime, @internalLastWriteTime);
objectWriteAttr := windows.setFileAttributesW(system.PWideChar(internalFullPath), system.DWord(internalAttributes)) and objectWriteAttr;
if not windows.closeHandle(objectHandle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if not objectWriteAttr or not objectWriteTime then begin
raise ObjectWriteAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.write-attributes'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
end;
procedure VolumeFileSystem.move(const objectOldName, objectNewName: UnicodeString);
var
standardNameLength: int;
internalOldFullPath: UnicodeString;
internalNewFullPath: UnicodeString;
begin
standardNameLength := objectOldName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectOldName')
]));
end;
if (standardNameLength <= 0) or objectOldName.endsWith('/') or not isObjectNameValid(objectOldName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectOldName')
]));
end;
internalOldFullPath := toVolumeFullPath(objectOldName);
if internalOldFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectOldName')
]));
end;
standardNameLength := objectNewName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectNewName')
]));
end;
if (standardNameLength <= 0) or objectNewName.endsWith('/') or not isObjectNameValid(objectNewName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectNewName')
]));
end;
internalNewFullPath := toVolumeFullPath(objectNewName);
if internalNewFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectNewName')
]));
end;
tryMakeCurrentReliableBlock();
internalOldFullPath := makeInternalFullPathAndCheckExist(internalOldFullPath, AT_OBJECT);
internalNewFullPath := makeInternalFullPathAndCheckCreat(internalNewFullPath);
if not windows.moveFileW(system.PWideChar(internalOldFullPath), system.PWideChar(internalNewFullPath)) then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalOldFullPath.toUTF8())
]));
windows.ERROR_FILE_EXISTS,
windows.ERROR_ALREADY_EXISTS:
raise MoveOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'move'), [
CoAnsiString.create(internalOldFullPath.toUTF8()), CoAnsiString.create(internalNewFullPath.toUTF8())
]));
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure VolumeFileSystem.deleteFile(const fileName: UnicodeString);
var
standardNameLength: int;
internalFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
if not windows.deleteFileW(system.PWideChar(internalFullPath)) then begin
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.deletion'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
end;
procedure VolumeFileSystem.deleteDirectory(const directoryName: UnicodeString);
var
standardNameLength: int;
internalFullPath: UnicodeString;
begin
standardNameLength := directoryName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryName')
]));
end;
internalFullPath := toVolumeFullPath(directoryName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
tryMakeCurrentReliableBlock();
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
if not windows.removeDirectoryW(system.PWideChar(internalFullPath)) then begin
case windows.getLastError() of
windows.ERROR_DIR_NOT_EMPTY: ;
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_NOT_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
end;
procedure VolumeFileSystem.createDirectory(const directoryName: UnicodeString);
var
standardNameLength: int;
internalFullPath: UnicodeString;
begin
standardNameLength := directoryName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryName')
]));
end;
internalFullPath := toVolumeFullPath(directoryName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
if not windows.createDirectoryW(system.PWideChar(internalFullPath), nil) then case windows.getLastError() of
windows.ERROR_FILE_EXISTS,
windows.ERROR_ALREADY_EXISTS:
raise DirectoryCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.creation'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function VolumeFileSystem.isAttached(): boolean;
begin
windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
if not windows.getVolumeInformationA(system.PAnsiChar(fldCanonicalRootPath), nil, 0, nil, nil, nil, nil, 0) then case windows.getLastError() of
windows.ERROR_NOT_READY: begin
result := false;
exit;
end;
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := true;
end;
function VolumeFileSystem.isReadOnly(): boolean;
var
flags: int;
rootPath: AnsiString;
begin
flags := 0;
rootPath := fldCanonicalRootPath;
windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
if not windows.getVolumeInformationA(system.PAnsiChar(rootPath), nil, 0, nil, nil, @flags, nil, 0) then case windows.getLastError() of
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(rootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := (flags and { windows.FILE_READ_ONLY_VOLUME } $00080000) <> 0;
end;
function VolumeFileSystem.isObjectNameCaseSensitive(): boolean;
var
rootPath: AnsiString;
begin
rootPath := fldCanonicalRootPath;
windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
if not windows.getVolumeInformationA(system.PAnsiChar(rootPath), nil, 0, nil, nil, nil, nil, 0) then case windows.getLastError() of
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(rootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := false;
end;
function VolumeFileSystem.isObjectExists(const objectName: UnicodeString): boolean;
var
standardNameLength: int;
internalFullPath: UnicodeString;
begin
standardNameLength := objectName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
internalFullPath := toVolumeFullPath(objectName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
internalFullPath := makeInternalFullPathAndIsExist(internalFullPath);
if internalFullPath = '' then begin
result := false;
exit;
end;
if windows.getFileAttributesW(system.PWideChar(internalFullPath)) = windows.INVALID_FILE_ATTRIBUTES then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND: begin
result := false;
exit;
end;
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := true;
end;
function VolumeFileSystem.isObjectNameValid(const objectName: UnicodeString): boolean;
var
index: int;
length: int;
beginIndex: int;
endIndex: int;
begin
length := objectName.length;
if (length > OBJECT_NAME_MAXIMUM_LENGTH) or (objectName.indexOf('//') > 0) then begin
result := false;
exit;
end;
for index := 0 to length - 1 do case objectName[index + 1] of
#$0000..#$001f, '<', '>', ':', '"', '\', '|', '?', '*': begin
result := false;
exit;
end;
end;
if length > 0 then begin
beginIndex := 0;
if objectName[1] = '/' then inc(beginIndex);
while (beginIndex >= 0) and (beginIndex < length) do begin
endIndex := objectName.indexOf('/', beginIndex + 1) - 1;
if endIndex < 0 then endIndex := length;
if isComponentReserved(objectName.substring(beginIndex + 1, endIndex + 1)) then begin
result := false;
exit;
end;
beginIndex := endIndex + 1;
end;
end;
result := true;
end;
function VolumeFileSystem.isInternalNameFull(const internalName: UnicodeString): boolean;
begin
result := internalName.startsWith('\');
end;
function VolumeFileSystem.isInternalNameValid(const internalName: UnicodeString): boolean;
var
index: int;
length: int;
beginIndex: int;
endIndex: int;
begin
length := internalName.length;
if (length > OBJECT_NAME_MAXIMUM_LENGTH) or (internalName.indexOf('\\') > 0) then begin
result := false;
exit;
end;
for index := 0 to length - 1 do case internalName[index + 1] of
#$0000..#$001f, '<', '>', ':', '"', '/', '|', '?', '*': begin
result := false;
exit;
end;
end;
if length > 0 then begin
beginIndex := 0;
if internalName[1] = '\' then inc(beginIndex);
while (beginIndex >= 0) and (beginIndex < length) do begin
endIndex := internalName.indexOf('\', beginIndex + 1) - 1;
if endIndex < 0 then endIndex := length;
if isComponentReserved(internalName.substring(beginIndex + 1, endIndex + 1)) then begin
result := false;
exit;
end;
beginIndex := endIndex + 1;
end;
end;
result := true;
end;
function VolumeFileSystem.getObjectNameMaximumLength(): int;
begin
result := OBJECT_NAME_MAXIMUM_LENGTH;
end;
function VolumeFileSystem.totalSize(): long;
var
bytesAvailable: long;
bytesTotal: long;
bytesFree: long;
rootPath: AnsiString;
begin
bytesAvailable := 0;
bytesTotal := 0;
bytesFree := 0;
rootPath := fldCanonicalRootPath;
if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(rootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := bytesTotal;
end;
function VolumeFileSystem.usedSize(): long;
var
bytesAvailable: long;
bytesTotal: long;
bytesFree: long;
rootPath: AnsiString;
begin
bytesAvailable := 0;
bytesTotal := 0;
bytesFree := 0;
rootPath := fldCanonicalRootPath;
if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(rootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := bytesTotal - bytesFree;
end;
function VolumeFileSystem.availableSize(): long;
var
bytesAvailable: long;
bytesTotal: long;
bytesFree: long;
rootPath: AnsiString;
begin
bytesAvailable := 0;
bytesTotal := 0;
bytesFree := 0;
rootPath := fldCanonicalRootPath;
if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(rootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := bytesFree;
end;
function VolumeFileSystem.getCurrentDirectory(): UnicodeString;
begin
result := toObjectName(fldCurrentDirectory);
end;
function VolumeFileSystem.toInternalName(const objectName: UnicodeString): UnicodeString;
begin
result := objectName.replaceAll('/', '\');
end;
function VolumeFileSystem.toObjectName(const internalName: UnicodeString): UnicodeString;
begin
result := internalName.replaceAll('\', '/');
end;
function VolumeFileSystem.findFirst(const objectPath: UnicodeString): ObjectEnumeration;
var
internalFullPath: UnicodeString;
findHandle: system.THandle;
findData: windows.TWin32FindDataW;
begin
if objectPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectPath')
]));
end;
if not isObjectNameValid(objectPath) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectPath')
]));
end;
internalFullPath := toVolumeFullPath(objectPath);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectPath')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
if internalFullPath.endsWith('\') then internalFullPath := internalFullPath + '*';
&Array.zeroRaw(findData, sizeof(windows.TWin32FindDataW));
findHandle := windows.findFirstFileW(system.PWideChar(internalFullPath), @findData);
if findHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := VolumeObjectEnumeration.create(long(findHandle), findData);
end;
function VolumeFileSystem.createFile(const fileName: UnicodeString): ByteWriter;
var
standardNameLength: int;
internalFullPath: UnicodeString;
fileHandle: system.THandle;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
fileHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_WRITE,
0,
nil,
windows.CREATE_NEW,
windows.FILE_ATTRIBUTE_NORMAL,
0
);
if fileHandle = windows.INVALID_HANDLE_VALUE then begin
case windows.getLastError() of
windows.ERROR_FILE_EXISTS,
windows.ERROR_ALREADY_EXISTS: ;
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.rewriteFile(const fileName: UnicodeString): ByteWriter;
var
standardNameLength: int;
internalFullPath: UnicodeString;
fileHandle: system.THandle;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
fileHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_WRITE,
0,
nil,
windows.CREATE_ALWAYS,
windows.FILE_ATTRIBUTE_NORMAL,
0
);
if fileHandle = windows.INVALID_HANDLE_VALUE then begin
case windows.getLastError() of
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.openFileForAppend(const fileName: UnicodeString): ByteWriter;
var
standardNameLength: int;
internalFullPath: UnicodeString;
fileHandle: system.THandle;
filePosition: LongRecord;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
fileHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_WRITE,
0,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0
);
if fileHandle = windows.INVALID_HANDLE_VALUE then begin
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
filePosition.value := 0;
windows.setFilePointer(fileHandle, filePosition.slow, @(filePosition.shigh), windows.FILE_END);
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.openFileForRead(const fileName: UnicodeString): ByteReader;
var
standardNameLength: int;
internalFullPath: UnicodeString;
fileHandle: system.THandle;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
fileHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_READ,
windows.FILE_SHARE_READ,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0
);
if fileHandle = windows.INVALID_HANDLE_VALUE then begin
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
result := FileInputStream.create(long(fileHandle));
end;
function VolumeFileSystem.openFile(const fileName: UnicodeString): ByteStream;
var
standardNameLength: int;
internalFullPath: UnicodeString;
fileHandle: system.THandle;
begin
standardNameLength := fileName.length;
if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := toVolumeFullPath(fileName);
if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
fileHandle := windows.createFileW(
system.PWideChar(internalFullPath),
windows.GENERIC_READ or windows.GENERIC_WRITE,
0,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0
);
if fileHandle = windows.INVALID_HANDLE_VALUE then begin
case windows.getLastError() of
windows.ERROR_FILE_NOT_FOUND,
windows.ERROR_PATH_NOT_FOUND:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
windows.ERROR_WRITE_PROTECT:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
windows.ERROR_NOT_READY:
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(internalFullPath.toUTF8())
]));
end;
result := FileBidirectStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.newAttributes(): Attributes;
begin
result := VolumeObjectAttributes.create();
end;
{%endregion}
{%region VolumeRequiredAttributes }
class function VolumeRequiredAttributes.toObjectTime(internalTime: long): long;
begin
if internalTime < 0 then begin
result := CoLong.MIN_VALUE;
exit;
end;
result := internalTime div 10000 + $2debe1767000;
end;
class function VolumeRequiredAttributes.toInternalTime(objectTime: long): long;
begin
if (objectTime < $2debe1767000) or (objectTime > $000374c83ed9f865) then begin
result := CoLong.MIN_VALUE;
exit;
end;
result := (objectTime - $2debe1767000) * 10000;
end;
{%endregion}
{%region VolumeObjectAttributes }
class procedure VolumeObjectAttributes.initialize();
var
index: int;
begin
attrIds := [ B_READ_ONLY, B_HIDDEN, B_SYSTEM, '', B_DIRECTORY, B_ARCHIVE, '', '', L_CREATION_TIME, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME ];
index := system.length(attrIds);
attrHashes := &Array.newLong1d(index);
for index := index - 1 downto 0 do begin
attrHashes[index] := (CoAnsiString.create(attrIds[index]) as RefCountInterface).getHashCode();
end;
end;
class procedure VolumeObjectAttributes.finalize();
begin
attrIds := nil;
attrHashes := nil;
end;
class procedure VolumeObjectAttributes.stringAttributeIdIsInvalid(const attributeId: AnsiString);
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
if attributeId.startsWith('s') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
class function VolumeObjectAttributes.booleanAttributeIdToIndex(const attributeId: AnsiString): int;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 8);
if (index >= 0) and (attributeId = attrIds[index]) then begin
result := index - 0;
exit;
end;
if attributeId.startsWith('b') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
class function VolumeObjectAttributes.longAttributeIdToIndex(const attributeId: AnsiString): int;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 8, 8);
if (index >= 0) and (attributeId = attrIds[index]) then begin
result := index - 8;
exit;
end;
if attributeId.startsWith('l') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
constructor VolumeObjectAttributes.create();
begin
inherited create();
fldTimes := &Array.newLong1d(3);
end;
procedure VolumeObjectAttributes.setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
var
mask: int;
begin
mask := 1 shl booleanAttributeIdToIndex(attributeId);
if attributeValue then begin
fldAttributes := fldAttributes or mask;
exit;
end;
fldAttributes := fldAttributes and not mask;
end;
procedure VolumeObjectAttributes.setLongAttribute(const attributeId: AnsiString; attributeValue: long);
var
index: int;
begin
index := longAttributeIdToIndex(attributeId);
fldTimes[index] := attributeValue;
end;
procedure VolumeObjectAttributes.setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
begin
stringAttributeIdIsInvalid(attributeId);
end;
function VolumeObjectAttributes.isSupportedAttributeId(const attributeId: AnsiString): boolean;
var
index: int;
begin
if attributeId.length <= 0 then begin
result := false;
exit;
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
result := (index >= 0) and (attributeId = attrIds[index]);
end;
function VolumeObjectAttributes.getBooleanAttribute(const attributeId: AnsiString): boolean;
var
mask: int;
begin
mask := 1 shl booleanAttributeIdToIndex(attributeId);
result := (fldAttributes and mask) <> 0;
end;
function VolumeObjectAttributes.getLongAttribute(const attributeId: AnsiString): long;
var
index: int;
begin
index := longAttributeIdToIndex(attributeId);
result := fldTimes[index];
end;
function VolumeObjectAttributes.getStringAttribute(const attributeId: AnsiString): UnicodeString;
begin
stringAttributeIdIsInvalid(attributeId);
result := ''; { недостижимый код }
end;
function VolumeObjectAttributes.displayName(const attributeId: AnsiString): UnicodeString;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
if (index >= 0) and (attributeId = attrIds[index]) then case index of
0: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.read-only');
exit;
end;
1: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.hidden');
exit;
end;
2: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.system');
exit;
end;
4: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.directory');
exit;
end;
5: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.archive');
exit;
end;
8: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.creation-time');
exit;
end;
9: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-access-time');
exit;
end;
10: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-write-time');
exit;
end;
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
function VolumeObjectAttributes.getSupportedAttributeIds(): AnsiString_Array1d;
begin
result := [ B_DIRECTORY, B_READ_ONLY, B_ARCHIVE, B_HIDDEN, B_SYSTEM, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME, L_CREATION_TIME ];
end;
{%endregion}
{%region VolumeObjectEnumeration }
procedure VolumeObjectEnumeration.setAttributes(const info);
var
attr: int;
findData: windows.TWin32FindDataW absolute info;
begin
attr := int(findData.dwFileAttributes);
name := UnicodeString(system.PWideChar(@(findData.cFileName)));
if (attr and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0 then begin
size := 0;
end else begin
size := long(findData.nFileSizeLow) + (long(findData.nFileSizeHigh) shl 32);
end;
setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (attr and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (attr and windows.FILE_ATTRIBUTE_READONLY) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE, (attr and windows.FILE_ATTRIBUTE_ARCHIVE) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN, (attr and windows.FILE_ATTRIBUTE_HIDDEN) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM, (attr and windows.FILE_ATTRIBUTE_SYSTEM) <> 0);
setLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftCreationTime)));
setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftLastWriteTime)));
setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftLastAccessTime)));
end;
constructor VolumeObjectEnumeration.create(handle: long; const info);
begin
inherited create(VolumeObjectAttributes.create());
fldHandle := handle;
setAttributes(info);
end;
procedure VolumeObjectEnumeration.close();
var
handle: system.THandle;
begin
handle := system.THandle(fldHandle);
destroy;
if not windows.findClose(handle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function VolumeObjectEnumeration.findNext(): boolean;
var
findData: windows.TWin32FindDataW;
begin
&Array.zeroRaw(findData, sizeof(windows.TWin32FindDataW));
if windows.findNextFileW(system.THandle(fldHandle), @findData) then begin
setAttributes(findData);
result := true;
exit;
end;
if windows.getLastError() <> windows.ERROR_NO_MORE_FILES then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := false;
end;
{%endregion}
{%region HandleInputStream }
constructor HandleInputStream.create(handle: long);
begin
inherited create();
fldHandle := handle;
end;
procedure HandleInputStream.close();
begin
end;
function HandleInputStream.skip(bytesQuantity: long): long;
var
data: int;
skiped: long;
readed: system.DWord;
handle: system.THandle;
begin
if bytesQuantity <= 0 then begin
result := 0;
exit;
end;
data := 0;
skiped := 0;
readed := 0;
handle := system.THandle(fldHandle);
repeat
if not windows.readFile(handle, data, 1, readed, nil) then begin
if skiped > 0 then break;
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then break;
inc(skiped);
dec(bytesQuantity);
until bytesQuantity > 0;
result := skiped;
end;
function HandleInputStream.read(): int;
var
data: int;
readed: system.DWord;
begin
data := 0;
readed := 0;
if not windows.readFile(system.THandle(fldHandle), data, 1, readed, nil) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then begin
result := -1;
exit;
end;
result := data;
end;
function HandleInputStream.read(const dst: byte_Array1d): int;
begin
result := read(dst, 0, system.length(dst));
end;
function HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
readed: system.DWord;
begin
&Array.checkBounds(dst, offset, length);
if length <= 0 then begin
result := 0;
exit;
end;
readed := 0;
if not windows.readFile(system.THandle(fldHandle), dst[offset], system.DWord(length), readed, nil) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then begin
result := -1;
exit;
end;
result := int(readed);
end;
{%endregion}
{%region HandleOutputStream }
constructor HandleOutputStream.create(handle: long; const rootPath: AnsiString);
begin
inherited create();
fldHandle := handle;
fldRootPath := rootPath;
end;
procedure HandleOutputStream.close();
begin
end;
procedure HandleOutputStream.flush();
begin
end;
procedure HandleOutputStream.write(byteData: int);
var
writed: system.DWord;
begin
writed := 0;
if not windows.writeFile(system.THandle(fldHandle), byteData, 1, writed, nil) then case windows.getLastError() of
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if writed < 1 then begin
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
end;
end;
procedure HandleOutputStream.write(const src: byte_Array1d);
begin
write(src, 0, system.length(src));
end;
procedure HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
var
writed: system.DWord;
begin
&Array.checkBounds(src, offset, length);
if length <= 0 then exit;
writed := 0;
if not windows.writeFile(system.THandle(fldHandle), src[offset], system.DWord(length), writed, nil) then case windows.getLastError() of
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if writed < system.DWord(length) then begin
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
end;
end;
{%endregion}
{%region HandleBidirectStream }
constructor HandleBidirectStream.create(handleForRead, handleForWrite: long);
begin
inherited create();
fldHandleForRead := handleForRead;
fldHandleForWrite := handleForWrite;
end;
destructor HandleBidirectStream.destroy;
begin
fldReader.free();
fldWriter.free();
inherited destroy;
end;
procedure HandleBidirectStream.close();
var
handleForRead: system.THandle;
handleForWrite: system.THandle;
begin
handleForRead := system.THandle(fldHandleForRead);
handleForWrite := system.THandle(fldHandleForWrite);
destroy;
if not windows.closeHandle(handleForRead) or (handleForRead <> handleForWrite) and not windows.closeHandle(handleForWrite) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function HandleBidirectStream.getReader(): ByteReader;
begin
result := fldReader;
end;
function HandleBidirectStream.getWriter(): ByteWriter;
begin
result := fldWriter;
end;
{%endregion}
{%region FileSeekExtension }
constructor FileSeekExtension.create(handle: long);
begin
inherited create();
fldHandle := handle;
end;
function FileSeekExtension.available(): long;
var
handle: system.THandle;
fileSize: LongRecord;
filePosition: LongRecord;
begin
handle := system.THandle(fldHandle);
fileSize.value := 0;
fileSize.ulow := windows.getFileSize(handle, @(fileSize.uhigh));
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := fileSize.value - filePosition.value;
end;
function FileSeekExtension.seek(offset: long; from: SeekFrom): long;
var
locLength: long;
locPosition: long;
handle: system.THandle;
filePosition: LongRecord;
begin
if (from < SeekFrom.sfBegin) or (from > SeekFrom.sfEnd) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('from') ]));
end;
handle := system.THandle(fldHandle);
filePosition.value := 0;
filePosition.ulow := windows.getFileSize(handle, @(filePosition.uhigh));
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locLength := filePosition.value;
case from of
SeekFrom.sfBegin:
locPosition := offset;
SeekFrom.sfEnd:
locPosition := offset + locLength;
else
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locPosition := offset + filePosition.value;
end;
if locPosition < 0 then locPosition := 0;
if locPosition > locLength then locPosition := locLength;
filePosition.value := locPosition;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_BEGIN);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := filePosition.value;
end;
function FileSeekExtension.position(): long;
var
filePosition: LongRecord;
begin
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := filePosition.value;
end;
function FileSeekExtension.size(): long;
var
fileSize: LongRecord;
begin
fileSize.value := 0;
fileSize.ulow := windows.getFileSize(system.THandle(fldHandle), @(fileSize.uhigh));
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := fileSize.value;
end;
{%endregion}
{%region FileInputStream }
constructor FileInputStream.create(handle: long; seekable: FileSeekExtension);
begin
inherited create(handle);
if seekable <> nil then begin
fldOwnedSeekable := false;
end else begin
seekable := FileSeekExtension.create(handle);
fldOwnedSeekable := true;
end;
fldExtensions := [ self, seekable ];
end;
destructor FileInputStream.destroy;
begin
if fldOwnedSeekable then begin
fldExtensions[1].free();
end;
inherited destroy;
end;
procedure FileInputStream.close();
var
handle: system.THandle;
begin
handle := system.THandle(fldHandle);
destroy;
if not windows.closeHandle(handle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileInputStream.reset();
begin
FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
end;
procedure FileInputStream.mark(transferLimit: int);
var
filePosition: LongRecord;
begin
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
fldMarked := 0;
exit;
end;
fldMarked := filePosition.value;
end;
function FileInputStream.skip(bytesQuantity: long): long;
var
length: long;
position: long;
remainder: long;
handle: system.THandle;
filePosition: LongRecord;
begin
if bytesQuantity <= 0 then begin
result := 0;
exit;
end;
handle := system.THandle(fldHandle);
filePosition.value := 0;
filePosition.ulow := windows.getFileSize(handle, @(filePosition.uhigh));
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
length := filePosition.value;
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
position := filePosition.value;
remainder := length - position;
if bytesQuantity > remainder then bytesQuantity := remainder;
filePosition.value := position + bytesQuantity;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_BEGIN);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := filePosition.value - position;
end;
{%endregion}
{%region FileOutputStream }
constructor FileOutputStream.create(handle: long; const rootPath: AnsiString; seekable: FileSeekExtension; reader: FileInputStream);
begin
inherited create(handle, rootPath);
if seekable <> nil then begin
fldOwnedSeekable := false;
end else begin
seekable := FileSeekExtension.create(handle);
fldOwnedSeekable := true;
end;
fldReader := reader;
fldExtensions := [ self, seekable ];
end;
destructor FileOutputStream.destroy;
begin
if fldOwnedSeekable then begin
fldExtensions[1].free();
end;
inherited destroy;
end;
procedure FileOutputStream.close();
var
handle: system.THandle;
begin
handle := system.THandle(fldHandle);
destroy;
if not windows.closeHandle(handle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileOutputStream.flush();
begin
if not windows.flushFileBuffers(system.THandle(fldHandle)) then case windows.getLastError() of
windows.ERROR_DISK_FULL:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileOutputStream.truncate();
var
locLength: long;
handle: system.THandle;
filePosition: LongRecord absolute locLength;
reader: FileInputStream;
begin
handle := system.THandle(fldHandle);
if not windows.setEndOfFile(handle) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
reader := fldReader;
if fldMarked > locLength then fldMarked := locLength;
if (reader <> nil) and (reader.fldMarked > locLength) then reader.fldMarked := locLength;
end;
procedure FileOutputStream.reset();
begin
FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
end;
procedure FileOutputStream.mark(transferLimit: int);
var
filePosition: LongRecord;
begin
filePosition.value := 0;
filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
if windows.getLastError() <> windows.NO_ERROR then begin
fldMarked := 0;
exit;
end;
fldMarked := filePosition.value;
end;
{%endregion}
{%region CurrentEnvironment }
procedure CurrentEnvironment.update();
var
index: int;
str: UnicodeString;
name: UnicodeString;
value: UnicodeString;
ptr: system.PWideChar;
block: system.PWideChar;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
vars.clear();
block := windows.getEnvironmentStringsW();
try
ptr := block;
while ptr[0] <> #$0000 do begin
str := UnicodeString(ptr);
index := str.indexOf('=');
name := str.substring(1, index).trim();
value := str.substring(index + 1).trim();
vars[name] := value;
inc(ptr, str.length + 1);
end;
finally
windows.freeEnvironmentStringsW(block);
end;
finally
emon.endSynchronized();
end;
end;
procedure CurrentEnvironment.setVariable(const name, value: UnicodeString);
begin
if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
end;
if value.indexOf(#0) > 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
end;
if windows.setEnvironmentVariableW(system.PWideChar(name), system.PWideChar(value)) then update();
end;
constructor CurrentEnvironment.create();
begin
inherited create();
update();
end;
procedure CurrentEnvironment.clear();
begin
if setEnvironmentStringsW(#$0000) then update();
end;
procedure CurrentEnvironment.assign(anot: Environment);
var
index: int;
name: UnicodeString;
block: UnicodeString;
begin
if anot = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
end;
if anot = self then exit;
block := '';
for index := 0 to anot.length - 1 do begin
name := anot.variableAt(index);
block := block + name + '=' + anot[name] + #$0000;
end;
block := block + #$0000;
if setEnvironmentStringsW(system.PWideChar(block)) then update();
end;
{%endregion}
{%region CurrentProcess }
class procedure CurrentProcess.initialize();
begin
instance := CurrentProcess.create();
end;
class procedure CurrentProcess.finalize();
begin
instance.free();
end;
class function CurrentProcess.parseCommandLine(): UnicodeString_Array1d;
const
SPACE = uchar(#$0020);
QUOTE = uchar(#$0022);
var
quoted: boolean;
curr: uchar;
prev: uchar;
index: int;
length: int;
commandLineLength: int;
commandLineChars: UnicodeString;
argument: UnicodeString;
arguments: UnicodeString_Array1d;
moduleFileName: system.PWideChar;
begin
commandLineChars := UnicodeString(system.PWideChar(windows.getCommandLineW()));
commandLineLength := commandLineChars.length;
length := 0;
quoted := false;
prev := SPACE;
for index := 0 to commandLineLength - 1 do begin
curr := commandLineChars[index + 1];
if curr = QUOTE then begin
quoted := not quoted;
end else
if not quoted and (prev <> SPACE) and (curr = SPACE) then begin
inc(length);
end;
if (commandLineLength - index = 1) and (curr <> SPACE) then begin
inc(length);
end;
prev := curr;
end;
arguments := &Array.newUnicodeString1d(length);
argument := '';
length := 0;
quoted := false;
prev := SPACE;
for index := 0 to commandLineLength - 1 do begin
curr := commandLineChars[index + 1];
if curr = QUOTE then begin
quoted := not quoted;
end else
if quoted or (curr <> SPACE) then begin
argument := argument + curr;
end else
if prev <> SPACE then begin
arguments[length] := argument;
inc(length);
argument := '';
end;
if (commandLineLength - index = 1) and (curr <> SPACE) then begin
arguments[length] := argument;
inc(length);
end;
prev := curr;
end;
moduleFileName := system.PWideChar(system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1)));
try
windows.getModuleFileNameW(0, moduleFileName, windows.MAX_PATH + 1);
if length <= 0 then begin
result := [ UnicodeString(moduleFileName) ];
exit;
end;
arguments[0] := UnicodeString(moduleFileName);
finally
system.freeMemory(moduleFileName);
end;
result := arguments;
end;
procedure CurrentProcess.setPriority(newPriority: int);
var
priorityClass: int;
begin
if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
case newPriority of
MIN_PRIORITY - 0:
priorityClass := windows.IDLE_PRIORITY_CLASS;
LOW_PRIORITY - 1..
LOW_PRIORITY - 0:
priorityClass := windows.BELOW_NORMAL_PRIORITY_CLASS;
NORM_PRIORITY - 1..
NORM_PRIORITY + 1:
priorityClass := windows.NORMAL_PRIORITY_CLASS;
HIGH_PRIORITY + 0..
HIGH_PRIORITY + 1:
priorityClass := windows.ABOVE_NORMAL_PRIORITY_CLASS;
else
priorityClass := windows.HIGH_PRIORITY_CLASS;
end;
windows.setPriorityClass(windows.getCurrentProcess(), system.DWord(priorityClass));
end;
procedure CurrentProcess.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
var
success: boolean;
directory: UnicodeString;
cdmon: Mutex;
begin
if not newWorkingDirectory.startsWith('/') then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
directory := FileSystemRoot.toInternalPath(newWorkingDirectory);
cdmon := fldWorkingDirectoryMonitor;
cdmon.beginSynchronized();
try
success := windows.setCurrentDirectoryW(system.PWideChar(directory));
finally
cdmon.endSynchronized();
end;
if not success then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
end;
procedure CurrentProcess.setCommandLine(const newCommandLine: UnicodeString_Array1d);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardInput(newStandardInput: ByteReader);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardOutput(newStandardOutput: ByteWriter);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardError(newStandardError: ByteWriter);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
function CurrentProcess.getPriority(): int;
begin
case windows.getPriorityClass(windows.getCurrentProcess()) of
windows.IDLE_PRIORITY_CLASS:
result := MIN_PRIORITY;
windows.BELOW_NORMAL_PRIORITY_CLASS:
result := LOW_PRIORITY;
windows.ABOVE_NORMAL_PRIORITY_CLASS:
result := HIGH_PRIORITY;
windows.HIGH_PRIORITY_CLASS:
result := MAX_PRIORITY;
else
result := NORM_PRIORITY;
end;
end;
function CurrentProcess.getWorkingDirectory(): UnicodeString;
var
length: int;
directory: UnicodeString;
cdmon: Mutex;
begin
cdmon := fldWorkingDirectoryMonitor;
cdmon.beginSynchronized();
try
length := windows.getCurrentDirectoryW(0, nil) - 1;
directory := UnicodeString.create(length);
windows.getCurrentDirectoryW(length + 1, @(directory[1]));
finally
cdmon.endSynchronized();
end;
if not directory.endsWith('\') then directory := directory + '\';
result := FileSystemRoot.toObjectPath(directory);
end;
function CurrentProcess.createEnvironment(): Environment;
begin
result := CurrentEnvironment.create();
end;
constructor CurrentProcess.create();
var
locStdIn: HandleInputStream;
locStdOut: HandleOutputStream;
locStdErr: HandleOutputStream;
begin
inherited create();
locStdIn := HandleInputStream.create(long(windows.getStdHandle(windows.STD_INPUT_HANDLE)));
locStdOut := HandleOutputStream.create(long(windows.getStdHandle(windows.STD_OUTPUT_HANDLE)));
locStdErr := HandleOutputStream.create(long(windows.getStdHandle(windows.STD_ERROR_HANDLE)));
fldStarting := true;
fldCommandLine := parseCommandLine();
fldStandardInput := locStdIn;
fldStandardOutput := locStdOut;
fldStandardError := locStdErr;
fldStandardInputRef := locStdIn;
fldStandardOutputRef := locStdOut;
fldStandardErrorRef := locStdErr;
fldWorkingDirectoryMonitor := Mutex.create();
end;
destructor CurrentProcess.destroy;
begin
fldStandardInputRef.free();
fldStandardOutputRef.free();
fldStandardErrorRef.free();
fldWorkingDirectoryMonitor.free();
inherited destroy;
end;
function CurrentProcess.isTerminated(): boolean;
begin
result := false;
end;
function CurrentProcess.getExitCode(): int;
begin
result := STILL_ACTIVE;
end;
{%endregion}
{$ELSE}
{%region OS API — additional}
const
LIBC = 'libc';
const
SYSCALL_NR_FSYNC = long(74);
SYSCALL_NR_GETTIMEOFDAY = long(96);
SYSCALL_NR_STATFS = long(137);
const
DT_DIR = int(4);
{$PACKRECORDS C}
type
PTM = ^TM;
TM = record
tm_sec: int;
tm_min: int;
tm_hour: int;
tm_mday: int;
tm_mon: int;
tm_year: int;
tm_wday: int;
tm_yday: int;
tm_isdst: int;
tm_gmtoff: long;
tm_tm_zone: system.PAnsiChar;
end;
{$PACKRECORDS DEFAULT}
{$CALLING CDECL}
function doSyscall(__sysnr, __arg1: long): long; external name 'FPC_SYSCALL1';
function doSyscall(__sysnr, __arg1, __arg2: long): long; external name 'FPC_SYSCALL2';
function gmtTime(__time: baseunix.PTime_t; __result: PTM): PTM; external LIBC name 'gmtime_r';
function localTime(__time: baseunix.PTime_t; __result: PTM): PTM; external LIBC name 'localtime_r';
function unixTime(__time: PTM): baseunix.Time_t; external LIBC name 'mktime';
{$CALLING REGISTER}
{%endregion}
{%region FileSystemRoot }
class function FileSystemRoot.deescapeMountPoint(const point: UnicodeString): UnicodeString;
var
character: uchar;
code: int;
length: int;
position: int;
deescaped: UnicodeString;
begin
deescaped := point;
length := deescaped.length - 3;
position := length + 1;
repeat
position := deescaped.lastIndexOf('\', position - 1);
if (position <= 0) or (position > length) then break;
code:= 0;
character := deescaped[position + 1];
if (character < '0') or (character > '7') then continue;
code := (code shl 3) + (int(character) - int('0'));
character := deescaped[position + 2];
if (character < '0') or (character > '7') then continue;
code := (code shl 3) + (int(character) - int('0'));
character := deescaped[position + 3];
if (character < '0') or (character > '7') then continue;
code := (code shl 3) + (int(character) - int('0'));
deescaped := deescaped.substring(1, position) + uchar(code) + deescaped.substring(position + 4);
until false;
result := deescaped;
end;
class function FileSystemRoot.getRootPathOf(const objectPath: UnicodeString): UnicodeString;
var
position: int;
objectLength: int;
lastRootLength: int;
currentRootLength: int;
lastRootPath: UnicodeString;
currentRootPath: UnicodeString;
begin
objectLength := objectPath.length;
lastRootLength := 0;
lastRootPath := '';
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
currentRootPath := readln();
if currentRootPath.length <= 0 then break;
position := currentRootPath.indexOf(#$0020) + 1;
currentRootPath := deescapeMountPoint(currentRootPath.substring(position, currentRootPath.indexOf(#$0020, position)));
if currentRootPath.endsWith('/') then currentRootPath := currentRootPath.substring(1, currentRootPath.length);
currentRootLength := currentRootPath.length;
if objectPath.startsWith(currentRootPath) and ((objectLength <= currentRootLength) or (objectPath[currentRootLength + 1] = '/')) and (currentRootLength > lastRootLength) then begin
lastRootLength := currentRootLength;
lastRootPath := currentRootPath;
end;
until false;
finally
close();
end;
result := lastRootPath;
end;
class function FileSystemRoot.isObjectPathCaseSensitive(): boolean;
begin
result := true;
end;
class function FileSystemRoot.isInternalPathFull(const internalPath: UnicodeString): boolean;
begin
result := internalPath.startsWith('/');
end;
class function FileSystemRoot.toInternalPath(const objectPath: UnicodeString): UnicodeString;
begin
result := objectPath.copy();
end;
class function FileSystemRoot.toObjectPath(const internalPath: UnicodeString): UnicodeString;
begin
result := internalPath.copy();
end;
class function FileSystemRoot.getUserCacheDir(): UnicodeString;
label
break_label0;
var
pos: int;
checkPath: UnicodeString;
subDirPath: UnicodeString;
objectPath: UnicodeString;
internalPath: UnicodeString;
begin
begin
with CurrentProcess.instance.environment do begin
internalPath := variable['XDG_CACHE_HOME'];
if internalPath.length <= 0 then begin
internalPath := variable['HOME'];
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
internalPath := internalPath + '.cache/';
goto break_label0;
end;
end;
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
end;
break_label0:
objectPath := toObjectPath(internalPath);
with get(objectPath), fileSystem do begin
subDirPath := objectPath.substring(path.length + 1);
pos := 1;
repeat
pos := subDirPath.indexOf('/', pos + 1);
if pos < 1 then break;
checkPath := subDirPath.substring(1, pos);
if not isObjectExists(checkPath) then createDirectory(checkPath);
until false;
end;
result := objectPath;
end;
class function FileSystemRoot.getUserLocalDir(): UnicodeString;
label
break_label0;
var
pos: int;
checkPath: UnicodeString;
subDirPath: UnicodeString;
objectPath: UnicodeString;
internalPath: UnicodeString;
begin
begin
with CurrentProcess.instance.environment do begin
internalPath := variable['XDG_DATA_HOME'];
if internalPath.length <= 0 then begin
internalPath := variable['HOME'];
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
internalPath := internalPath + '.local/share/';
goto break_label0;
end;
end;
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
end;
break_label0:
objectPath := toObjectPath(internalPath);
with get(objectPath), fileSystem do begin
subDirPath := objectPath.substring(path.length + 1);
pos := 1;
repeat
pos := subDirPath.indexOf('/', pos + 1);
if pos < 1 then break;
checkPath := subDirPath.substring(1, pos);
if not isObjectExists(checkPath) then createDirectory(checkPath);
until false;
end;
result := objectPath;
end;
class function FileSystemRoot.getUserConfigDir(): UnicodeString;
label
break_label0;
var
pos: int;
checkPath: UnicodeString;
subDirPath: UnicodeString;
objectPath: UnicodeString;
internalPath: UnicodeString;
begin
begin
with CurrentProcess.instance.environment do begin
internalPath := variable['XDG_CONFIG_HOME'];
if internalPath.length <= 0 then begin
internalPath := variable['HOME'];
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
internalPath := internalPath + '.config/';
goto break_label0;
end;
end;
if not internalPath.endsWith('/') then internalPath := internalPath + '/';
end;
break_label0:
objectPath := toObjectPath(internalPath);
with get(objectPath), fileSystem do begin
subDirPath := objectPath.substring(path.length + 1);
pos := 1;
repeat
pos := subDirPath.indexOf('/', pos + 1);
if pos < 1 then break;
checkPath := subDirPath.substring(1, pos);
if not isObjectExists(checkPath) then createDirectory(checkPath);
until false;
end;
result := objectPath;
end;
class function FileSystemRoot.enumerate(): FileSystemRoot_Collection1d;
var
position: int;
rootsLength: int;
mountPoint: UnicodeString;
rootsCreated: HashtableOfUnicodeStringToFileSystemRoot;
rootsArray: FileSystemRoot_Array1d;
rootsCopy: FileSystemRoot_Array1d;
volumeDescriptor: FileSystemRoot;
begin
rootsLength := 0;
rootsArray := FileSystemRoot_Array1d(&Array.newTObject1d($1f));
rootsCreated := HashtableOfUnicodeStringToFileSystemRoot.create();
try
fileSystemRootMonitor.beginSynchronized();
try
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
mountPoint := readln();
if mountPoint.length <= 0 then break;
position := mountPoint.indexOf(#$0020) + 1;
mountPoint := deescapeMountPoint(mountPoint.substring(position, mountPoint.indexOf(#$0020, position)));
if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
volumeDescriptor := fileSystemRootTable[mountPoint];
if volumeDescriptor = nil then begin
volumeDescriptor := FileSystemRootDescriptor.create(mountPoint, '/');
fileSystemRootRegister(mountPoint, volumeDescriptor);
rootsCreated[mountPoint] := volumeDescriptor;
end;
if rootsLength = system.length(rootsArray) then begin
rootsCopy := FileSystemRoot_Array1d(&Array.newTObject1d((rootsLength shl 1) or 1));
&Array.copyObjects(rootsArray, 0, rootsCopy, 0, rootsLength);
rootsArray := rootsCopy;
end;
rootsArray[rootsLength] := volumeDescriptor;
inc(rootsLength);
until false;
finally
close();
end;
finally
fileSystemRootMonitor.endSynchronized();
end;
finally
rootsCreated.free();
end;
result := FileSystemRootCollection.create(rootsArray, rootsLength);
end;
class function FileSystemRoot.get(const objectPath: UnicodeString): FileSystemRoot;
var
rootPath: UnicodeString;
volumeDescriptor: FileSystemRoot;
begin
rootPath := getRootPathOf(objectPath);
fileSystemRootMonitor.beginSynchronized();
try
volumeDescriptor := fileSystemRootTable[rootPath];
if volumeDescriptor = nil then begin
volumeDescriptor := FileSystemRootDescriptor.create(rootPath, '/');
fileSystemRootRegister(rootPath, volumeDescriptor);
end;
finally
fileSystemRootMonitor.endSynchronized();
end;
result := volumeDescriptor;
end;
constructor FileSystemRoot.create(const path: UnicodeString);
begin
inherited create();
fldPath := path;
end;
{%endregion}
{%region Environment }
procedure Environment.setVariable(const name, value: UnicodeString);
var
emon: Mutex;
begin
if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
end;
if value.indexOf(#0) > 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
end;
emon := fldMonitor;
emon.beginSynchronized();
try
fldVariables[name] := value;
finally
emon.endSynchronized();
end;
end;
function Environment.getLength(): int;
begin
result := fldVariables.length;
end;
function Environment.getVariable(const name: UnicodeString): UnicodeString;
var
emon: Mutex;
begin
if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
end;
emon := fldMonitor;
emon.beginSynchronized();
try
result := fldVariables[name];
finally
emon.endSynchronized();
end;
end;
constructor Environment.create();
begin
inherited create();
fldMonitor := Mutex.create();
fldVariables := HashtableOfUnicodeStringToUnicodeString.create();
end;
destructor Environment.destroy;
begin
fldMonitor.free();
fldVariables.free();
inherited destroy;
end;
procedure Environment.clear();
var
emon: Mutex;
begin
emon := fldMonitor;
emon.beginSynchronized();
try
fldVariables.clear();
finally
emon.endSynchronized();
end;
end;
procedure Environment.assign(anot: Environment);
var
index: int;
vname: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
if anot = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
end;
if anot = self then exit;
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
vars.clear();
for index := 0 to anot.length - 1 do begin
vname := anot.variableAt(index);
vars[vname] := anot[vname];
end;
finally
emon.endSynchronized();
end;
end;
function Environment.equals(anot: TObject): boolean;
var
count: int;
aindex: int;
aname: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
aenv: Environment;
emon: Mutex;
begin
if not(anot is Environment) then begin
result := false;
exit;
end;
if anot = self then begin
result := true;
exit;
end;
aenv := Environment(anot);
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
count := aenv.length;
if vars.length <> count then begin
result := false;
exit;
end;
for aindex := count - 1 downto 0 do begin
aname := aenv.variableAt(aindex);
if aenv[aname] <> vars[aname] then begin
result := false;
exit;
end;
end;
finally
emon.endSynchronized();
end;
result := true;
end;
function Environment.toString(): AnsiString;
var
index: int;
text: UnicodeString;
name: UnicodeString;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
text := '';
vars := fldVariables;
emon := fldMonitor;
emon.beginSynchronized();
try
for index := 0 to vars.length - 1 do begin
name := vars.keyAt(index);
text := text + name + '=' + vars[name] + CoUnicodeString.LINE_ENDING;
end;
finally
emon.endSynchronized();
end;
result := text.toUTF8();
end;
function Environment.isEmulation(): boolean;
begin
result := false;
end;
function Environment.isCaseSensitive(): boolean;
begin
result := true;
end;
function Environment.contains(const name: UnicodeString): boolean;
var
emon: Mutex;
begin
emon := fldMonitor;
emon.beginSynchronized();
try
result := fldVariables.contains(name);
finally
emon.endSynchronized();
end;
end;
function Environment.variableAt(index: int): UnicodeString;
var
emon: Mutex;
begin
emon := fldMonitor;
emon.beginSynchronized();
try
result := fldVariables.keyAt(index);
finally
emon.endSynchronized();
end;
end;
{%endregion}
{%region Process }
class function Process.getExitCodeProcess(processId: int; status: Pint): boolean;
var
resultId: int;
begin
resultId := safeWaitPid(processId, status, baseunix.WNOHANG);
if resultId = 0 then begin
status^ := STILL_ACTIVE;
result := true;
exit;
end;
if resultId < 0 then begin
status^ := 0;
result := false;
exit;
end;
result := true;
end;
class function Process.safeDup2(oldFD, newFD: int): int;
var
callResult: int;
begin
repeat
callResult := baseunix.fpDup2(oldFD, newFD);
until (callResult <> -1) or (baseunix.fpGetErrNo() <> baseunix.ESysEINTR);
result := callResult;
end;
class function Process.safeWaitPid(processId: int; status: Pint; options: int): int;
var
callResult: int;
begin
repeat
callResult := baseunix.fpWaitPid(processId, status, options);
until (callResult <> -1) or (baseunix.fpGetErrNo() <> baseunix.ESysEINTR);
result := callResult;
end;
class function Process.hasInstance(): boolean;
function isDigitalName(const name: AnsiString): boolean; inline;
var
idx: int;
begin
for idx := 0 to name.length - 1 do if not CoChar.isDigit(name[idx + 1]) then begin
result := false;
exit;
end;
result := true;
end;
var
objectType: int;
objectName: AnsiString;
currentProcessId: AnsiString;
currentExecutablePath: AnsiString;
objectInfo: baseunix.PDirEnt;
directoryInfo: baseunix.PDir;
begin
currentProcessId := CoInt.toString(baseunix.fpGetPid());
currentExecutablePath := baseunix.fpReadLink('/proc/self/exe');
directoryInfo := baseunix.fpOpenDir('/proc/');
if directoryInfo = nil then begin
result := false;
exit;
end;
try
repeat
objectInfo := baseunix.fpReadDir(directoryInfo^);
if objectInfo = nil then break;
objectName := AnsiString(system.PAnsiChar(@(objectInfo^.d_name)));
objectType := objectInfo^.d_type;
if
(objectName <> '.') and (objectName <> '..') and (objectType = DT_DIR) and
isDigitalName(objectName) and (currentProcessId <> objectName) and (currentExecutablePath = baseunix.fpReadLink('/proc/' + objectName + '/exe'))
then begin
result := true;
exit;
end;
until false;
finally
baseunix.fpCloseDir(directoryInfo^);
end;
result := false;
end;
class function Process.createPipe(): ByteStream;
var
handles: baseunix.TFilDes;
begin
&Array.zeroRaw(handles, sizeof(baseunix.TFilDes));
if baseunix.fpPipe(handles) <> 0 then begin
raise errorOutOfResources;
end;
result := PipeStream.create(handles[0], handles[1]);
end;
class function Process.current(): Process;
begin
result := CurrentProcess.instance;
end;
procedure Process.setPriority(newPriority: int);
begin
if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
fldPriority := newPriority;
end;
procedure Process.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
begin
fldWorkingDirectory := newWorkingDirectory.copy();
end;
procedure Process.setCommandLine(const newCommandLine: UnicodeString_Array1d);
var
index: int;
count: int;
element: UnicodeString;
locCommandLine: UnicodeString_Array1d;
begin
count := system.length(newCommandLine);
locCommandLine := nil;
for index := 0 to count - 1 do begin
element := newCommandLine[index].copy();
if (element.indexOf('"') > 0) or (element.indexOf(#0) > 0) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
if index = 0 then begin
if not element.startsWith('/') then begin
raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
end;
locCommandLine := &Array.newUnicodeString1d(count);
end;
locCommandLine[index] := element;
end;
fldCommandLine := locCommandLine;
end;
procedure Process.setEnvironment(newEnvironment: Environment);
begin
fldEnvironment.assign(newEnvironment);
end;
procedure Process.setStandardInput(newStandardInput: ByteReader);
begin
if (newStandardInput <> nil) and not Lang.isInstance(newStandardInput, HandleInputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardInput := newStandardInput;
end;
procedure Process.setStandardOutput(newStandardOutput: ByteWriter);
begin
if (newStandardOutput <> nil) and not Lang.isInstance(newStandardOutput, HandleOutputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardOutput := newStandardOutput;
end;
procedure Process.setStandardError(newStandardError: ByteWriter);
begin
if (newStandardError <> nil) and not Lang.isInstance(newStandardError, HandleOutputStream) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
]));
end;
fldStandardError := newStandardError;
end;
procedure Process.clearStarting();
begin
fldStarting := false;
end;
function Process.getPriority(): int;
begin
result := fldPriority;
end;
function Process.getWorkingDirectory(): UnicodeString;
begin
result := fldWorkingDirectory.copy();
end;
function Process.createEnvironment(): Environment;
begin
result := nil;
end;
function Process.isStarting(): boolean; assembler; nostackframe;
asm
mov eax, true
xchg byte [rdi+offset fldStarting], al
movzx eax, al
end;
function Process.getCommandLine(): UnicodeString_Array1d;
var
index: int;
count: int;
commandLineData: UnicodeString_Array1d;
commandLineCopy: UnicodeString_Array1d;
begin
commandLineData := fldCommandLine;
count := system.length(commandLineData);
commandLineCopy := &Array.newUnicodeString1d(count);
for index := count - 1 downto 0 do begin
commandLineCopy[index] := commandLineData[index].copy();
end;
result := commandLineCopy;
end;
constructor Process.create();
var
locCurrentProcess: Process;
locEnvironment: Environment;
begin
inherited create();
locEnvironment := createEnvironment();
if locEnvironment = nil then begin
locEnvironment := platform.independent.osservices.Environment.create();
end;
locCurrentProcess := CurrentProcess.instance;
if locCurrentProcess <> nil then begin
locEnvironment.assign(locCurrentProcess.environment);
fldWorkingDirectory := locCurrentProcess.workingDirectory;
fldStandardInput := locCurrentProcess.fldStandardInput;
fldStandardOutput := locCurrentProcess.fldStandardOutput;
fldStandardError := locCurrentProcess.fldStandardError;
end;
fldPriority := NORM_PRIORITY;
fldEnvironment := locEnvironment;
end;
destructor Process.destroy;
begin
fldEnvironment.free();
inherited destroy;
end;
procedure Process.start();
var
stdInHandle: int;
stdOutHandle: int;
stdErrHandle: int;
index: int;
count: int;
processId: int;
processExitCode: int;
locWorkingDirectory: AnsiString;
element: UnicodeString;
commandLineArray: AnsiString_Array1d;
environmentArray: AnsiString_Array1d;
commandLineData: UnicodeString_Array1d;
environmentData: Environment;
stdInStream: HandleInputStream;
stdOutStream: HandleOutputStream;
stdErrStream: HandleOutputStream;
attributes: baseunix.Stat;
begin
if isStarting() then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
try
processExitCode := 0;
processId := fldProcessId;
if (processId > 0) and getExitCodeProcess(processId, @processExitCode) and (processExitCode = STILL_ACTIVE) then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
commandLineData := fldCommandLine;
count := system.length(commandLineData);
if count <= 0 then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process.command-line'));
end;
{ приоритет }
{ === приоритеты процессов не поддерживаются здесь === }
{ рабочая папка }
locWorkingDirectory := FileSystemRoot.toInternalPath(fldWorkingDirectory).toUTF8();
{ командная строка }
commandLineArray := &Array.newAnsiString1d(count + 1);
for index := 0 to count - 1 do begin
commandLineArray[index] := commandLineData[index].toUTF8();
end;
{ переменные среды }
environmentData := fldEnvironment;
count := environmentData.length;
environmentArray := &Array.newAnsiString1d(count + 1);
for index := 0 to count - 1 do begin
element := environmentData.variableAt(index);
environmentArray[index] := (element + '=' + environmentData[element]).toUTF8();
end;
{ стандартные потоки ввода-вывода }
stdInStream := HandleInputStream(Lang.cast(fldStandardInput, HandleInputStream));
stdInHandle := -1;
if stdInStream <> nil then stdInHandle := int(stdInStream.fldHandle);
stdOutStream := HandleOutputStream(Lang.cast(fldStandardOutput, HandleOutputStream));
stdOutHandle := -1;
if stdOutStream <> nil then stdOutHandle := int(stdOutStream.fldHandle);
stdErrStream := HandleOutputStream(Lang.cast(fldStandardError, HandleOutputStream));
stdErrHandle := -1;
if stdErrStream <> nil then stdErrHandle := int(stdErrStream.fldHandle);
{ создание процесса }
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if (baseunix.fpStat(commandLineArray[0], attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFREG) = 0) then begin
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(commandLineArray[0])
]));
end;
if (attributes.st_mode and (baseunix.S_IXUSR + baseunix.S_IXGRP + baseunix.S_IXOTH)) <> (baseunix.S_IXUSR + baseunix.S_IXGRP + baseunix.S_IXOTH) then begin
raise FileOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.operation'), [
CoAnsiString.create(commandLineArray[0])
]));
end;
if (baseunix.fpStat(locWorkingDirectory, attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFDIR) = 0) then begin
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(locWorkingDirectory)
]));
end;
processId := baseunix.fpFork();
if processId < 0 then begin
raise errorOutOfResources;
end;
if processId > 0 then begin
{ этот процесс }
fldProcessId := processId;
end else begin
{ новый процесс }
baseunix.fpChDir(locWorkingDirectory);
if stdInHandle >= 0 then safeDup2(stdInHandle, 0);
if stdOutHandle >= 0 then safeDup2(stdOutHandle, 1);
if stdErrHandle >= 0 then safeDup2(stdErrHandle, 2);
baseunix.fpExecve(commandLineArray[0], system.PPChar(commandLineArray), system.PPChar(environmentArray));
baseunix.fpExit(127);
end;
finally
clearStarting();
end;
end;
procedure Process.join();
var
processId: int;
begin
processId := fldProcessId;
if processId <= 0 then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
safeWaitPid(processId, nil, 0);
end;
procedure Process.join(timeInMillis: long);
var
processId: int;
begin
processId := fldProcessId;
if processId <= 0 then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
if timeInMillis < 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
end;
if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
if timeInMillis = 0 then timeInMillis := -1;
repeat
Thread.sleep(1);
if safeWaitPid(processId, nil, baseunix.WNOHANG) > 0 then break;
if timeInMillis > 0 then dec(timeInMillis);
until timeInMillis = 0;
end;
procedure Process.join(timeInMillis: long; timeInNanos: int);
var
processId: int;
begin
processId := fldProcessId;
if processId <= 0 then begin
raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
end;
if timeInMillis < 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
end;
if (timeInNanos < 0) or (timeInNanos > 999999) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInNanos') ]));
end;
if (timeInMillis < CoLong.MAX_VALUE) and (timeInNanos >= 500000) or (timeInMillis = 0) and (timeInNanos > 0) then inc(timeInMillis);
if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
if timeInMillis = 0 then timeInMillis := -1;
repeat
Thread.sleep(1);
if safeWaitPid(processId, nil, baseunix.WNOHANG) > 0 then break;
if timeInMillis > 0 then dec(timeInMillis);
until timeInMillis = 0;
end;
function Process.isTerminated(): boolean;
var
processId: int;
begin
processId := fldProcessId;
result := (processId > 0) and (safeWaitPid(processId, nil, baseunix.WNOHANG) > 0);
end;
function Process.getExitCode(): int;
var
processId: int;
processExitCode: int;
begin
processExitCode := 0;
processId := fldProcessId;
if (processId <= 0) or not getExitCodeProcess(processId, @processExitCode) then begin
result := 0;
exit;
end;
result := processExitCode;
end;
{%endregion}
{%region TimeBase }
class function TimeBase.currentOffsetInMillis(): int;
var
timeStruct: TM;
timeInSeconds: baseunix.Time_t;
begin
&Array.zeroRaw(timeStruct, sizeof(TM));
timeInSeconds := baseunix.fpTime();
localTime(@timeInSeconds, @timeStruct);
result := 1000 * int(timeStruct.tm_gmtoff);
end;
class function TimeBase.currentTimeInMillis(): long;
var
timeStruct: TM;
timeInMicros: baseunix.TimeVal;
begin
&Array.zeroRaw(timeStruct, sizeof(TM));
&Array.zeroRaw(timeInMicros, sizeof(baseunix.TimeVal));
doSyscall(SYSCALL_NR_GETTIMEOFDAY, long(@timeInMicros), long(nil));
gmtTime(@(timeInMicros.tv_sec), @timeStruct);
with timeStruct do result := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(timeInMicros.tv_usec) div 1000);
end;
{%endregion}
{%region MemoryManager }
class procedure MemoryManager.deallocate(region: MemoryRegion; flags: int);
var
address: long;
size: long;
reference: Pointer absolute address;
begin
if region = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('region') ]));
end;
if not(region is MemoryRegionDescriptor) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('region') ]));
end;
address := region.address;
size := region.size;
if (flags and RESERVED) <> 0 then begin
baseunix.fpMProtect(reference, unixtype.size_t(size), baseunix.PROT_NONE);
exit;
end;
baseunix.fpMUnmap(reference, baseunix.size_t(size));
end;
class function MemoryManager.enumerate(): MemoryRegion_Collection1d;
var
regionsLength: int;
beginPosition: int;
endPosition: int;
beginAddress: long;
endAddress: long;
line: UnicodeString;
regionsData: MemoryRegion_Array1d;
regionsCopy: MemoryRegion_Array1d;
begin
regionsLength := 0;
regionsData := MemoryRegion_Array1d(&Array.newIUnknown1d($7f));
try
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/self/maps'), baseunix.O_RDONLY))), bigEndianDataInput do try
repeat
line := readln();
if line.length <= 0 then break;
beginPosition := line.indexOf('-');
endPosition := line.indexOf(#$0020, beginPosition + 1);
beginAddress := CoLong.parseUnsigned(line.substring(1, beginPosition).toUTF8(), $10);
endAddress := CoLong.parseUnsigned(line.substring(beginPosition + 1, endPosition).toUTF8(), $10);
if regionsLength = system.length(regionsData) then begin
regionsCopy := MemoryRegion_Array1d(&Array.newIUnknown1d((regionsLength shl 1) or 1));
&Array.copyUnknowns(regionsData, 0, regionsCopy, 0, regionsLength);
regionsData := regionsCopy;
end;
regionsData[regionsLength] := MemoryRegionDescriptor.create(beginAddress, endAddress - beginAddress);
inc(regionsLength);
until false;
finally
close();
end;
except
end;
result := MemoryRegionCollection.create(regionsData, regionsLength);
end;
class function MemoryManager.allocate(address, size: long; flags: int): MemoryRegion;
var
allocationProtect: int;
reference: Pointer absolute address;
begin
if (size < 0) or (size > MEMORY_LIMIT_ADDRESS - MEMORY_START_ADDRESS) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('size') ]));
end;
if (address + size > MEMORY_LIMIT_ADDRESS) or (address < MEMORY_START_ADDRESS) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('address') ]));
end;
address := address and -PAGE_SIZE;
if size = 0 then size := 1;
size := size + (-size and (PAGE_SIZE - 1));
allocationProtect := baseunix.PROT_NONE;
if (flags and READABLE) <> 0 then begin
inc(allocationProtect, baseunix.PROT_READ);
end;
if (flags and WRITEABLE) <> 0 then begin
inc(allocationProtect, baseunix.PROT_WRITE);
end;
if (flags and EXECUTABLE) <> 0 then begin
inc(allocationProtect, baseunix.PROT_EXEC);
end;
if ((flags and RESERVED) <> 0) or (baseunix.fpMProtect(reference, unixtype.size_t(size), allocationProtect) <> 0) then begin
reference := baseunix.fpMMap(reference, unixtype.size_t(size), allocationProtect, baseunix.MAP_PRIVATE + baseunix.MAP_ANONYMOUS, 0, 0);
if address = -1 then case baseunix.fpGetErrNo() of
baseunix.ESYSENOMEM:
raise errorOutOfResources;
else
raise IllegalArgumentException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.passed'));
end;
end;
result := MemoryRegionDescriptor.create(address, size);
end;
{%endregion}
{%region SystemInfo }
class procedure SystemInfo.initialize();
var
readed: boolean;
cpuBrandLength: int;
line: UnicodeString;
cpuBrandArray: system.PAnsiChar;
begin
try
{ количество потоков процессора }
cpuNumberOfCores := 0;
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/cpuinfo'), baseunix.O_RDONLY))), bigEndianDataInput do try
repeat
line := readln();
if line.length <= 0 then break;
readed := false;
repeat
if not readed and (line.substring(1, line.indexOf(':')).trim() = 'processor') then begin
inc(cpuNumberOfCores);
readed := true;
end;
line := readln();
if line.length <= 0 then break;
until false;
until false;
finally
close();
end;
{ название модели процессора }
cpuBrandArray := system.PAnsiChar(system.getMemory($30 * sizeof(char)));
try
cpuBrandLength := cpuReadBrandStringTo(cpuBrandArray);
cpuBrandString := AnsiString.create(cpuBrandLength);
&Array.copyRaw(cpuBrandArray^, cpuBrandString[1], cpuBrandLength * sizeof(char));
finally
system.freeMemory(cpuBrandArray);
end;
{ версия операционной системы }
osVersion := '';
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/version'), baseunix.O_RDONLY))), bigEndianDataInput do try
repeat
line := read();
if line = 'version' then begin
osVersion := read().toUTF8();
break;
end;
until false;
finally
close();
end;
except
end;
end;
class function SystemInfo.getProcessorCodeBits(): int;
begin
result := 64;
end;
class function SystemInfo.getProcessorNumberOfCores(): int;
begin
result := cpuNumberOfCores;
end;
class function SystemInfo.getProcessorBrandString(): AnsiString;
begin
result := cpuBrandString;
end;
class function SystemInfo.getOperatingSystemName(): AnsiString;
begin
result := 'GNU Linux';
end;
class function SystemInfo.getOperatingSystemVersion(): AnsiString;
begin
result := osVersion;
end;
{%endregion}
{%region MemoryRegionDescriptor }
function MemoryRegionDescriptor.getProtectionString(): AnsiString;
var
beginPosition: int;
endPosition: int;
address: long;
line: UnicodeString;
begin
address := fldAddress;
try
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/self/maps'), baseunix.O_RDONLY))), bigEndianDataInput do try
repeat
line := readln();
if line.length <= 0 then break;
endPosition := line.indexOf('-');
if CoLong.parseUnsigned(line.substring(1, endPosition).toUTF8(), $10) = address then begin
beginPosition := line.indexOf(#$0020, endPosition + 1);
result := line.substring(beginPosition + 1, beginPosition + 5).toUTF8();
exit;
end;
until false;
finally
close();
end;
except
end;
result := '----';
end;
constructor MemoryRegionDescriptor.create(address, size: long);
begin
inherited create();
fldAddress := address;
fldSize := size;
end;
procedure MemoryRegionDescriptor.setProtectionBits(newProtectionBits: int);
var
protect: int;
address: long;
reference: Pointer absolute address;
begin
address := fldAddress;
protect := baseunix.PROT_NONE;
if (newProtectionBits and MemoryManager.READABLE) <> 0 then begin
inc(protect, baseunix.PROT_READ);
end;
if (newProtectionBits and MemoryManager.WRITEABLE) <> 0 then begin
inc(protect, baseunix.PROT_WRITE);
end;
if (newProtectionBits and MemoryManager.EXECUTABLE) <> 0 then begin
inc(protect, baseunix.PROT_EXEC);
end;
baseunix.fpMProtect(reference, unixtype.size_t(fldSize), protect);
end;
function MemoryRegionDescriptor.equals(anot: TObject): boolean;
var
amrd: MemoryRegionDescriptor;
begin
if not(anot is MemoryRegionDescriptor) then begin
result := false;
exit;
end;
if anot = self then begin
result := true;
exit;
end;
amrd := MemoryRegionDescriptor(anot);
result := (fldAddress = amrd.fldAddress) and (fldSize = amrd.fldSize);
end;
function MemoryRegionDescriptor.getHashCode(): long;
begin
result := fldAddress xor CoLong.rol(fldSize, 35);
end;
function MemoryRegionDescriptor.toString(): AnsiString;
var
address: long;
begin
address := fldAddress;
result := CoLong.toHexString(address) + '-' + CoLong.toHexString(address + fldSize) + #$20 + getProtectionString();
end;
function MemoryRegionDescriptor.getProtectionBit(index: int): boolean;
begin
if (index < 0) or (index > 3) then begin
result := false;
exit;
end;
result := getProtectionString()[index + 1] <> '-';
end;
function MemoryRegionDescriptor.getProtectionBits(): int;
var
index: int;
protect: int;
str: AnsiString;
begin
protect := 0;
str := getProtectionString();
for index := 0 to 3 do if str[index + 1] <> '-' then begin
inc(protect, 1 shl index);
end;
result := protect;
end;
function MemoryRegionDescriptor.getAddress(): long;
begin
result := fldAddress;
end;
function MemoryRegionDescriptor.getSize(): long;
begin
result := fldSize;
end;
function MemoryRegionDescriptor.pointerTo(offset: long): Pointer;
var
address: long;
reference: Pointer absolute address;
begin
if (offset < 0) or (offset > fldSize) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('offset') ]));
end;
address := fldAddress + offset;
result := reference;
end;
{%endregion}
{%region FileSystemRootDescriptor }
function FileSystemRootDescriptor.getName(): UnicodeString;
begin
result := '';
end;
function FileSystemRootDescriptor.getFileSystem(): FileSystem;
begin
result := fldFileSystem;
end;
constructor FileSystemRootDescriptor.create(const argPath, argCurrentDirectory: UnicodeString);
begin
inherited create(argPath);
fldFileSystem := VolumeFileSystem.create(toInternalPath(argPath), argCurrentDirectory.copy());
end;
destructor FileSystemRootDescriptor.destroy;
begin
fldFileSystem.free();
inherited destroy;
end;
{%endregion}
{%region VolumeFileSystem }
function VolumeFileSystem.toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
var
internalName: UnicodeString;
begin
internalName := toInternalName(objectName);
if isInternalNameFull(internalName) then begin
result := internalName;
exit;
end;
result := fldCurrentDirectory + internalName;
end;
function VolumeFileSystem.makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
label
break_label0;
var
error: int;
dotPosition: int;
reductionPosition: int;
internalRootPath: UnicodeString;
internalFullPath: UnicodeString;
internalResultPath: UnicodeString;
attributes: baseunix.Stat;
begin
internalRootPath := fldInternalRootPath;
internalFullPath := volumeFullPath;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('/./', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('/.') then break;
dotPosition := internalFullPath.length - 1;
end;
internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
until false;
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('/../', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('/..') then break;
dotPosition := internalFullPath.length - 2;
end;
begin
if dotPosition > 1 then begin
error := baseunix.fpStat(system.PAnsiChar((internalRootPath + internalFullPath.substring(1, dotPosition)).toUTF8()), attributes);
if (error = 0) and ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then goto break_label0;
case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR: ;
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
result := '';
exit;
end;
break_label0:
reductionPosition := internalFullPath.lastIndexOf('/', dotPosition - 1);
internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
dotPosition := reductionPosition;
until false;
internalResultPath := internalRootPath + internalFullPath;
if FileSystemRoot.getRootPathOf(internalResultPath) <> internalRootPath then begin
result := '';
exit;
end;
if internalResultPath = '' then begin
result := '/.';
exit;
end;
result := internalResultPath;
end;
function VolumeFileSystem.makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
var
position: int;
utfFullPath: AnsiString;
checkFullPath: UnicodeString;
attributes: baseunix.Stat;
begin
position := volumeFullPath.lastIndexOf('/') + 1;
checkFullPath := makeInternalFullPathAndCheckExist(volumeFullPath.substring(1, position), AT_DIRECTORY) + '.';
utfFullPath := checkFullPath.toUTF8();
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := checkFullPath.substring(1, checkFullPath.length) + volumeFullPath.substring(position);
end;
function VolumeFileSystem.makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
label
break_label0;
var
error: int;
dotPosition: int;
reductionPosition: int;
internalRootPath: UnicodeString;
internalFullPath: UnicodeString;
internalResultPath: UnicodeString;
attributes: baseunix.Stat;
begin
internalRootPath := fldInternalRootPath;
internalFullPath := volumeFullPath;
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('/./', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('/.') then break;
dotPosition := internalFullPath.length - 1;
end;
internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
until false;
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
dotPosition := 1;
repeat
dotPosition := internalFullPath.indexOf('/../', dotPosition);
if dotPosition <= 0 then begin
if not internalFullPath.endsWith('/..') then break;
dotPosition := internalFullPath.length - 2;
end;
begin
if dotPosition > 1 then begin
error := baseunix.fpStat(system.PAnsiChar((internalRootPath + internalFullPath.substring(1, dotPosition)).toUTF8()), attributes);
if (error = 0) and ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then goto break_label0;
case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR: ;
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
case argumentType of
AT_FILE:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
AT_DIRECTORY:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
else
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
]));
end;
end;
break_label0:
reductionPosition := internalFullPath.lastIndexOf('/', dotPosition - 1);
internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
dotPosition := reductionPosition;
until false;
internalResultPath := internalRootPath + internalFullPath;
if FileSystemRoot.getRootPathOf(internalResultPath) <> internalRootPath then case argumentType of
AT_FILE:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
]));
AT_DIRECTORY:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
]));
end;
if internalResultPath = '' then begin
result := '/.';
exit;
end;
result := internalResultPath;
end;
constructor VolumeFileSystem.create(const rootPath, currentDirectory: UnicodeString);
begin
inherited create();
fldObjectNameMaximumLength := OBJECT_NAME_MAXIMUM_LENGTH - rootPath.length;
if rootPath = '' then begin
fldCanonicalRootPath := '/';
end else begin
fldCanonicalRootPath := rootPath.toUTF8();
end;
fldInternalRootPath := rootPath;
fldCurrentDirectory := currentDirectory;
end;
procedure VolumeFileSystem.changeCurrentDirectory(const directoryPath: UnicodeString);
var
maximumNameLength: int;
utfFullPath: AnsiString;
internalFullPath: UnicodeString;
attributes: baseunix.Stat;
begin
maximumNameLength := fldObjectNameMaximumLength;
if directoryPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryPath')
]));
end;
if not isObjectNameValid(directoryPath) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryPath')
]));
end;
internalFullPath := toVolumeFullPath(directoryPath);
if internalFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryPath')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
if internalFullPath.endsWith('/') then internalFullPath := internalFullPath.substring(1, internalFullPath.length);
utfFullPath := internalFullPath.toUTF8();
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if (attributes.st_mode and baseunix.S_IFDIR) = 0 then begin
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(utfFullPath)
]));
end;
fldCurrentDirectory := internalFullPath.substring(fldInternalRootPath.length + 1) + '/';
end;
procedure VolumeFileSystem.readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
var
maximumNameLength: int;
standardNameLength: int;
internalAttributes: int;
timeInMillis: long;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
internalLastAccessTime: TM;
internalLastWriteTime: TM;
attributes: baseunix.Stat;
begin
standardNameLength := objectName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
volumeFullPath := toVolumeFullPath(objectName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
&Array.zeroRaw(internalLastAccessTime, sizeof(TM));
&Array.zeroRaw(internalLastWriteTime, sizeof(TM));
if (gmtTime(@(attributes.st_atime), @internalLastAccessTime) = nil) or (gmtTime(@(attributes.st_mtime), @internalLastWriteTime) = nil) then begin
raise ObjectReadAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.read-attributes'), [
CoAnsiString.create(utfFullPath)
]));
end;
if objectAttr = nil then exit;
internalAttributes := int(attributes.st_mode);
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (internalAttributes and baseunix.S_IFDIR) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (internalAttributes and (baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH)) = 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_READABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, (internalAttributes and baseunix.S_IRUSR) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, (internalAttributes and baseunix.S_IWUSR) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, (internalAttributes and baseunix.S_IXUSR) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_READABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, (internalAttributes and baseunix.S_IRGRP) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, (internalAttributes and baseunix.S_IWGRP) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, (internalAttributes and baseunix.S_IXGRP) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_READABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, (internalAttributes and baseunix.S_IROTH) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, (internalAttributes and baseunix.S_IWOTH) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then begin
objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, (internalAttributes and baseunix.S_IXOTH) <> 0);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
with internalLastAccessTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_atime_nsec) div 1000000);
objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, timeInMillis);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
with internalLastWriteTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_mtime_nsec) div 1000000);
objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, timeInMillis);
end;
end;
procedure VolumeFileSystem.writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
var
objectWriteTime: boolean;
objectWriteAttr: boolean;
toWriteAttributes: int;
maximumNameLength: int;
standardNameLength: int;
internalAttributes: int;
timePacked: long;
timeOffset: long;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
internalTime: TM;
attributes: baseunix.Stat;
toWriteTimes: baseunix.UTimBuf;
timeFields: TimeRecord absolute timePacked;
begin
standardNameLength := objectName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
volumeFullPath := toVolumeFullPath(objectName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if objectAttr = nil then begin
raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('objectAttr') ]));
end;
toWriteAttributes := baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH;
&Array.zeroRaw(toWriteTimes, sizeof(baseunix.UTimBuf));
&Array.zeroRaw(internalTime, sizeof(TM));
timeOffset := TimeBase.currentOffsetInMillis();
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_READABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IRUSR;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE) then inc(toWriteAttributes, baseunix.S_IRUSR);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IWUSR;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWUSR);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IXUSR;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXUSR);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_READABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IRGRP;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE) then inc(toWriteAttributes, baseunix.S_IRGRP);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IWGRP;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWGRP);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IXGRP;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXGRP);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_READABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IROTH;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE) then inc(toWriteAttributes, baseunix.S_IROTH);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IWOTH;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWOTH);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then begin
toWriteAttributes := toWriteAttributes and not baseunix.S_IXOTH;
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXOTH);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY) then begin
toWriteAttributes := toWriteAttributes and not(baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH);
end else begin
toWriteAttributes := toWriteAttributes or baseunix.S_IWUSR;
end;
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
timePacked := timeToPackedFields(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) + timeOffset);
internalTime.tm_sec := timeFields.millisecond div 1000;
internalTime.tm_min := timeFields.minute;
internalTime.tm_hour := timeFields.hour;
internalTime.tm_mday := timeFields.day;
internalTime.tm_mon := timeFields.month - 1;
internalTime.tm_year := timeFields.year - 1900;
toWriteTimes.actime := unixTime(@internalTime);
end;
if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
timePacked := timeToPackedFields(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME) + timeOffset);
internalTime.tm_sec := timeFields.millisecond div 1000;
internalTime.tm_min := timeFields.minute;
internalTime.tm_hour := timeFields.hour;
internalTime.tm_mday := timeFields.day;
internalTime.tm_mon := timeFields.month - 1;
internalTime.tm_year := timeFields.year - 1900;
toWriteTimes.modtime := unixTime(@internalTime);
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
internalAttributes := int(attributes.st_mode) and (baseunix.S_IRWXO or baseunix.S_IRWXG or baseunix.S_IRWXU or baseunix.S_ISUID or baseunix.S_ISGID or baseunix.S_ISVTX);
if ((internalAttributes and baseunix.S_IWUSR) = 0) and (baseunix.fpChMod(system.PAnsiChar(utfFullPath), internalAttributes or baseunix.S_IWUSR) <> 0) then case baseunix.fpGetErrNo() of
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
objectWriteTime := (toWriteTimes.actime <> -1) and (toWriteTimes.modtime <> -1) and (baseunix.fpUTime(system.PAnsiChar(utfFullPath), @toWriteTimes) = 0);
objectWriteAttr := baseunix.fpChMod(system.PAnsiChar(utfFullPath), toWriteAttributes or internalAttributes and (baseunix.S_ISUID or baseunix.S_ISGID or baseunix.S_ISVTX)) = 0;
if not objectWriteTime or not objectWriteAttr then begin
raise ObjectWriteAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.write-attributes'), [
CoAnsiString.create(utfFullPath)
]));
end;
end;
procedure VolumeFileSystem.move(const objectOldName, objectNewName: UnicodeString);
var
maximumNameLength: int;
standardNameLength: int;
utfOldFullPath: AnsiString;
utfNewFullPath: AnsiString;
volumeOldFullPath: UnicodeString;
volumeNewFullPath: UnicodeString;
begin
standardNameLength := objectOldName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectOldName')
]));
end;
if (standardNameLength <= 0) or objectOldName.endsWith('/') or not isObjectNameValid(objectOldName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectOldName')
]));
end;
volumeOldFullPath := toVolumeFullPath(objectOldName);
if volumeOldFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectOldName')
]));
end;
standardNameLength := objectNewName.length;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectNewName')
]));
end;
if (standardNameLength <= 0) or objectNewName.endsWith('/') or not isObjectNameValid(objectNewName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectNewName')
]));
end;
volumeNewFullPath := toVolumeFullPath(objectNewName);
if volumeNewFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectNewName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfOldFullPath := makeInternalFullPathAndCheckExist(volumeOldFullPath, AT_OBJECT).toUTF8();
utfNewFullPath := makeInternalFullPathAndCheckCreat(volumeNewFullPath).toUTF8();
if baseunix.fpRename(system.PAnsiChar(utfOldFullPath), system.PAnsiChar(utfNewFullPath)) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfOldFullPath)
]));
baseunix.ESysEEXIST:
raise MoveOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'move'), [
CoAnsiString.create(utfOldFullPath), CoAnsiString.create(utfNewFullPath)
]));
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure VolumeFileSystem.deleteFile(const fileName: UnicodeString);
var
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
if baseunix.fpUnlink(system.PAnsiChar(utfFullPath)) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysEISDIR:
raise FileDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.deletion'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure VolumeFileSystem.deleteDirectory(const directoryName: UnicodeString);
var
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
attributes: baseunix.Stat;
begin
standardNameLength := directoryName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryName')
]));
end;
volumeFullPath := toVolumeFullPath(directoryName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_DIRECTORY).toUTF8();
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if (baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) = 0) and ((attributes.st_mode and baseunix.S_IFDIR) = 0) then begin
raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
CoAnsiString.create(utfFullPath)
]));
end;
if baseunix.fpRmDir(system.PAnsiChar(utfFullPath)) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOTEMPTY:
raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure VolumeFileSystem.createDirectory(const directoryName: UnicodeString);
const
DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
var
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := directoryName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('directoryName')
]));
end;
volumeFullPath := toVolumeFullPath(directoryName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('directoryName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
if baseunix.fpMkDir(system.PAnsiChar(utfFullPath), DEFAULT_RIGHTS) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysEEXIST:
raise DirectoryCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.creation'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function VolumeFileSystem.isAttached(): boolean;
var
position: int;
rootPath: UnicodeString;
mountPoint: UnicodeString;
begin
rootPath := fldInternalRootPath;
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
mountPoint := readln();
if mountPoint.length <= 0 then break;
position := mountPoint.indexOf(#$0020) + 1;
mountPoint := FileSystemRoot.deescapeMountPoint(mountPoint.substring(position, mountPoint.indexOf(#$0020, position)));
if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
if mountPoint = rootPath then begin
result := true;
exit;
end;
until false;
finally
close();
end;
result := false;
end;
function VolumeFileSystem.isReadOnly(): boolean;
const
READ_ONLY = UnicodeString('ro');
var
position0: int;
position1: int;
rootPath: UnicodeString;
mountPoint: UnicodeString;
mountParams: UnicodeString;
begin
rootPath := fldInternalRootPath;
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
mountParams := readln();
if mountParams.length <= 0 then break;
position0 := mountParams.indexOf(#$0020) + 1;
position1 := mountParams.indexOf(#$0020, position0);
mountPoint := FileSystemRoot.deescapeMountPoint(mountParams.substring(position0, position1));
if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
if mountPoint = rootPath then begin
position0 := mountParams.indexOf(#$0020, position1 + 1) + 1;
mountParams := mountParams.substring(position0, mountParams.indexOf(#$0020, position0));
result := (mountParams = READ_ONLY) or mountParams.startsWith(READ_ONLY + ',') or (mountParams.indexOf(',' + READ_ONLY + ',') > 0) or mountParams.endsWith(',' + READ_ONLY);
exit;
end;
until false;
finally
close();
end;
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
function VolumeFileSystem.isObjectNameCaseSensitive(): boolean;
const
{ список нечувствительных к имени объекта файловых систем }
caseInsensitiveFSTypes: array [0..6] of UnicodeString = (
'exfat', 'hpfs', 'iso9660', 'msdos', 'ntfs', 'umsdos', 'vfat'
);
var
index: int;
position0: int;
position1: int;
rootPath: UnicodeString;
mountPoint: UnicodeString;
mountFSType: UnicodeString;
begin
rootPath := fldInternalRootPath;
with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
repeat
mountFSType := readln();
if mountFSType.length <= 0 then break;
position0 := mountFSType.indexOf(#$0020) + 1;
position1 := mountFSType.indexOf(#$0020, position0);
mountPoint := FileSystemRoot.deescapeMountPoint(mountFSType.substring(position0, position1));
if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
if mountPoint = rootPath then begin
inc(position1);
mountFSType := mountFSType.substring(position1, mountFSType.indexOf(#$0020, position1));
for index := 0 to 6 do if caseInsensitiveFSTypes[index].equalsIgnoreCase(mountFSType) then begin
result := false;
exit;
end;
result := true;
exit;
end;
until false;
finally
close();
end;
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
function VolumeFileSystem.isObjectExists(const objectName: UnicodeString): boolean;
var
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
attributes: baseunix.Stat;
begin
standardNameLength := objectName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectName')
]));
end;
volumeFullPath := toVolumeFullPath(objectName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndIsExist(volumeFullPath).toUTF8();
if utfFullPath = '' then begin
result := false;
exit;
end;
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR: begin
result := false;
exit;
end;
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := true;
end;
function VolumeFileSystem.isObjectNameValid(const objectName: UnicodeString): boolean;
const
COMPONENT_NAME_MAXIMUM_LENGTH = int(255);
var
index: int;
length: int;
beginIndex: int;
endIndex: int;
utfName: AnsiString;
begin
if (objectName.length > fldObjectNameMaximumLength) or (objectName.indexOf('//') > 0) then begin
result := false;
exit;
end;
utfName := objectName.toUTF8();
length := utfName.length;
for index := 0 to length - 1 do if utfName[index + 1] = #$00 then begin
result := false;
exit;
end;
if length > 0 then begin
beginIndex := 0;
if utfName[1] = '/' then inc(beginIndex);
while (beginIndex >= 0) and (beginIndex < length) do begin
endIndex := utfName.indexOf('/', beginIndex + 1) - 1;
if endIndex < 0 then endIndex := length;
if endIndex - beginIndex > COMPONENT_NAME_MAXIMUM_LENGTH then begin
result := false;
exit;
end;
beginIndex := endIndex + 1;
end;
end;
result := true;
end;
function VolumeFileSystem.isInternalNameFull(const internalName: UnicodeString): boolean;
begin
result := internalName.startsWith('/');
end;
function VolumeFileSystem.isInternalNameValid(const internalName: UnicodeString): boolean;
const
COMPONENT_NAME_MAXIMUM_LENGTH = int(255);
var
index: int;
length: int;
beginIndex: int;
endIndex: int;
utfName: AnsiString;
begin
if (internalName.length > fldObjectNameMaximumLength) or (internalName.indexOf('//') > 0) then begin
result := false;
exit;
end;
utfName := internalName.toUTF8();
length := utfName.length;
for index := 0 to length - 1 do if utfName[index + 1] = #$00 then begin
result := false;
exit;
end;
if length > 0 then begin
beginIndex := 0;
if utfName[1] = '/' then inc(beginIndex);
while (beginIndex >= 0) and (beginIndex < length) do begin
endIndex := utfName.indexOf('/', beginIndex + 1) - 1;
if endIndex < 0 then endIndex := length;
if endIndex - beginIndex > COMPONENT_NAME_MAXIMUM_LENGTH then begin
result := false;
exit;
end;
beginIndex := endIndex + 1;
end;
end;
result := true;
end;
function VolumeFileSystem.getObjectNameMaximumLength(): int;
begin
result := fldObjectNameMaximumLength;
end;
function VolumeFileSystem.totalSize(): long;
var
utfRootPath: AnsiString;
volumeInfo: baseunix.TStatFS;
begin
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfRootPath := fldInternalRootPath.toUTF8() + '/.';
&Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := volumeInfo.bsize * long(volumeInfo.blocks);
end;
function VolumeFileSystem.usedSize(): long;
var
utfRootPath: AnsiString;
volumeInfo: baseunix.TStatFS;
begin
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfRootPath := fldInternalRootPath.toUTF8() + '/.';
&Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := volumeInfo.bsize * long(volumeInfo.blocks - volumeInfo.bfree);
end;
function VolumeFileSystem.availableSize(): long;
var
utfRootPath: AnsiString;
volumeInfo: baseunix.TStatFS;
begin
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfRootPath := fldInternalRootPath.toUTF8() + '/.';
&Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := volumeInfo.bsize * long(volumeInfo.bfree);
end;
function VolumeFileSystem.getCurrentDirectory(): UnicodeString;
begin
result := fldCurrentDirectory.copy();
end;
function VolumeFileSystem.toInternalName(const objectName: UnicodeString): UnicodeString;
begin
result := objectName;
end;
function VolumeFileSystem.toObjectName(const internalName: UnicodeString): UnicodeString;
begin
result := internalName;
end;
function VolumeFileSystem.findFirst(const objectPath: UnicodeString): ObjectEnumeration;
var
position: int;
maximumNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
attributes: baseunix.Stat;
findHandle: baseunix.PDir;
findData: baseunix.PDirEnt;
begin
maximumNameLength := fldObjectNameMaximumLength;
if objectPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectPath')
]));
end;
if not isObjectNameValid(objectPath) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('objectPath')
]));
end;
volumeFullPath := toVolumeFullPath(objectPath);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('objectPath')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
if not utfFullPath.endsWith('/') then begin
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
findData := system.getMemory(sizeof(baseunix.DirEnt));
try
&Array.zeroRaw(findData^, sizeof(baseunix.DirEnt));
position := utfFullPath.lastIndexOf('/') + 1;
if (attributes.st_mode and baseunix.S_IFDIR) <> 0 then findData^.d_type := DT_DIR;
&Array.copyRaw(utfFullPath[position], findData^.d_name, sizeof(char) * (utfFullPath.length - position + 1));
result := VolumeObjectEnumeration.create(utfFullPath.substring(1, position), nil, findData^);
finally
system.freeMemory(findData);
end;
exit;
end;
findHandle := baseunix.fpOpenDir(system.PAnsiChar(utfFullPath));
if findHandle = nil then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
findData := baseunix.fpReadDir(findHandle^);
result := VolumeObjectEnumeration.create(utfFullPath, findHandle, findData^);
end;
function VolumeFileSystem.createFile(const fileName: UnicodeString): ByteWriter;
const
DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
var
fileHandle: int;
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY or baseunix.O_CREAT or baseunix.O_EXCL, DEFAULT_RIGHTS);
if fileHandle < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysEEXIST,
baseunix.ESysEISDIR:
raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.rewriteFile(const fileName: UnicodeString): ByteWriter;
const
DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
var
fileHandle: int;
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY or baseunix.O_CREAT or baseunix.O_TRUNC, DEFAULT_RIGHTS);
if fileHandle < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysEISDIR:
raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.openFileForAppend(const fileName: UnicodeString): ByteWriter;
var
fileHandle: int;
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY);
if fileHandle < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysEISDIR:
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
baseunix.fpLSeek(fileHandle, 0, baseunix.SEEK_END);
result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.openFileForRead(const fileName: UnicodeString): ByteReader;
var
fileHandle: int;
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
attributes: baseunix.Stat;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_RDONLY or baseunix.O_NONBLOCK);
if fileHandle < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(utfFullPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if (baseunix.fpFStat(fileHandle, attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then begin
baseunix.fpClose(fileHandle);
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(utfFullPath)
]));
end;
result := FileInputStream.create(long(fileHandle));
end;
function VolumeFileSystem.openFile(const fileName: UnicodeString): ByteStream;
var
fileHandle: int;
maximumNameLength: int;
standardNameLength: int;
utfFullPath: AnsiString;
volumeFullPath: UnicodeString;
begin
standardNameLength := fileName.length;
maximumNameLength := fldObjectNameMaximumLength;
if standardNameLength > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
CoAnsiString.create('fileName')
]));
end;
volumeFullPath := toVolumeFullPath(fileName);
if volumeFullPath.length > maximumNameLength then begin
raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
CoAnsiString.create('fileName')
]));
end;
if not isAttached() then begin
raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
end;
utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_RDWR);
if fileHandle < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysEISDIR:
raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysENOENT,
baseunix.ESysENOTDIR:
raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
CoAnsiString.create(utfFullPath)
]));
baseunix.ESysEROFS:
raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
CoAnsiString.create(fldCanonicalRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := FileBidirectStream.create(long(fileHandle), fldCanonicalRootPath);
end;
function VolumeFileSystem.newAttributes(): Attributes;
begin
result := VolumeObjectAttributes.create();
end;
{%endregion}
{%region VolumeObjectAttributes }
class procedure VolumeObjectAttributes.initialize();
var
index: int;
begin
attrIds := [
B_OTHER_EXECUTABLE, B_OTHER_WRITEABLE, B_OTHER_READABLE,
B_GROUP_EXECUTABLE, B_GROUP_WRITEABLE, B_GROUP_READABLE,
B_OWNER_EXECUTABLE, B_OWNER_WRITEABLE, B_OWNER_READABLE,
'', '', '', '', '', B_DIRECTORY, '',
L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME
];
index := system.length(attrIds);
attrHashes := &Array.newLong1d(index);
for index := index - 1 downto 0 do begin
attrHashes[index] := (CoAnsiString.create(attrIds[index]) as RefCountInterface).getHashCode();
end;
end;
class procedure VolumeObjectAttributes.finalize();
begin
attrIds := nil;
attrHashes := nil;
end;
class procedure VolumeObjectAttributes.stringAttributeIdIsInvalid(const attributeId: AnsiString);
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
if attributeId.startsWith('s') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
class function VolumeObjectAttributes.booleanAttributeIdToIndex(const attributeId: AnsiString): int;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 16);
if (index >= 0) and (attributeId = attrIds[index]) then begin
result := index - 0;
exit;
end;
if attributeId.startsWith('b') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
class function VolumeObjectAttributes.longAttributeIdToIndex(const attributeId: AnsiString): int;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 16, 8);
if (index >= 0) and (attributeId = attrIds[index]) then begin
result := index - 16;
exit;
end;
if attributeId.startsWith('l') then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
CoAnsiString.create(attributeId)
]));
end;
constructor VolumeObjectAttributes.create();
begin
inherited create();
fldTimes := &Array.newLong1d(2);
end;
procedure VolumeObjectAttributes.setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
var
mask: int;
begin
if attributeId <> B_READ_ONLY then begin
mask := 1 shl booleanAttributeIdToIndex(attributeId);
end else begin
if attributeValue then begin
mask := baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH;
end else begin
mask := baseunix.S_IWUSR;
end;
attributeValue := not attributeValue;
end;
if attributeValue then begin
fldAttributes := fldAttributes or mask;
exit;
end;
fldAttributes := fldAttributes and not mask;
end;
procedure VolumeObjectAttributes.setLongAttribute(const attributeId: AnsiString; attributeValue: long);
var
index: int;
begin
index := longAttributeIdToIndex(attributeId);
fldTimes[index] := attributeValue;
end;
procedure VolumeObjectAttributes.setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
begin
stringAttributeIdIsInvalid(attributeId);
end;
function VolumeObjectAttributes.isSupportedAttributeId(const attributeId: AnsiString): boolean;
var
index: int;
begin
if attributeId.length <= 0 then begin
result := false;
exit;
end;
if attributeId = B_READ_ONLY then begin
result := true;
exit;
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
result := (index >= 0) and (attributeId = attrIds[index]);
end;
function VolumeObjectAttributes.getBooleanAttribute(const attributeId: AnsiString): boolean;
var
mask: int;
begin
if attributeId = B_READ_ONLY then begin
mask := baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH;
result := (fldAttributes and mask) = 0;
exit;
end;
mask := 1 shl booleanAttributeIdToIndex(attributeId);
result := (fldAttributes and mask) <> 0;
end;
function VolumeObjectAttributes.getLongAttribute(const attributeId: AnsiString): long;
var
index: int;
begin
index := longAttributeIdToIndex(attributeId);
result := fldTimes[index];
end;
function VolumeObjectAttributes.getStringAttribute(const attributeId: AnsiString): UnicodeString;
begin
stringAttributeIdIsInvalid(attributeId);
result := ''; { недостижимый код }
end;
function VolumeObjectAttributes.displayName(const attributeId: AnsiString): UnicodeString;
var
index: int;
begin
if attributeId.length <= 0 then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
end;
if attributeId = B_READ_ONLY then begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.read-only');
exit;
end;
index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
if (index >= 0) and (attributeId = attrIds[index]) then case index of
0: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-executable');
exit;
end;
1: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-writeable');
exit;
end;
2: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-readable');
exit;
end;
3: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-executable');
exit;
end;
4: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-writeable');
exit;
end;
5: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-readable');
exit;
end;
6: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-executable');
exit;
end;
7: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-writeable');
exit;
end;
8: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-readable');
exit;
end;
14: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.directory');
exit;
end;
16: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-access-time');
exit;
end;
17: begin
result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-write-time');
exit;
end;
end;
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
CoAnsiString.create(attributeId)
]));
end;
function VolumeObjectAttributes.getSupportedAttributeIds(): AnsiString_Array1d;
begin
result := [
B_OTHER_EXECUTABLE, B_OTHER_WRITEABLE, B_OTHER_READABLE, B_GROUP_EXECUTABLE, B_GROUP_WRITEABLE, B_GROUP_READABLE,
B_OWNER_EXECUTABLE, B_OWNER_WRITEABLE, B_OWNER_READABLE, B_DIRECTORY, B_READ_ONLY, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME
];
end;
{%endregion}
{%region VolumeObjectEnumeration }
procedure VolumeObjectEnumeration.setAttributes(const info);
var
attr: int;
timeInMillis: long;
path: AnsiString;
internalLastAccessTime: TM;
internalLastWriteTime: TM;
attributes: baseunix.Stat;
findData: baseunix.DirEnt absolute info;
begin
path := AnsiString(system.PAnsiChar(@(findData.d_name)));
name := path.toUTF16();
path := fldFullDirectoryPath + path;
&Array.zeroRaw(attributes, sizeof(baseunix.Stat));
if baseunix.fpStat(system.PAnsiChar(path), attributes) <> 0 then begin
size := 0;
setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, int(findData.d_type) = DT_DIR);
setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, true);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, false);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, false);
setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, 0);
setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, 0);
exit;
end;
attr := int(attributes.st_mode);
if (attr and baseunix.S_IFDIR) <> 0 then begin
size := 0;
end else begin
size := attributes.st_size;
end;
setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (attr and baseunix.S_IFDIR) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (attr and (baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH)) = 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, (attr and baseunix.S_IRUSR) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, (attr and baseunix.S_IWUSR) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, (attr and baseunix.S_IXUSR) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, (attr and baseunix.S_IRGRP) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, (attr and baseunix.S_IWGRP) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, (attr and baseunix.S_IXGRP) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, (attr and baseunix.S_IROTH) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, (attr and baseunix.S_IWOTH) <> 0);
setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, (attr and baseunix.S_IXOTH) <> 0);
&Array.zeroRaw(internalLastAccessTime, sizeof(TM));
&Array.zeroRaw(internalLastWriteTime, sizeof(TM));
if (gmtTime(@(attributes.st_atime), @internalLastAccessTime) = nil) or (gmtTime(@(attributes.st_mtime), @internalLastWriteTime) = nil) then begin
setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, 0);
setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, 0);
exit;
end;
with internalLastAccessTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_atime_nsec) div 1000000);
setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, timeInMillis);
with internalLastWriteTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_mtime_nsec) div 1000000);
setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, timeInMillis);
end;
constructor VolumeObjectEnumeration.create(const fullDirectoryPath: AnsiString; handle: baseunix.PDir; const info);
begin
inherited create(VolumeObjectAttributes.create());
fldFullDirectoryPath := fullDirectoryPath;
fldHandle := handle;
setAttributes(info);
end;
procedure VolumeObjectEnumeration.close();
var
handle: baseunix.PDir;
begin
handle := fldHandle;
destroy;
if (handle <> nil) and (baseunix.fpCloseDir(handle^) <> 0) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function VolumeObjectEnumeration.findNext(): boolean;
var
root: boolean;
onam: AnsiString;
path: UnicodeString;
full: UnicodeString;
findHandle: baseunix.PDir;
findData: baseunix.PDirEnt;
begin
findHandle := fldHandle;
if findHandle = nil then begin
result := false;
exit;
end;
path := fldFullDirectoryPath.toUTF16();
root := FileSystemRoot.getRootPathOf(path) = path.substring(1, path.length);
repeat
baseunix.fpSetErrNo(0);
findData := baseunix.fpReadDir(findHandle^);
if findData = nil then break;
onam := AnsiString(system.PAnsiChar(@(findData^.d_name)));
full := path + onam.toUTF16();
if (not root or (onam <> '..')) and (FileSystemRoot.getRootPathOf(full) <> full) then begin
setAttributes(findData^);
result := true;
exit;
end;
until false;
if baseunix.fpGetErrNo() <> 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := false;
end;
{%endregion}
{%region HandleInputStream }
constructor HandleInputStream.create(handle: long);
begin
inherited create();
fldHandle := handle;
end;
procedure HandleInputStream.close();
begin
end;
function HandleInputStream.skip(bytesQuantity: long): long;
var
data: int;
readed: int;
handle: int;
skiped: long;
begin
if bytesQuantity <= 0 then begin
result := 0;
exit;
end;
data := 0;
readed := 0;
skiped := 0;
handle := int(fldHandle);
repeat
readed := int(baseunix.fpRead(handle, data, 1));
if readed < 0 then begin
if skiped > 0 then break;
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then break;
inc(skiped);
dec(bytesQuantity);
until bytesQuantity > 0;
result := skiped;
end;
function HandleInputStream.read(): int;
var
data: int;
readed: int;
begin
data := 0;
readed := int(baseunix.fpRead(int(fldHandle), data, 1));
if readed < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then begin
result := -1;
exit;
end;
result := data;
end;
function HandleInputStream.read(const dst: byte_Array1d): int;
begin
result := read(dst, 0, system.length(dst));
end;
function HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
readed: int;
begin
&Array.checkBounds(dst, offset, length);
if length <= 0 then begin
result := 0;
exit;
end;
readed := int(baseunix.fpRead(int(fldHandle), dst[offset], unixtype.TSize(length)));
if readed < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if readed = 0 then begin
result := -1;
exit;
end;
result := readed;
end;
{%endregion}
{%region HandleOutputStream }
constructor HandleOutputStream.create(handle: long; const rootPath: AnsiString);
begin
inherited create();
fldHandle := handle;
fldRootPath := rootPath;
end;
procedure HandleOutputStream.close();
begin
end;
procedure HandleOutputStream.flush();
begin
end;
procedure HandleOutputStream.write(byteData: int);
var
writed: int;
begin
writed := int(baseunix.fpWrite(int(fldHandle), byteData, 1));
if writed < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if writed < 1 then begin
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
end;
end;
procedure HandleOutputStream.write(const src: byte_Array1d);
begin
write(src, 0, system.length(src));
end;
procedure HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
var
writed: int;
begin
&Array.checkBounds(src, offset, length);
if length <= 0 then exit;
writed := int(baseunix.fpWrite(int(fldHandle), src[offset], unixtype.TSize(length)));
if writed < 0 then case baseunix.fpGetErrNo() of
baseunix.ESysENOSPC:
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
else
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
if writed < length then begin
raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
CoAnsiString.create(fldRootPath)
]));
end;
end;
{%endregion}
{%region HandleBidirectStream }
constructor HandleBidirectStream.create(handleForRead, handleForWrite: long);
begin
inherited create();
fldHandleForRead := handleForRead;
fldHandleForWrite := handleForWrite;
end;
destructor HandleBidirectStream.destroy;
begin
fldReader.free();
fldWriter.free();
inherited destroy;
end;
procedure HandleBidirectStream.close();
var
handleForRead: int;
handleForWrite: int;
begin
handleForRead := int(fldHandleForRead);
handleForWrite := int(fldHandleForWrite);
destroy;
if (baseunix.fpClose(handleForRead) < 0) or (handleForRead <> handleForWrite) and (baseunix.fpClose(handleForWrite) < 0) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
function HandleBidirectStream.getReader(): ByteReader;
begin
result := fldReader;
end;
function HandleBidirectStream.getWriter(): ByteWriter;
begin
result := fldWriter;
end;
{%endregion}
{%region FileSeekExtension }
constructor FileSeekExtension.create(handle: long);
begin
inherited create();
fldHandle := handle;
end;
function FileSeekExtension.available(): long;
var
handle: int;
locLength: long;
locPosition: long;
begin
handle := int(fldHandle);
locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
if locLength < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := locLength - locPosition;
end;
function FileSeekExtension.seek(offset: long; from: SeekFrom): long;
var
handle: int;
locLength: long;
locPosition: long;
begin
if (from < SeekFrom.sfBegin) or (from > SeekFrom.sfEnd) then begin
raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('from') ]));
end;
handle := int(fldHandle);
locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
if locLength < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
case from of
SeekFrom.sfBegin:
locPosition := offset;
SeekFrom.sfEnd:
locPosition := offset + locLength;
else
locPosition := offset + locPosition;
end;
if locPosition < 0 then locPosition := 0;
if locPosition > locLength then locPosition := locLength;
locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := locPosition;
end;
function FileSeekExtension.position(): long;
var
locPosition: long;
begin
locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := locPosition;
end;
function FileSeekExtension.size(): long;
var
handle: int;
locLength: long;
locPosition: long;
begin
handle := int(fldHandle);
locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
if locLength < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
if locPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := locLength;
end;
{%endregion}
{%region FileInputStream }
constructor FileInputStream.create(handle: long; seekable: FileSeekExtension);
begin
inherited create(handle);
if seekable <> nil then begin
fldOwnedSeekable := false;
end else begin
seekable := FileSeekExtension.create(handle);
fldOwnedSeekable := true;
end;
fldExtensions := [ self, seekable ];
end;
destructor FileInputStream.destroy;
begin
if fldOwnedSeekable then begin
fldExtensions[1].free();
end;
inherited destroy;
end;
procedure FileInputStream.close();
var
handle: int;
begin
handle := int(fldHandle);
destroy;
if baseunix.fpClose(handle) < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileInputStream.reset();
begin
FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
end;
procedure FileInputStream.mark(transferLimit: int);
var
locPosition: long;
begin
locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
fldMarked := 0;
exit;
end;
fldMarked := locPosition;
end;
function FileInputStream.skip(bytesQuantity: long): long;
var
handle: int;
locLength: long;
oldPosition: long;
newPosition: long;
begin
if bytesQuantity <= 0 then begin
result := 0;
exit;
end;
handle := int(fldHandle);
oldPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
if oldPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
if locLength < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
newPosition := bytesQuantity + oldPosition;
if (newPosition < 0) or (newPosition > locLength) then newPosition := locLength;
newPosition := baseunix.fpLSeek(handle, newPosition, baseunix.SEEK_SET);
if newPosition < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
result := newPosition - oldPosition;
end;
{%endregion}
{%region FileOutputStream }
constructor FileOutputStream.create(handle: long; const rootPath: AnsiString; seekable: FileSeekExtension; reader: FileInputStream);
begin
inherited create(handle, rootPath);
if seekable <> nil then begin
fldOwnedSeekable := false;
end else begin
seekable := FileSeekExtension.create(handle);
fldOwnedSeekable := true;
end;
fldReader := reader;
fldExtensions := [ self, seekable ];
end;
destructor FileOutputStream.destroy;
begin
if fldOwnedSeekable then begin
fldExtensions[1].free();
end;
inherited destroy;
end;
procedure FileOutputStream.close();
var
handle: int;
begin
handle := int(fldHandle);
destroy;
if baseunix.fpClose(handle) < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileOutputStream.flush();
begin
if doSyscall(SYSCALL_NR_FSYNC, int(fldHandle)) < 0 then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
end;
procedure FileOutputStream.truncate();
var
handle: int;
locLength: long;
reader: FileInputStream;
begin
handle := int(fldHandle);
locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
if (locLength < 0) or (baseunix.fpFTruncate(handle, locLength) < 0) then begin
raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
end;
reader := fldReader;
if fldMarked > locLength then fldMarked := locLength;
if (reader <> nil) and (reader.fldMarked > locLength) then reader.fldMarked := locLength;
end;
procedure FileOutputStream.reset();
begin
FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
end;
procedure FileOutputStream.mark(transferLimit: int);
var
locPosition: long;
begin
locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
if locPosition < 0 then begin
fldMarked := 0;
exit;
end;
fldMarked := locPosition;
end;
{%endregion}
{%region CurrentEnvironment }
procedure CurrentEnvironment.update();
const
BUFFER_LENGTH = int($0200);
var
index: int;
readed: int;
str: UnicodeString;
name: UnicodeString;
value: UnicodeString;
buffer: byte_Array1d;
stream: byte_Array1d;
ptr: system.PAnsiChar;
vars: HashtableOfUnicodeStringToUnicodeString;
emon: Mutex;
begin
stream := nil;
try
buffer := &Array.newByte1d(BUFFER_LENGTH);
with ByteArrayOutputStream.create() do try
with FileInputStream.create(baseunix.fpOpen('/proc/self/environ', baseunix.O_RDONLY)) do try
repeat
readed := read(buffer, 0, BUFFER_LENGTH);
if readed < 0 then break;
write(buffer, 0, readed);
until false;
finally
close();
end;
write(0); { Последний нулевой байт обязателен! Нужен для работы нижеследующего кода. }
stream := toByteArray();
finally
close();
end;
buffer := nil;
except
end;
emon := fldMonitor;
emon.beginSynchronized();
try
vars := fldVariables;
vars.clear();
if stream <> nil then begin
ptr := @(stream[0]);
while ptr[0] <> #$00 do begin
str := AnsiString(ptr).toUTF16();
index := str.indexOf('=');
name := str.substring(1, index).trim();
value := str.substring(index + 1).trim();
vars[name] := value;
inc(ptr, system.length(ptr) + 1);
end;
end;
finally
emon.endSynchronized();
end;
end;
constructor CurrentEnvironment.create();
begin
inherited create();
update();
end;
function CurrentEnvironment.isEmulation(): boolean;
begin
result := true;
end;
{%endregion}
{%region CurrentProcess }
class procedure CurrentProcess.initialize();
begin
instance := CurrentProcess.create();
end;
class procedure CurrentProcess.finalize();
begin
instance.free();
end;
class function CurrentProcess.parseCommandLine(): UnicodeString_Array1d;
const
BUFFER_LENGTH = int($0200);
var
length: int;
readed: int;
buffer: byte_Array1d;
stream: byte_Array1d;
ptr: system.PAnsiChar;
block: system.PAnsiChar;
arguments: UnicodeString_Array1d;
begin
stream := nil;
try
buffer := &Array.newByte1d(BUFFER_LENGTH);
with ByteArrayOutputStream.create() do try
with FileInputStream.create(baseunix.fpOpen('/proc/self/cmdline', baseunix.O_RDONLY)) do try
repeat
readed := read(buffer, 0, BUFFER_LENGTH);
if readed < 0 then break;
write(buffer, 0, readed);
until false;
finally
close();
end;
write(0); { Последний нулевой байт обязателен! Нужен для работы нижеследующего кода. }
stream := toByteArray();
finally
close();
end;
buffer := nil;
except
end;
length := 0;
arguments := nil;
if stream <> nil then begin
block := @(stream[0]);
ptr := block;
while ptr[0] <> #$00 do begin
inc(ptr, system.length(ptr) + 1);
inc(length);
end;
arguments := &Array.newUnicodeString1d(length);
length := 0;
ptr := block;
while ptr[0] <> #$00 do begin
arguments[length] := AnsiString(ptr).toUTF16();
inc(ptr, system.length(ptr) + 1);
inc(length);
end;
end;
if length <= 0 then begin
result := [ AnsiString(baseunix.fpReadLink('/proc/self/exe')).toUTF16() ];
exit;
end;
arguments[0] := AnsiString(baseunix.fpReadLink('/proc/self/exe')).toUTF16();
result := arguments;
end;
procedure CurrentProcess.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
begin
if not newWorkingDirectory.startsWith('/') or (baseunix.fpChDir(FileSystemRoot.toInternalPath(newWorkingDirectory).toUTF8()) <> 0) then begin
raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
end;
procedure CurrentProcess.setCommandLine(const newCommandLine: UnicodeString_Array1d);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardInput(newStandardInput: ByteReader);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardOutput(newStandardOutput: ByteWriter);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
procedure CurrentProcess.setStandardError(newStandardError: ByteWriter);
begin
raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
]));
end;
function CurrentProcess.getWorkingDirectory(): UnicodeString;
var
path: AnsiString;
begin
path := baseunix.fpReadLink('/proc/self/cwd');
if not path.endsWith('/') then path := path + '/';
result := FileSystemRoot.toObjectPath(path.toUTF16());
end;
function CurrentProcess.createEnvironment(): Environment;
begin
result := CurrentEnvironment.create();
end;
constructor CurrentProcess.create();
var
locStdIn: HandleInputStream;
locStdOut: HandleOutputStream;
locStdErr: HandleOutputStream;
begin
inherited create();
locStdIn := HandleInputStream.create(0);
locStdOut := HandleOutputStream.create(1);
locStdErr := HandleOutputStream.create(2);
fldStarting := true;
fldCommandLine := parseCommandLine();
fldStandardInput := locStdIn;
fldStandardOutput := locStdOut;
fldStandardError := locStdErr;
fldStandardInputRef := locStdIn;
fldStandardOutputRef := locStdOut;
fldStandardErrorRef := locStdErr;
end;
destructor CurrentProcess.destroy;
begin
fldStandardInputRef.free();
fldStandardOutputRef.free();
fldStandardErrorRef.free();
inherited destroy;
end;
function CurrentProcess.isTerminated(): boolean;
begin
result := false;
end;
function CurrentProcess.getExitCode(): int;
begin
result := STILL_ACTIVE;
end;
{%endregion}
{$ENDIF}
{%region OutOfResourcesError}
procedure OutOfResourcesError.freeAllow();
begin
inherited freeAllow();
end;
procedure OutOfResourcesError.freeDisallow();
begin
inherited freeDisallow();
end;
{%endregion}
{%region MemoryRegionCollection }
constructor MemoryRegionCollection.create(const regions: MemoryRegion_Array1d; length: int);
begin
inherited create();
fldLength := length;
fldRegions := regions;
end;
procedure MemoryRegionCollection.copyInto(const dstArray; dstOffset: int);
begin
&Array.copyUnknowns(fldRegions, 0, dstArray, dstOffset, fldLength);
end;
function MemoryRegionCollection.getLength(): int;
begin
result := fldLength;
end;
function MemoryRegionCollection.componentAt(index: int): MemoryRegion;
begin
if (index < 0) or (index >= fldLength) then begin
raise ArrayIndexOutOfBoundsException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'out-of-bounds.array-index'));
end;
result := fldRegions[index];
end;
function MemoryRegionCollection.toArray(): MemoryRegion_Array1d;
var
length: int;
regions: MemoryRegion_Array1d;
begin
length := fldLength;
regions := MemoryRegion_Array1d(&Array.newIUnknown1d(length));
&Array.copyUnknowns(fldRegions, 0, regions, 0, length);
result := regions;
end;
{%endregion}
{%region FileSystemRootCollection }
constructor FileSystemRootCollection.create(const roots: FileSystemRoot_Array1d; length: int);
begin
inherited create();
fldLength := length;
fldRoots := roots;
end;
procedure FileSystemRootCollection.copyInto(const dstArray; dstOffset: int);
begin
&Array.copyObjects(fldRoots, 0, dstArray, dstOffset, fldLength);
end;
function FileSystemRootCollection.getLength(): int;
begin
result := fldLength;
end;
function FileSystemRootCollection.componentAt(index: int): FileSystemRoot;
begin
if (index < 0) or (index >= fldLength) then begin
raise ArrayIndexOutOfBoundsException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'out-of-bounds.array-index'));
end;
result := fldRoots[index];
end;
function FileSystemRootCollection.toArray(): FileSystemRoot_Array1d;
var
length: int;
roots: FileSystemRoot_Array1d;
begin
length := fldLength;
roots := FileSystemRoot_Array1d(&Array.newTObject1d(length));
&Array.copyObjects(fldRoots, 0, roots, 0, length);
result := roots;
end;
{%endregion}
{%region Extensions }
function Extensions.getExtensions(): Extension_Array1d;
var
index: int;
length: int;
source: TObject_Array1d;
extensions: Extension_Array1d;
begin
source := fldExtensions;
length := system.length(source);
extensions := Extension_Array1d(&Array.newISimple1d(length));
for index := length - 1 downto 0 do begin
extensions[index] := source[index] as Extension;
end;
result := extensions;
end;
function Extensions.getExtension(const typ: ShortString): Extension;
var
index: int;
source: TObject_Array1d;
ext: Extension;
begin
source := fldExtensions;
for index := 0 to system.length(source) - 1 do begin
ext := source[index] as Extension;
if Lang.isInstance(ext, typ) then begin
result := Extension(Lang.cast(ext, typ));
exit;
end;
end;
result := nil;
end;
{%endregion}
{%region FileBidirectStream }
constructor FileBidirectStream.create(handle: long; const rootPath: AnsiString);
var
reader: FileInputStream;
writer: FileOutputStream;
seekable: FileSeekExtension;
begin
inherited create(handle, handle);
seekable := FileSeekExtension.create(handle);
reader := FileBidirectStreamReader.create(handle, seekable);
writer := FileBidirectStreamWriter.create(handle, rootPath, seekable, reader);
fldReader := reader;
fldWriter := writer;
fldExtensions := [ seekable ];
end;
destructor FileBidirectStream.destroy;
begin
fldExtensions[0].free();
inherited destroy;
end;
{%endregion}
{%region FileBidirectStreamReader }
procedure FileBidirectStreamReader.close();
begin
end;
{%endregion}
{%region FileBidirectStreamWriter }
procedure FileBidirectStreamWriter.close();
begin
end;
{%endregion}
{%region PipeStream }
constructor PipeStream.create(handleForRead, handleForWrite: long);
begin
inherited create(handleForRead, handleForWrite);
fldReader := HandleInputStream.create(handleForRead);
fldWriter := HandleOutputStream.create(handleForWrite);
end;
{%endregion}
{%region} initialization
Lang.registerInterfaces([
typeInfo(MemoryRegion)
]);
exceptionInitialize();
fileSystemRootInitialize();
VolumeObjectAttributes.initialize();
SystemInfo.initialize();
CurrentProcess.initialize();
fileSystemRootCreateWorkingRoot();
{%endregion}
{%region} finalization
CurrentProcess.finalize();
VolumeObjectAttributes.finalize();
fileSystemRootFinalize();
exceptionFinalize();
{%endregion}
end.