fileio.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    FileIO – модуль файлового ввода-вывода.

    Copyright © 2017 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit FileIO;

{$MODE DELPHI,EXTENDEDSYNTAX ON}

interface

uses
    Lang,
    {$IF DEFINED(GO32V2)} IntfGo32
    {$ELSEIF DEFINED(WINDOWS)} IntfWin
    {$ENDIF}, IOStream, VFS;

{$ASMMODE INTEL,CALLING REGISTER,INLINE ON,GOTO ON}
{$H+,I-,J-,M-,Q-,R-,T-}

type
    FileInputStream = class;
    FileOutputStream = class;
    FileStream = class;
    LocalFileSystem = class;

    FileInputStream = class(InputStream)
    public
        constructor create(const osFileName: UnicodeString);
        procedure close(); override;
        function seekSupported(): boolean; override;
        function seek(delta: long): long; override;
        function size(): long; override;
        function position(): long; override;
        function read(): int; overload; override;
        function read(const dst: byte_Array1d; offset, length: int): int; overload; override;
        function hasOpenError(): boolean; virtual;
        procedure checkOpenError(); virtual;
    strict private
        closed: boolean;
        error: int;
        block: byte_Array1d;
        blockPosition: short;
        blockSize: short;
        filePosition: long;
        fileSize: long;
        fileID: OSIntfFileOpenID;
        function readFile(position: long): boolean;
    end;

    FileOutputStream = class(OutputStream)
    public
        constructor create(const osFileName: UnicodeString; appending: boolean);
        procedure close(); override;
        procedure flush(); override;
        function write(value: int): boolean; overload; override;
        function write(const src: byte_Array1d; offset, length: int): int; overload; override;
        function hasOpenError(): boolean; virtual;
        procedure checkOpenError(); virtual;
    strict private
        closed: boolean;
        error: int;
        block: byte_Array1d;
        blockSize: short;
        fileID: OSIntfFileOpenID;
        function writeFile(): boolean;
    end;

    FileStream = class(InputOutputStream)
    private
        const ERROR_NO_ERROR = 0;
        const ERROR_OPEN_ERROR = 1;
        const ERROR_FILE_NAME_NOT_SPECIFIED = 2;
        const DEFAULT_BLOCK_SIZE = $1000;

    public
        constructor create(const osFileName: UnicodeString);
        procedure close(); override;
        procedure flush(); override;
        function seekSupported(): boolean; override;
        function seek(delta: long): long; override;
        function size(): long; override;
        function position(): long; override;
        function read(): int; overload; override;
        function read(const dst: byte_Array1d; offset, length: int): int; overload; override;
        function write(value: int): boolean; overload; override;
        function write(const src: byte_Array1d; offset, length: int): int; overload; override;
        procedure truncate(); override;
        function hasOpenError(): boolean; virtual;
        procedure checkOpenError(); virtual;
    strict private
        closed: boolean;
        error: int;
        block: byte_Array1d;
        blockRewrited: boolean;
        blockPosition: short;
        blockSize: short;
        filePosition: long;
        fileSize: long;
        fileID: OSIntfFileOpenID;
        function readFile(position: long): boolean;
        function writeFile(): boolean;
    end;

    LocalFileSystem = class(RefCountInterfacedObject, VirtualFileSystemReadOnly,
            VirtualFileSystemReadWrite)
    public
        class function getInstance(): VirtualFileSystemReadWrite;
    private
        class procedure clinit();
        class procedure cldone();
        class procedure timeToOSIntf(const internal: long; out osintf: OSIntfDateTimeRecord);
        class function timeToInternal(const osintf: OSIntfDateTimeRecord): long;
    strict private
        class var INSTANCE: VirtualFileSystemReadWrite;

    public
        constructor create();
        procedure readAttributes(const fileOrDirName: UnicodeString;
                attrDst: FileAttributes); virtual;
        function fileNameCorrect(const fileOrDirName: UnicodeString): boolean; virtual;
        function fileNameCaseSensitive(): boolean; virtual;
        function fileNameMaximumLength(): int; virtual;
        function findFirst(): FileEnumerator; overload; virtual;
        function findFirst(const pathOrFileName: UnicodeString): FileEnumerator; overload; virtual;
        function openFileForRead(const fileName: UnicodeString): InputStream; virtual;
        procedure writeAttributes(const fileOrDirName: UnicodeString;
                attrSrc: FileAttributes); virtual;
        procedure move(const oldFileOrDirName, newFileOrDirName: UnicodeString); virtual;
        procedure deleteDirectory(const dirName: UnicodeString); virtual;
        procedure createDirectory(const dirName: UnicodeString); virtual;
        procedure deleteFile(const fileName: UnicodeString); virtual;
        function createFile(const fileName: UnicodeString): OutputStream; virtual;
        function openFileForAppending(const fileName: UnicodeString): OutputStream; virtual;
        function openFileForReadWrite(const fileName: UnicodeString): InputOutputStream; virtual;
    end;

resourcestring
    msgFileOpenError = 'Ошибка при открытии файла.';
    msgFileNameNotSpecified = 'Имя файла не задано.';
    msgFileAlreadyClosed = 'Файл уже был закрыт раннее.';
    msgFileFailedRead = 'Не удалось прочитать данные из файла.';
    msgFileFailedWrite = 'Не удалось записать данные в файл.';
    msgFileFailedReadAttributes = 'Не удалось прочитать атрибуты файла.';
    msgFileFailedWriteAttributes = 'Не удалось записать атрибуты файла.';
    msgFileFailedMoveOrRename = 'Не удалось переместить и/или переименовать файл.';
    msgFileFailedDelete = 'Не удалось удалить файл.';
    msgDirectoryFailedCreate = 'Не удалось создать папку.';
    msgDirectoryFailedDelete = 'Не удалось удалить папку.';

implementation

type
    OSFileEnumerator = class(FileEnumerator)
    public
        constructor create(const findInfo: OSIntfFileFindInfo);
        procedure close(); override;
        function findNext(): boolean; override;
    strict private
        findInfo: OSIntfFileFindInfo;
        procedure assignAttributes(); overload;
    end;

{ FileInputStream }

constructor FileInputStream.create(const osFileName: UnicodeString);
begin
    inherited create();
    if length(osFileName) <= 0 then begin
        self.error := FileStream.ERROR_FILE_NAME_NOT_SPECIFIED;
        exit;
    end;
    if osintfFileOpenForReadOnly(osFileName, self.fileID) = false then begin
        self.error := FileStream.ERROR_OPEN_ERROR;
        exit;
    end;
    self.closed := false;
    self.error := FileStream.ERROR_NO_ERROR;
    self.block := byte_Array1d_create(FileStream.DEFAULT_BLOCK_SIZE);
    self.blockPosition := 0;
    self.blockSize := 0;
    self.filePosition := -1;
    self.fileSize := osintfFileGetSize(self.fileID);
    readFile(0);
end;

procedure FileInputStream.close();
begin
    checkOpenError();
    if osintfFileClose(fileID) then begin
        closed := true;
    end;
end;

function FileInputStream.seekSupported(): boolean;
begin
    result := (error = 0) and (closed = false);
end;

function FileInputStream.seek(delta: long): long;
begin
    checkOpenError();
    result := Math.max(0, Math.min(filePosition + blockPosition + delta, fileSize));
    if readFile(result and (-FileStream.DEFAULT_BLOCK_SIZE)) = false then begin
        raise IOException.create(msgFileFailedRead);
    end;
    blockPosition := int(result) and (FileStream.DEFAULT_BLOCK_SIZE - 1);
end;

function FileInputStream.size(): long;
begin
    checkOpenError();
    result := fileSize;
end;

function FileInputStream.position(): long;
begin
    checkOpenError();
    result := filePosition + blockPosition;
end;

function FileInputStream.read(): int;
var
    bsiz: int;
    bpos: int;
begin
    checkOpenError();
    result := -1;
    bsiz := blockSize;
    bpos := blockPosition;
    if bpos >= bsiz then begin
        exit;
    end;
    result := block[bpos] and $ff;
    inc(bpos);
    blockPosition := short(bpos);
    if (bpos >= bsiz) and (readFile(filePosition + bsiz) = false) then begin
        raise IOException.create(msgFileFailedRead);
    end;
end;

function FileInputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    bsiz: int;
    bpos: int;
    reading: int;
    remaining: int;
    block: byte_Array1d;
begin
    checkOpenError();
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    remaining := length;
    block := self.block;
    while remaining > 0 do begin
        bsiz := blockSize;
        bpos := blockPosition;
        reading := Math.min(bsiz - bpos, remaining);
        if reading = 0 then begin
            exit;
        end;
        arraycopyPrimitives(block, bpos, dst, offset, reading);
        inc(bpos, reading);
        inc(offset, reading);
        inc(result, reading);
        dec(remaining, reading);
        blockPosition := short(bpos);
        if (bpos >= bsiz) and (readFile(filePosition + bsiz) = false) then begin
            raise IOException.create(msgFileFailedRead);
        end;
    end;
end;

function FileInputStream.hasOpenError(): boolean;
begin
    result := (error <> 0) or (closed <> false);
end;

procedure FileInputStream.checkOpenError();
begin
    case error of
    FileStream.ERROR_NO_ERROR: begin
        if closed then begin
            raise IOException.create(msgFileAlreadyClosed);
        end;
    end;
    FileStream.ERROR_OPEN_ERROR: begin
        raise IOException.create(msgFileOpenError);
    end;
    FileStream.ERROR_FILE_NAME_NOT_SPECIFIED: begin
        raise IOException.create(msgFileNameNotSpecified);
    end;
    end;
end;

function FileInputStream.readFile(position: long): boolean;
var
    must: int;
    readed: int;
begin
    blockPosition := 0;
    if filePosition = position then begin
        result := true;
        exit;
    end;
    position := osintfFileSetPos(fileID, position);
    must := int(Math.min(fileSize - position, FileStream.DEFAULT_BLOCK_SIZE));
    if must > 0 then begin
        readed := osintfFileRead(fileID, @(block[0]), FileStream.DEFAULT_BLOCK_SIZE);
    end else begin
        readed := 0;
    end;
    blockSize := short(readed);
    filePosition := position;
    result := readed >= must;
end;

{ FileOutputStream }

constructor FileOutputStream.create(const osFileName: UnicodeString; appending: boolean);
var
    osintfFileOpenFuntion: function (const osFileName: UnicodeString;
            out fileID: OSIntfFileOpenID): boolean;
begin
    inherited create();
    if appending then begin
        osintfFileOpenFuntion := osintfFileOpenForAppending;
    end else begin
        osintfFileOpenFuntion := osintfFileCreate;
    end;
    if length(osFileName) <= 0 then begin
        self.error := FileStream.ERROR_FILE_NAME_NOT_SPECIFIED;
        exit;
    end;
    if osintfFileOpenFuntion(osFileName, self.fileID) = false then begin
        self.error := FileStream.ERROR_OPEN_ERROR;
        exit;
    end;
    self.closed := false;
    self.error := FileStream.ERROR_NO_ERROR;
    self.block := byte_Array1d_create(FileStream.DEFAULT_BLOCK_SIZE);
    self.blockSize := 0;
end;

procedure FileOutputStream.close();
var
    writed: boolean;
begin
    checkOpenError();
    writed := writeFile();
    if osintfFileClose(fileID) then begin
        closed := true;
    end;
    if writed = false then begin
        raise IOException.create(msgFileFailedWrite);
    end;
end;

procedure FileOutputStream.flush();
begin
    checkOpenError();
    if writeFile() = false then begin
        raise IOException.create(msgFileFailedWrite);
    end;
end;

function FileOutputStream.write(value: int): boolean;
var
    bsiz: int;
begin
    checkOpenError();
    bsiz := blockSize;
    block[bsiz] := byte(value);
    inc(bsiz);
    blockSize := short(bsiz);
    if (bsiz >= FileStream.DEFAULT_BLOCK_SIZE) and (writeFile() = false) then begin
        raise IOException.create(msgFileFailedWrite);
    end;
    result := true;
end;

function FileOutputStream.write(const src: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    bsiz: int;
    writing: int;
    remaining: int;
    block: byte_Array1d;
begin
    checkOpenError();
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    remaining := length;
    block := self.block;
    while remaining > 0 do begin
        bsiz := blockSize;
        writing := Math.min(FileStream.DEFAULT_BLOCK_SIZE - bsiz, remaining);
        if writing = 0 then begin
            exit;
        end;
        arraycopyPrimitives(src, offset, block, bsiz, writing);
        inc(bsiz, writing);
        inc(offset, writing);
        inc(result, writing);
        dec(remaining, writing);
        blockSize := short(bsiz);
        if (bsiz >= FileStream.DEFAULT_BLOCK_SIZE) and (writeFile() = false) then begin
            raise IOException.create(msgFileFailedWrite);
        end;
    end;
end;

function FileOutputStream.hasOpenError(): boolean;
begin
    result := (error <> 0) or (closed <> false);
end;

procedure FileOutputStream.checkOpenError();
begin
    case error of
    FileStream.ERROR_NO_ERROR: begin
        if closed then begin
            raise IOException.create(msgFileAlreadyClosed);
        end;
    end;
    FileStream.ERROR_OPEN_ERROR: begin
        raise IOException.create(msgFileOpenError);
    end;
    FileStream.ERROR_FILE_NAME_NOT_SPECIFIED: begin
        raise IOException.create(msgFileNameNotSpecified);
    end;
    end;
end;

function FileOutputStream.writeFile(): boolean;
var
    bsiz: int;
begin
    bsiz := blockSize;
    if bsiz <= 0 then begin
        result := true;
        exit;
    end;
    blockSize := 0;
    result := osintfFileWrite(fileID, @(block[0]), bsiz) >= bsiz;
end;

{ FileStream }

constructor FileStream.create(const osFileName: UnicodeString);
begin
    inherited create();
    if length(osFileName) <= 0 then begin
        self.error := FileStream.ERROR_FILE_NAME_NOT_SPECIFIED;
        exit;
    end;
    if osintfFileOpenForReadWrite(osFileName, self.fileID) = false then begin
        self.error := FileStream.ERROR_OPEN_ERROR;
        exit;
    end;
    self.closed := false;
    self.error := FileStream.ERROR_NO_ERROR;
    self.block := byte_Array1d_create(FileStream.DEFAULT_BLOCK_SIZE);
    self.blockRewrited := false;
    self.blockPosition := 0;
    self.blockSize := 0;
    self.filePosition := -1;
    self.fileSize := osintfFileGetSize(self.fileID);
    readFile(0);
end;

procedure FileStream.close();
var
    writed: boolean;
begin
    checkOpenError();
    writed := writeFile();
    if osintfFileClose(fileID) then begin
        closed := true;
    end;
    if writed = false then begin
        raise IOException.create(msgFileFailedWrite);
    end;
end;

procedure FileStream.flush();
begin
    checkOpenError();
    if writeFile() = false then begin
        raise IOException.create(msgFileFailedWrite);
    end;
end;

function FileStream.seekSupported(): boolean;
begin
    result := (error = 0) and (closed = false);
end;

function FileStream.seek(delta: long): long;
begin
    checkOpenError();
    result := Math.max(0, Math.min(filePosition + blockPosition + delta, fileSize));
    if (writeFile() = false) or
            (readFile(result and (-FileStream.DEFAULT_BLOCK_SIZE)) = false) then begin
        raise IOException.create(msgFileFailedRead);
    end;
    blockPosition := int(result) and (FileStream.DEFAULT_BLOCK_SIZE - 1);
end;

function FileStream.size(): long;
begin
    checkOpenError();
    result := fileSize;
end;

function FileStream.position(): long;
begin
    checkOpenError();
    result := filePosition + blockPosition;
end;

function FileStream.read(): int;
var
    bsiz: int;
    bpos: int;
begin
    checkOpenError();
    result := -1;
    bsiz := blockSize;
    bpos := blockPosition;
    if bpos >= bsiz then begin
        exit;
    end;
    result := block[bpos] and $ff;
    inc(bpos);
    blockPosition := short(bpos);
    if (bpos >= bsiz) and
            ((writeFile() = false) or (readFile(filePosition + bsiz) = false)) then begin
        raise IOException.create(msgFileFailedRead);
    end;
end;

function FileStream.read(const dst: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    bsiz: int;
    bpos: int;
    reading: int;
    remaining: int;
    block: byte_Array1d;
begin
    checkOpenError();
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    remaining := length;
    block := self.block;
    while remaining > 0 do begin
        bsiz := blockSize;
        bpos := blockPosition;
        reading := Math.min(bsiz - bpos, remaining);
        if reading = 0 then begin
            exit;
        end;
        arraycopyPrimitives(block, bpos, dst, offset, reading);
        inc(bpos, reading);
        inc(offset, reading);
        inc(result, reading);
        dec(remaining, reading);
        blockPosition := short(bpos);
        if (bpos >= bsiz) and
                ((writeFile() = false) or (readFile(filePosition + bsiz) = false)) then begin
            raise IOException.create(msgFileFailedRead);
        end;
    end;
end;

function FileStream.write(value: int): boolean;
var
    bpos: int;
    fpos: long;
begin
    checkOpenError();
    bpos := blockPosition;
    block[bpos] := byte(value);
    inc(bpos);
    blockRewrited := true;
    blockPosition := short(bpos);
    fpos := filePosition + bpos;
    if blockSize < bpos then begin
        blockSize := short(bpos);
    end;
    if fileSize < fpos then begin
        fileSize := fpos;
    end;
    if (bpos >= FileStream.DEFAULT_BLOCK_SIZE) and
            ((writeFile() = false) or (readFile(fpos) = false)) then begin
        raise IOException.create(msgFileFailedWrite);
    end;
    result := true;
end;

function FileStream.write(const src: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    bpos: int;
    writing: int;
    remaining: int;
    fpos: long;
    block: byte_Array1d;
begin
    checkOpenError();
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    remaining := length;
    block := self.block;
    while remaining > 0 do begin
        bpos := blockPosition;
        writing := Math.min(FileStream.DEFAULT_BLOCK_SIZE - bpos, remaining);
        if writing = 0 then begin
            exit;
        end;
        arraycopyPrimitives(src, offset, block, bpos, writing);
        inc(bpos, writing);
        inc(offset, writing);
        inc(result, writing);
        dec(remaining, writing);
        blockRewrited := true;
        blockPosition := short(bpos);
        fpos := filePosition + bpos;
        if blockSize < bpos then begin
            blockSize := short(bpos);
        end;
        if fileSize < fpos then begin
            fileSize := fpos;
        end;
        if (bpos >= FileStream.DEFAULT_BLOCK_SIZE) and
                ((writeFile() = false) or (readFile(fpos) = false)) then begin
            raise IOException.create(msgFileFailedWrite);
        end;
    end;
end;

procedure FileStream.truncate();
var
    bpos: int;
    fpos: long;
begin
    checkOpenError();
    bpos := blockPosition;
    fpos := filePosition + bpos;
    blockSize := short(bpos);
    fileSize := fpos;
    osintfFileSetPos(fileID, fpos);
    osintfFileSetSize(fileID);
end;

function FileStream.hasOpenError(): boolean;
begin
    result := (error <> 0) or (closed <> false);
end;

procedure FileStream.checkOpenError();
begin
    case error of
    FileStream.ERROR_NO_ERROR: begin
        if closed then begin
            raise IOException.create(msgFileAlreadyClosed);
        end;
    end;
    FileStream.ERROR_OPEN_ERROR: begin
        raise IOException.create(msgFileOpenError);
    end;
    FileStream.ERROR_FILE_NAME_NOT_SPECIFIED: begin
        raise IOException.create(msgFileNameNotSpecified);
    end;
    end;
end;

function FileStream.readFile(position: long): boolean;
var
    must: int;
    readed: int;
begin
    blockPosition := 0;
    if filePosition = position then begin
        result := true;
        exit;
    end;
    position := osintfFileSetPos(fileID, position);
    must := int(Math.min(fileSize - position, FileStream.DEFAULT_BLOCK_SIZE));
    if must > 0 then begin
        readed := osintfFileRead(fileID, @(block[0]), must);
    end else begin
        readed := 0;
    end;
    blockSize := short(readed);
    blockRewrited := false;
    filePosition := position;
    result := readed >= must;
end;

function FileStream.writeFile(): boolean;
var
    bsiz: int;
begin
    bsiz := blockSize;
    if (blockRewrited = false) or (bsiz <= 0) then begin
        result := true;
        exit;
    end;
    blockRewrited := false;
    osintfFileSetPos(fileID, filePosition);
    result := osintfFileWrite(fileID, @(block[0]), bsiz) >= bsiz;
end;

{ LocalFileSystem }

class function LocalFileSystem.getInstance(): VirtualFileSystemReadWrite;
begin
    result := INSTANCE;
end;

class procedure LocalFileSystem.clinit();
begin
    INSTANCE := LocalFileSystem.create();
end;

class procedure LocalFileSystem.cldone();
begin
    INSTANCE := nil;
end;

class procedure LocalFileSystem.timeToOSIntf(const internal: long;
        out osintf: OSIntfDateTimeRecord);
var
    millis: int;
begin
    millis := internal.shorts[0] and $ffff;
    osintf.year := internal.shorts[3] and $ffff;
    osintf.month := internal.bytes[5];
    osintf.day := internal.bytes[4];
    osintf.reserved := 0;
    osintf.hour := internal.bytes[3];
    osintf.minute := internal.bytes[2];
    osintf.second := millis div 1000;
    osintf.millis := millis mod 1000;
end;

class function LocalFileSystem.timeToInternal(const osintf: OSIntfDateTimeRecord): long;
begin
    result := longBuild(osintf.year, (osintf.month shl 8) + (osintf.day and $ff),
            (osintf.hour shl 8) + (osintf.minute and $ff), osintf.second * 1000 + osintf.millis);
end;

constructor LocalFileSystem.create();
begin
    inherited create();
end;

procedure LocalFileSystem.readAttributes(const fileOrDirName: UnicodeString;
        attrDst: FileAttributes);
var
    attributes: int;
    creationTime: OSIntfDateTimeRecord;
    lastWriteTime: OSIntfDateTimeRecord;
    lastAccessTime: OSIntfDateTimeRecord;
begin
    if attrDst = nil then begin
        raise NullPointerException.create(msgNullPointer);
    end;
    if osintfFileReadAttributes(osintfFileNameToOS(fileOrDirName), attributes,
            creationTime, lastWriteTime, lastAccessTime) = false then begin
        raise IOException.create(msgFileFailedReadAttributes);
    end;
    attrDst.assignAttributes(attributes, timeToInternal(creationTime),
            timeToInternal(lastWriteTime), timeToInternal(lastAccessTime));
end;

function LocalFileSystem.fileNameCorrect(const fileOrDirName: UnicodeString): boolean;
begin
    result := osintfFileNameValid(osintfFileNameToOS(fileOrDirName));
end;

function LocalFileSystem.fileNameCaseSensitive(): boolean;
begin
    result := osintfFileNameCaseSensitive();
end;

function LocalFileSystem.fileNameMaximumLength(): int;
begin
    result := osintfFileNameMaximumLength();
end;

function LocalFileSystem.findFirst(): FileEnumerator;
var
    findInfo: OSIntfFileFindInfo;
begin
    if osintfFileFindFirst(osintfFileNameToOS('/'), findInfo) = false then begin
        result := nil;
        exit;
    end;
    result := OSFileEnumerator.create(findInfo);
end;

function LocalFileSystem.findFirst(const pathOrFileName: UnicodeString): FileEnumerator;
var
    findInfo: OSIntfFileFindInfo;
begin
    if osintfFileFindFirst(osintfFileNameToOS(pathOrFileName), findInfo) = false then begin
        result := nil;
        exit;
    end;
    result := OSFileEnumerator.create(findInfo);
end;

function LocalFileSystem.openFileForRead(const fileName: UnicodeString): InputStream;
begin
    result := FileInputStream.create(osintfFileNameToOS(fileName));
    try
        FileInputStream(result).checkOpenError();
    except
        result.free();
        raise;
    end;
end;

procedure LocalFileSystem.writeAttributes(const fileOrDirName: UnicodeString;
        attrSrc: FileAttributes);
var
    attributes: int;
    creationTime: OSIntfDateTimeRecord;
    lastWriteTime: OSIntfDateTimeRecord;
    lastAccessTime: OSIntfDateTimeRecord;
begin
    if attrSrc = nil then begin
        raise NullPointerException.create(msgNullPointer);
    end;
    attributes := attrSrc.flags;
    timeToOSIntf(attrSrc.creationTime, creationTime);
    timeToOSIntf(attrSrc.lastWriteTime, lastWriteTime);
    timeToOSIntf(attrSrc.lastAccessTime, lastAccessTime);
    if osintfFileWriteAttributes(osintfFileNameToOS(fileOrDirName), attributes,
            creationTime, lastWriteTime, lastAccessTime) = false then begin
        raise IOException.create(msgFileFailedWriteAttributes);
    end;
end;

procedure LocalFileSystem.move(const oldFileOrDirName, newFileOrDirName: UnicodeString);
begin
    if osintfFileMove(osintfFileNameToOS(oldFileOrDirName),
            osintfFileNameToOS(newFileOrDirName)) = false then begin
        raise IOException.create(msgFileFailedMoveOrRename);
    end;
end;

procedure LocalFileSystem.deleteDirectory(const dirName: UnicodeString);
begin
    if osintfDirectoryDelete(osintfFileNameToOS(dirName)) = false then begin
        raise IOException.create(msgDirectoryFailedDelete);
    end;
end;

procedure LocalFileSystem.createDirectory(const dirName: UnicodeString);
begin
    if osintfDirectoryDelete(osintfFileNameToOS(dirName)) = false then begin
        raise IOException.create(msgDirectoryFailedCreate);
    end;
end;

procedure LocalFileSystem.deleteFile(const fileName: UnicodeString);
begin
    if osintfFileDelete(osintfFileNameToOS(fileName)) = false then begin
        raise IOException.create(msgFileFailedDelete);
    end;
end;

function LocalFileSystem.createFile(const fileName: UnicodeString): OutputStream;
begin
    result := FileOutputStream.create(osintfFileNameToOS(fileName), false);
    try
        FileOutputStream(result).checkOpenError();
    except
        result.free();
        raise;
    end;
end;

function LocalFileSystem.openFileForAppending(const fileName: UnicodeString): OutputStream;
begin
    result := FileOutputStream.create(osintfFileNameToOS(fileName), true);
    try
        FileOutputStream(result).checkOpenError();
    except
        result.free();
        raise;
    end;
end;

function LocalFileSystem.openFileForReadWrite(const fileName: UnicodeString): InputOutputStream;
begin
    result := FileStream.create(osintfFileNameToOS(fileName));
    try
        FileStream(result).checkOpenError();
    except
        result.free();
        raise;
    end;
end;

{ OSFileEnumerator }

constructor OSFileEnumerator.create(const findInfo: OSIntfFileFindInfo);
begin
    inherited create();
    self.findInfo := findInfo;
    assignAttributes();
end;

procedure OSFileEnumerator.close();
begin
    osintfFileFindClose(findInfo);
end;

function OSFileEnumerator.findNext(): boolean;
begin
    if osintfFileFindNext(findInfo) = false then begin
        result := false;
        exit;
    end;
    assignAttributes();
    result := true;
end;

procedure OSFileEnumerator.assignAttributes();
begin
    with findInfo do begin
        assignAttributes(attributes,
                LocalFileSystem.timeToInternal(creationTime),
                LocalFileSystem.timeToInternal(lastWriteTime),
                LocalFileSystem.timeToInternal(lastAccessTime),
                size, osintfFileNameToVFS(name));
    end;
end;

initialization
    LocalFileSystem.clinit();

finalization
    LocalFileSystem.cldone();

end.