Files
handleaiuinew/env/LMI/include/kApi/Io/kFile.cpp
T
2024-05-18 23:53:26 +08:00

1112 lines
27 KiB
C++

/**
* @file kFile.cpp
*
* @internal
* Copyright (C) 2004-2021 by LMI Technologies Inc.
* Licensed under the MIT License.
* Redistributed files must retain the above copyright notice.
*/
#define K_PLATFORM
#include <kApi/Io/kFile.h>
#include <kApi/Io/kDirectory.h>
#include <kApi/Io/kPath.h>
#include <kApi/Data/kArray1.h>
#include <kApi/Threads/kThread.h>
kBeginFullClassEx(k, kFile)
kAddPrivateVMethod(kFile, kObject, VRelease)
kAddPrivateVMethod(kFile, kStream, VReadSomeImpl)
kAddPrivateVMethod(kFile, kStream, VWriteImpl)
kAddPrivateVMethod(kFile, kStream, VSeek)
kAddPrivateVMethod(kFile, kStream, VFlush)
kAddPrivateVMethod(kFile, kStream, VFill)
kEndFullClassEx()
kFx(kStatus) xkFile_InitStatic()
{
kApiFileFx handlers = { kNULL };
handlers.open = xkFile_OpenImpl;
handlers.close = xkFile_CloseImpl;
handlers.read = xkFile_ReadImpl;
handlers.write = xkFile_WriteImpl;
handlers.flush = xkFile_FlushImpl;
handlers.seek = xkFile_SeekImpl;
handlers.size = xkFile_SizeImpl;
handlers.exists = xkFile_ExistsImpl;
handlers.copy = xkFile_CopyImpl;
handlers.move = xkFile_MoveImpl;
handlers.del = xkFile_DeleteImpl;
handlers.tempName = xkFile_TempNameImpl;
//respect handlers that have already been installed
if (kApiLib_HasFileHandlers())
{
kCheck(xkOverrideFunctions(&handlers, sizeof(handlers), kApiLib_FileHandlers()));
}
kCheck(kApiLib_SetFileHandlers(&handlers));
return kOK;
}
kFx(kStatus) xkFile_ReleaseStatic()
{
return kOK;
}
kFx(kStatus) kFile_Load(const kChar* path, void* data, kSize* size, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kFile file = kNULL;
kByte* buffer = kNULL;
k64u fileSize;
kStatus exception;
kTry
{
kTest(kFile_Construct(&file, path, kFILE_MODE_READ, kNULL));
if ((fileSize = kFile_Length(file)) >= kSIZE_MAX)
{
kThrow(kERROR_INCOMPLETE);
}
kTest(kAlloc_Get(alloc, (kSize) fileSize, &buffer));
kTest(kStream_Read(file, buffer, (kSize) fileSize));
kTest(kFile_Close(file));
*(void**)data = buffer;
*size = (kSize) fileSize;
}
kCatchEx(&exception)
{
kAlloc_Free(alloc, buffer);
kEndCatchEx(exception);
}
kFinallyEx
{
kCheck(kDestroyRef(&file));
kEndFinallyEx();
}
return kOK;
}
kFx(kStatus) kFile_LoadTo(const kChar* path, void* data, kSize capacity)
{
kFile file = kNULL;
kTry
{
kTest(kFile_Construct(&file, path, kFILE_MODE_READ, kNULL));
kTestTrue(kFile_Length(file) == capacity, kERROR_INCOMPLETE);
kTest(kStream_Read(file, data, capacity));
kTest(kFile_Close(file));
}
kFinally
{
kDestroyRef(&file);
kEndFinally();
}
return kOK;
}
kFx(kStatus) kFile_Save(const kChar* path, const void* data, kSize size)
{
kFile file = kNULL;
kTry
{
kTest(kFile_Construct(&file, path, kFILE_MODE_WRITE, kNULL));
kTest(kStream_Write(file, data, size));
kTest(kFile_Close(file));
}
kFinally
{
kDestroyRef(&file);
kEndFinally();
}
return kOK;
}
kFx(kStatus) kFile_Construct(kFile* file, const kChar* path, kFileMode mode, kAlloc allocator)
{
kAlloc alloc = kAlloc_Fallback(allocator);
kType type = kTypeOf(kFile);
kStatus status;
kCheck(kAlloc_GetObject(alloc, type, file));
if (!kSuccess(status = xkFile_Init(*file, type, path, mode, alloc)))
{
kAlloc_FreeRef(alloc, file);
}
return status;
}
kFx(kStatus) xkFile_Init(kFile file, kType type, const kChar* path, kFileMode mode, kAlloc allocator)
{
kObjR(kFile, file);
kStatus status = kOK;
k64u zero;
kCheck(kStream_Init(file, type, allocator));
obj->streamPosition = 0;
obj->streamLength = 0;
obj->lastMode = xkFILE_MODE_NULL;
xkFile_InitPlatformFields(file);
kTry
{
kTest(kApiLib_FileHandlers()->open(file, path, mode));
if (kIsError(kApiLib_FileHandlers()->seek(file, 0, kSEEK_ORIGIN_END, &obj->streamLength)) ||
kIsError(kApiLib_FileHandlers()->seek(file, 0, kSEEK_ORIGIN_BEGIN, &zero)))
{
//file may not support seek; set the reported length to max, but allow the file to be used
obj->streamLength = k64U_MAX;
}
}
kCatch(&status)
{
xkFile_VRelease(file);
kEndCatch(status);
}
return kOK;
}
kFx(kStatus) xkFile_VRelease(kFile file)
{
kObj(kFile, file);
//ignore errors
kFile_Close(file);
kCheck(kObject_FreeMemRef(file, &obj->base.readBuffer));
kCheck(kObject_FreeMemRef(file, &obj->base.writeBuffer));
kCheck(kStream_VRelease(file));
return kOK;
}
kFx(kStatus) kFile_Close(kFile file)
{
kStatus flushStatus = kOK;
kStatus closeStatus = kOK;
if (xkFile_IsOpen(file))
{
flushStatus = kStream_Flush(file);
closeStatus = kApiLib_FileHandlers()->close(file);
}
return kIsError(flushStatus) ? flushStatus : (kIsError(closeStatus) ? closeStatus : kOK);
}
kFx(kStatus) kFile_SetWriteBuffer(kFile file, kSize size)
{
kObj(kFile, file);
kCheckState(xkFile_IsOpen(file));
kCheck(kStream_Flush(file));
kCheck(kObject_FreeMemRef(file, &obj->base.writeBuffer));
obj->base.writeCapacity = 0;
obj->base.writeBegin = 0;
obj->base.writeEnd = 0;
if (size > 0)
{
kCheck(kObject_GetMem(file, size, &obj->base.writeBuffer));
obj->base.writeCapacity = size;
}
return kOK;
}
kFx(kStatus) kFile_SetReadBuffer(kFile file, kSize size)
{
kObj(kFile, file);
kCheckState(xkFile_IsOpen(file));
kCheck(kStream_Flush(file));
kCheck(kObject_FreeMemRef(file, &obj->base.readBuffer));
obj->base.readCapacity = 0;
obj->base.readBegin = 0;
obj->base.readEnd = 0;
if (size > 0)
{
kCheck(kObject_GetMem(file, size, &obj->base.readBuffer));
obj->base.readCapacity = size;
}
return kOK;
}
kFx(kStatus) xkFile_VReadSomeImpl(kFile file, void* buffer, kSize minCount, kSize maxCount, kSize* bytesRead)
{
kObj(kFile, file);
kByte* dest = (kByte*) buffer;
kSize readCount = 0;
kSize copyCount, mediumCount;
kCheckState(xkFile_IsOpen(file));
kCheckState((kFile_Position(file) + minCount) <= kFile_Length(file));
//configure the stream for reading, if necessary
if (obj->lastMode != kFILE_MODE_READ)
{
kCheck(kStream_Flush(file));
obj->lastMode = kFILE_MODE_READ;
}
//consume any bytes in the read buffer first
if ((obj->base.readEnd - obj->base.readBegin) > 0)
{
copyCount = kMin_(maxCount, obj->base.readEnd - obj->base.readBegin);
kMemCopy(dest, &obj->base.readBuffer[obj->base.readBegin], copyCount);
obj->base.readBegin += copyCount;
readCount += copyCount;
}
//if the request is not yet satisfied
if (readCount < minCount)
{
//if the request is larger than the internal read buffer, read directly into the caller's buffer; else read into the internal buffer
if ((maxCount - readCount) >= obj->base.readCapacity)
{
kCheck(xkFile_ReadAtLeast(file, &dest[readCount], minCount-readCount, maxCount-readCount, &mediumCount));
readCount += mediumCount;
}
else
{
kCheck(xkFile_ReadAtLeast(file, &obj->base.readBuffer[0], minCount - readCount, obj->base.readCapacity, &mediumCount));
copyCount = kMin_(mediumCount, maxCount - readCount);
kCheck(kMemCopy(&dest[readCount], &obj->base.readBuffer[0], copyCount));
obj->base.readBegin = copyCount;
obj->base.readEnd = mediumCount;
readCount += copyCount;
}
}
if (!kIsNull(bytesRead))
{
*bytesRead = readCount;
}
return kOK;
}
kFx(kStatus) xkFile_VWriteImpl(kFile file, const void* buffer, kSize size)
{
kObj(kFile, file);
kCheckState(xkFile_IsOpen(file));
//configure the stream for writing, if necessary
if (obj->lastMode != kFILE_MODE_WRITE)
{
kCheck(kStream_Flush(file));
obj->lastMode = kFILE_MODE_WRITE;
obj->base.writeEnd = obj->base.writeCapacity;
}
//if the internal write buffer already has content, but not enough free space, flush
if ((obj->base.writeBegin > 0) && ((obj->base.writeEnd - obj->base.writeBegin) < size))
{
kCheck(kStream_Flush(file));
}
//if the write is larger than the internal write buffer, write directly to the medium; else write to the internal buffer
if ((obj->base.writeEnd - obj->base.writeBegin) < size)
{
kCheck(xkFile_WriteAll(file, (const kByte*)buffer, size));
}
else
{
kCheck(kMemCopy(&obj->base.writeBuffer[obj->base.writeBegin], buffer, size));
obj->base.writeBegin += size;
}
return kOK;
}
kFx(kStatus) xkFile_VFlush(kFile file)
{
kObj(kFile, file);
kCheckState(xkFile_IsOpen(file));
if ((obj->lastMode == kFILE_MODE_WRITE) && (obj->base.writeBegin > 0))
{
kCheck(xkFile_WriteAll(file, obj->base.writeBuffer, obj->base.writeBegin));
}
else if ((obj->lastMode == kFILE_MODE_READ) && ((obj->base.readEnd - obj->base.readBegin) > 0))
{
obj->base.bytesRead -= (obj->base.readEnd - obj->base.readBegin);
}
obj->base.readBegin = obj->base.readEnd = 0;
obj->base.writeBegin = obj->base.writeEnd = 0;
return kOK;
}
kFx(kStatus) xkFile_VFill(kFile file)
{
kObj(kFile, file);
kSize mediumCount;
kCheckState(xkFile_IsOpen(file));
kCheckState(kFile_Position(file) < kFile_Length(file));
//configure the stream for reading, if necessary
if (obj->lastMode != kFILE_MODE_READ)
{
kCheck(kStream_Flush(file));
obj->lastMode = kFILE_MODE_READ;
}
kCheck(xkFile_ReadAtLeast(file, &obj->base.readBuffer[0], 1, obj->base.readCapacity, &mediumCount));
obj->base.readBegin = 0;
obj->base.readEnd = mediumCount;
return kOK;
}
kFx(kStatus) xkFile_VSeek(kFile file, k64s offset, kSeekOrigin origin)
{
kObj(kFile, file);
kCheckState(xkFile_IsOpen(file));
kCheck(kStream_Flush(file));
kCheck(kApiLib_FileHandlers()->seek(file, offset, origin, &obj->streamPosition));
return kOK;
}
kFx(k64u) kFile_Length(kFile file)
{
kObj(kFile, file);
kAssert(xkFile_IsOpen(file));
if (obj->lastMode != kFILE_MODE_WRITE)
{
return obj->streamLength;
}
else
{
return kMax_(obj->streamLength, obj->streamPosition + obj->base.writeBegin);
}
}
kFx(k64u) kFile_Position(kFile file)
{
kObj(kFile, file);
kAssert(xkFile_IsOpen(file));
if (obj->lastMode == kFILE_MODE_READ)
{
return obj->streamPosition - (obj->base.readEnd - obj->base.readBegin);
}
else if (obj->lastMode == kFILE_MODE_WRITE)
{
return obj->streamPosition + obj->base.writeBegin;
}
else
{
return obj->streamPosition;
}
}
kFx(kBool) kFile_Exists(const kChar* fileName)
{
return kApiLib_FileHandlers()->exists(fileName);
}
kFx(k64u) kFile_Size(const kChar* fileName)
{
return kApiLib_FileHandlers()->size(fileName);
}
kFx(kStatus) kFile_Copy(const kChar* source, const kChar* destination)
{
return kApiLib_FileHandlers()->copy(source, destination, kNULL, kNULL);
}
kFx(kStatus) kFile_CopyEx(const kChar* source, const kChar* destination, kCallbackFx progress, kPointer context)
{
return kApiLib_FileHandlers()->copy(source, destination, progress, context);
}
kFx(kStatus) kFile_Move(const kChar* source, const kChar* destination)
{
return kApiLib_FileHandlers()->move(source, destination, kNULL, kNULL);
}
kFx(kStatus) kFile_MoveEx(const kChar* source, const kChar* destination, kCallbackFx progress, kPointer context)
{
return kApiLib_FileHandlers()->move(source, destination, progress, context);
}
kFx(kStatus) kFile_Delete(const kChar* path)
{
return kApiLib_FileHandlers()->del(path);
}
kFx(kStatus) kFile_TempName(kChar* name, kSize capacity)
{
return kApiLib_FileHandlers()->tempName(name, capacity);
}
kFx(kStatus) xkFile_ReadAtLeast(kFile file, kByte* buffer, kSize minCount, kSize maxCount, kSize* bytesRead)
{
kObj(kFile, file);
kCheck(kApiLib_FileHandlers()->read(file, buffer, minCount, maxCount, bytesRead));
obj->streamPosition += *bytesRead;
obj->base.bytesRead += *bytesRead;
return kOK;
}
kFx(kStatus) xkFile_WriteAll(kFile file, const kByte* buffer, kSize count)
{
kObj(kFile, file);
kCheck(kApiLib_FileHandlers()->write(file, buffer, count));
obj->streamPosition += count;
obj->streamLength = kMax_(obj->streamLength, obj->streamPosition);
obj->base.bytesWritten += count;
return kOK;
}
kFx(k64u) xkFile_SizeImpl(const kChar* path)
{
kFile file = kNULL;
k64u size = 0;
if (kSuccess(kFile_Construct(&file, path, kFILE_MODE_READ, kNULL)))
{
size = kFile_Length(file);
kObject_Destroy(file);
}
return size;
}
kFx(kStatus) xkFile_CopyImpl(const kChar* source, const kChar* destination, kCallbackFx progress, kPointer context)
{
kFile from = kNULL;
kFile to = kNULL;
kArray1 buffer = kNULL;
k64u size = 0;
k64u remaining = 0;
kStatus status = kOK;
k64u updateTime = k64U_NULL;
if (kStrEquals(source, destination))
{
xkUpdateProgress(progress, context, kNULL, kNULL, 100);
return kOK;
}
kTry
{
kTest(kFile_Construct(&from, source, kFILE_MODE_READ, kNULL));
kTest(kFile_Construct(&to, destination, kFILE_MODE_WRITE, kNULL));
kTest(kArray1_Construct(&buffer, kTypeOf(kByte), xkFILE_COPY_BUFFER_SIZE, kNULL));
remaining = size = kFile_Length(from);
while (remaining > 0)
{
kSize segment = (kSize) kMin_(remaining, xkFILE_COPY_BUFFER_SIZE);
kTest(kStream_Read(from, kArray1_DataT(buffer, kByte), segment));
kTest(kStream_Write(to, kArray1_DataT(buffer, kByte), segment));
remaining -= segment;
xkUpdateProgress(progress, context, kNULL, &updateTime, (k32u)(100*(size-remaining)/size));
}
kTest(kFile_Close(from));
kTest(kFile_Close(to));
xkUpdateProgress(progress, context, kNULL, kNULL, 100);
}
kCatchEx(&status)
{
//eliminate any partially-copied remnant (not strictly required, but convenient)
if (!kIsNull(to))
{
kDestroyRef(&to);
kFile_Delete(destination);
}
kEndCatchEx(status);
}
kFinallyEx
{
kCheck(kDestroyRef(&from));
kCheck(kDestroyRef(&to));
kCheck(kObject_Destroy(buffer));
kEndFinallyEx();
}
return kOK;
}
kFx(kStatus) xkFile_MoveImpl(const kChar* source, const kChar* destination, kCallbackFx progress, kPointer context)
{
kCheck(kApiLib_FileHandlers()->copy(source, destination, progress, context));
kCheck(kApiLib_FileHandlers()->del(source));
return kOK;
}
kFx(kStatus) xkFile_TempNameImpl(kChar* name, kSize capacity)
{
return kStrPrintf(name, capacity, "%08X%08X%08X%08X", kRandom32u(),kRandom32u(), kRandom32u(), kRandom32u());
}
#if defined(K_WINDOWS)
kFx(void) xkFile_InitPlatformFields(kFile file)
{
kObj(kFile, file);
obj->handle = INVALID_HANDLE_VALUE;
}
kFx(kBool) xkFile_IsOpen(kFile file)
{
kObj(kFile, file);
return obj->handle != INVALID_HANDLE_VALUE;
}
kFx(kStatus) xkFile_OpenImpl(kFile file, const kChar* path, kFileMode mode)
{
kObj(kFile, file);
DWORD desiredAccess = 0;
DWORD shareMode = 0;
DWORD creationDisposition = 0;
DWORD flags = FILE_ATTRIBUTE_NORMAL;
kChar nativePath[kPATH_MAX];
WCHAR wpath[MAX_PATH];
kCheck(xkPath_FromVirtual(path, nativePath, kCountOf(nativePath)));
kCheck(kPath_ToNative(nativePath, nativePath, kCountOf(nativePath)));
if (MultiByteToWideChar(CP_UTF8, 0, nativePath, -1, wpath, kCountOf(wpath)) == 0)
{
return kERROR_PARAMETER;
}
if (mode == kFILE_MODE_READ)
{
desiredAccess = GENERIC_READ;
shareMode = FILE_SHARE_READ;
creationDisposition = OPEN_EXISTING;
}
else if (mode == kFILE_MODE_WRITE)
{
desiredAccess = GENERIC_WRITE;
creationDisposition = CREATE_ALWAYS;
}
else if (mode == (kFILE_MODE_READ | kFILE_MODE_WRITE))
{
desiredAccess = GENERIC_READ | GENERIC_WRITE;
creationDisposition = CREATE_ALWAYS;
}
else if (mode == (kFILE_MODE_WRITE | kFILE_MODE_UPDATE))
{
desiredAccess = GENERIC_WRITE;
creationDisposition = OPEN_ALWAYS;
}
else if (mode == (kFILE_MODE_READ | kFILE_MODE_WRITE | kFILE_MODE_UPDATE))
{
desiredAccess = GENERIC_READ | GENERIC_WRITE;
creationDisposition = OPEN_ALWAYS;
}
else
{
return kERROR_PARAMETER;
}
if ((obj->handle = CreateFile(wpath, desiredAccess, shareMode, kNULL, creationDisposition, flags, kNULL)) == INVALID_HANDLE_VALUE)
{
switch (GetLastError())
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
return kERROR_NOT_FOUND;
default:
return kERROR_STATE;
}
}
return kOK;
}
kFx(kStatus) xkFile_CloseImpl(kFile file)
{
kObj(kFile, file);
BOOL result = TRUE;
if (xkFile_IsOpen(file))
{
result = CloseHandle(obj->handle);
obj->handle = INVALID_HANDLE_VALUE;
}
return (result) ? kOK : kERROR_STREAM;
}
kFx(kStatus) xkFile_ReadImpl(kFile file, kByte* buffer, kSize minCount, kSize maxCount, kSize* bytesRead)
{
kObj(kFile, file);
DWORD read;
kCheckArgs(maxCount <= xkFILE_MAX_IO_SIZE);
kCheck(ReadFile(obj->handle, buffer, (DWORD)maxCount, &read, kNULL));
kCheck(read >= minCount);
*bytesRead = read;
return kOK;
}
kFx(kStatus) xkFile_WriteImpl(kFile file, const kByte* buffer, kSize count)
{
kObj(kFile, file);
DWORD written;
kCheckArgs(count <= xkFILE_MAX_IO_SIZE);
kCheck(WriteFile(obj->handle, buffer, (DWORD)count, &written, kNULL));
kCheck(written == count);
return kOK;
}
kFx(kStatus) xkFile_FlushImpl(kFile file)
{
kObj(kFile, file);
if (obj->lastMode == kFILE_MODE_WRITE)
{
kCheck(FlushFileBuffers(obj->handle));
}
return kOK;
}
kFx(kStatus) xkFile_SeekImpl(kFile file, k64s offset, kSeekOrigin origin, k64u* position)
{
kObj(kFile, file);
DWORD moveMethod;
LARGE_INTEGER offsetLarge;
LARGE_INTEGER positionLarge;
switch(origin)
{
case kSEEK_ORIGIN_BEGIN: moveMethod = FILE_BEGIN; break;
case kSEEK_ORIGIN_CURRENT: moveMethod = FILE_CURRENT; break;
case kSEEK_ORIGIN_END: moveMethod = FILE_END; break;
default: return 0;
}
offsetLarge.QuadPart = offset;
kCheck((SetFilePointerEx(obj->handle, offsetLarge, &positionLarge, moveMethod) == TRUE) ? kOK : kERROR_STREAM);
*position = (k64u) positionLarge.QuadPart;
return kOK;
}
kFx(kBool) xkFile_ExistsImpl(const kChar* path)
{
kFile file = kNULL;
kBool exists = kFALSE;
//works on Windows (not Linux) -- should we do something else?
if (kSuccess(kFile_Construct(&file, path, kFILE_MODE_READ, kNULL)))
{
exists = kTRUE;
kObject_Destroy(file);
}
return exists;
}
kFx(kStatus) xkFile_DeleteImpl(const kChar* path)
{
kChar absPath[kPATH_MAX];
kChar nativePath[kPATH_MAX];
kChar directory[kPATH_MAX];
WCHAR wdirectory[MAX_PATH];
WCHAR wpath[MAX_PATH];
WCHAR wtempFile[MAX_PATH];
kSize i = 0;
kBool moved = kFALSE;
//confirm that the file doesn't already exist, to prevent needlessly delaying on rename failure (below)
if (!kFile_Exists(path))
{
return kERROR_NOT_FOUND;
}
//we want to get the parent directory (below), but the given path may be relative;
//if so, we convert to an absolute path that is relative to the current working directory
kCheck(xkPath_FromVirtual(path, absPath, kCountOf(absPath)));
kCheck(kPath_ToAbsolute(kNULL, absPath, absPath, sizeof(absPath)));
kCheck(kPath_ToNative(absPath, nativePath, kCountOf(nativePath)));
if (MultiByteToWideChar(CP_UTF8, 0, nativePath, -1, wpath, kCountOf(wpath)) == 0)
{
return kERROR_PARAMETER;
}
kCheck(kPath_Directory(absPath, directory, kCountOf(directory)));
kCheck(kPath_ToNative(directory, directory, kCountOf(directory)));
if (MultiByteToWideChar(CP_UTF8, 0, directory, -1, wdirectory, kCountOf(wdirectory)) == 0)
{
return kERROR_PARAMETER;
}
if (GetTempFileName(wdirectory, L"kFile", 0, wtempFile) == 0)
{
return kERROR_OS;
}
for (i = 0; i < xkFILE_WINDOWS_IO_ATTEMPTS && !moved; i++)
{
if (MoveFileEx(wpath, wtempFile, MOVEFILE_REPLACE_EXISTING) != 0)
{
moved = kTRUE;
}
else
{
kCheck(kThread_Sleep(100000));
}
}
if (!moved)
{
return kERROR_OS;
}
for (i = 0; i < xkFILE_WINDOWS_IO_ATTEMPTS; i++)
{
if (DeleteFile(wtempFile) != 0)
{
return kOK;
}
kCheck(kThread_Sleep(100000));
}
return kERROR_OS;
}
kFx(kStatus) xkFile_TempFileImpl(const kChar* path, kChar* name, kSize capacity)
{
kChar nativePath[kPATH_MAX];
WCHAR wpath[MAX_PATH];
WCHAR tempFile[MAX_PATH];
kCheck(kPath_ToNative(path, nativePath, kCountOf(nativePath)));
if (MultiByteToWideChar(CP_UTF8, 0, nativePath, -1, wpath, kCountOf(wpath)) == 0)
{
return kERROR_PARAMETER;
}
if (GetTempFileName(wpath, L"kFile", 0, tempFile) == 0)
{
return kERROR_OS;
}
return xkPath_NativeWideToNormalizedWin(tempFile, name, capacity);
}
#elif defined(K_DARWIN) || defined (K_LINUX) || defined (K_QNX)
kFx(void) xkFile_InitPlatformFields(kFile file)
{
kObj(kFile, file);
obj->handle = -1;
}
kFx(kBool) xkFile_IsOpen(kFile file)
{
kObj(kFile, file);
return obj->handle != -1;
}
kFx(kStatus) xkFile_OpenImpl(kFile file, const kChar* path, kFileMode mode)
{
kObj(kFile, file);
kChar nativePath[kPATH_MAX];
int oflag = 0;
kCheck(xkPath_FromVirtual(path, nativePath, kCountOf(nativePath)));
kCheck(kPath_ToNative(nativePath, nativePath, kCountOf(nativePath)));
if (mode == kFILE_MODE_READ)
{
oflag = O_RDONLY;
}
else if (mode == kFILE_MODE_WRITE)
{
oflag = O_WRONLY | O_CREAT | O_TRUNC;
}
else if (mode == (kFILE_MODE_READ | kFILE_MODE_WRITE))
{
oflag = O_RDWR | O_CREAT | O_TRUNC;
}
else if (mode == (kFILE_MODE_WRITE | kFILE_MODE_UPDATE))
{
oflag = O_WRONLY | O_CREAT;
}
else if (mode == (kFILE_MODE_READ | kFILE_MODE_WRITE | kFILE_MODE_UPDATE))
{
oflag = O_RDWR | O_CREAT;
}
else
{
return kERROR_PARAMETER;
}
if ((oflag & O_CREAT) != 0)
{
obj->handle = open(nativePath, oflag, S_IRWXU);
}
else
{
obj->handle = open(nativePath, oflag);
}
if (obj->handle == -1)
{
switch (errno)
{
case ENOENT:
case ENOTDIR:
return kERROR_NOT_FOUND;
default:
return kERROR_STATE;
}
}
return kOK;
}
kFx(kStatus) xkFile_CloseImpl(kFile file)
{
kObj(kFile, file);
int result = 0;
if (xkFile_IsOpen(file))
{
result = close(obj->handle);
obj->handle = -1;
}
return (result != -1) ? kOK : kERROR_STREAM;
}
kFx(kStatus) xkFile_ReadImpl(kFile file, kByte* buffer, kSize minCount, kSize maxCount, kSize* bytesRead)
{
kObj(kFile, file);
ssize_t readSize;
if ((readSize = read(obj->handle, buffer, maxCount)) < 0)
{
return kERROR_STREAM;
}
if ((kSize)readSize < minCount)
{
return kERROR_STREAM;
}
*bytesRead = (kSize) readSize;
return kOK;
}
kFx(kStatus) xkFile_WriteImpl(kFile file, const kByte* buffer, kSize count)
{
kObj(kFile, file);
ssize_t writeSize;
if ((writeSize = write(obj->handle, buffer, count)) < 0)
{
return kERROR_STREAM;
}
if ((kSize)writeSize != count)
{
return kERROR_STREAM;
}
return kOK;
}
kFx(kStatus) xkFile_FlushImpl(kFile file)
{
return kOK;
}
kFx(kStatus) xkFile_SeekImpl(kFile file, k64s offset, kSeekOrigin origin, k64u* position)
{
kObj(kFile, file);
off_t updatedPosition;
int whence;
switch(origin)
{
case kSEEK_ORIGIN_BEGIN: whence = SEEK_SET; break;
case kSEEK_ORIGIN_CURRENT: whence = SEEK_CUR; break;
case kSEEK_ORIGIN_END: whence = SEEK_END; break;
default: return 0;
}
if ((updatedPosition = lseek(obj->handle, (off_t)offset, whence)) == -1)
{
return kERROR_STREAM;
}
*position = updatedPosition;
return kOK;
}
kFx(kBool) xkFile_ExistsImpl(const kChar* path)
{
struct stat st;
kChar nativePath[kPATH_MAX];
kCheck(xkPath_FromVirtual(path, nativePath, kCountOf(nativePath)));
return (stat(nativePath, &st) != -1) && S_ISREG(st.st_mode);
}
kFx(kStatus) xkFile_DeleteImpl(const kChar* path)
{
kChar nativePath[kPATH_MAX];
kCheck(xkPath_FromVirtual(path, nativePath, kCountOf(nativePath)));
kCheck(kPath_ToNative(nativePath, nativePath, kCountOf(nativePath)));
return (unlink(nativePath) == 0) ? kOK : kERROR_OS;
}
#else
kFx(void) xkFile_InitPlatformFields(kFile file)
{
kObj(kFile, file);
obj->handle = kNULL;
}
kFx(kBool) xkFile_IsOpen(kFile file)
{
kObj(kFile, file);
return !kIsNull(obj->handle);
}
kFx(kStatus) xkFile_OpenImpl(kFile file, const kChar* path, kFileMode mode)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kStatus) xkFile_CloseImpl(kFile file)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kStatus) xkFile_ReadImpl(kFile file, kByte* buffer, kSize minCount, kSize maxCount, kSize* bytesRead)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kStatus) xkFile_WriteImpl(kFile file, const kByte* buffer, kSize count)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kStatus) xkFile_FlushImpl(kFile file)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kStatus) xkFile_SeekImpl(kFile file, k64s offset, kSeekOrigin origin, k64u* position)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
kFx(kBool) xkFile_ExistsImpl(const kChar* path)
{
kAssert(kFALSE);
return kFALSE;
}
kFx(kStatus) xkFile_DeleteImpl(const kChar* path)
{
kAssert(kFALSE);
return kERROR_UNIMPLEMENTED;
}
#endif