/** * @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 #include #include #include #include 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