Initial commit
Some checks failed
CI / build-and-analyze (push) Failing after 33s

This commit is contained in:
2026-07-26 09:37:43 +03:00
commit 13a4452758
13 changed files with 965 additions and 0 deletions

79
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: CI
on:
push:
branches: [trunk]
pull_request:
branches: [trunk]
jobs:
build-and-analyze:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install tools
run: |
sudo apt-get update -qq
sudo apt-get install -y build-essential clang clang-tools clang-tidy cppcheck
- name: Build with GCC
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=gcc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with Clang
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=clang CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Linting checks
run: |
echo "Running all linting checks..."
FAILED=0
# cppcheck
echo "=== Check 1/4: cppcheck ==="
if make -f Makefile.lint cppcheck; then
echo "cppcheck: PASSED"
else
echo "cppcheck: FAILED"
FAILED=1
fi
# clang-tidy
echo "=== Check 2/4: clang-tidy ==="
if make -f Makefile.lint clang-tidy; then
echo "clang-tidy: PASSED"
else
echo "clang-tidy: FAILED"
FAILED=1
fi
# scan-build
echo "=== Check 3/4: scan-build ==="
if make -f Makefile.lint scan-build; then
echo "scan-build: PASSED"
else
echo "scan-build: FAILED"
FAILED=1
fi
# security-check
echo "=== Check 4/4: security-check ==="
if make -f Makefile.lint security-check; then
echo "security-check: PASSED"
else
echo "security-check: FAILED"
FAILED=1
fi
# Final result
if [ $FAILED -ne 0 ]; then
echo "One or more linting checks failed."
exit 1
else
echo "All linting checks passed."
fi

68
.gitignore vendored Normal file
View File

@@ -0,0 +1,68 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json

39
CMakeLists.txt Normal file
View File

@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.10)
project(CgeFile LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(SOURCES)
set(SOURCES_WIN32
src/win32/File.c
)
set(SOURCES_POSIX
src/posix/File.c
)
set(HEADERS
CgeFile.h
)
if(WIN32)
message(STATUS "Platform - Win32")
list(APPEND SOURCES ${SOURCES_WIN32})
elseif(UNIX)
message(STATUS "Platform: Unix (Linux/BSD/MacOS X)")
list(APPEND SOURCES ${SOURCES_POSIX})
endif()
add_library(CgeFile STATIC ${SOURCES} ${HEADERS})
target_include_directories(CgeFile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS CgeFile
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES ${HEADERS} DESTINATION include)

62
CgeFile.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef CGE_FILE_H
#define CGE_FILE_H
#include <stdint.h>
#include <stddef.h>
enum CgeFileMode {
CGE_FILE_READ = (1 << 0),
CGE_FILE_WRITE = (1 << 1),
CGE_FILE_READWRITE = (1 << 0) | (1 << 1),
CGE_FILE_APPEND = (1 << 4),
CGE_FILE_TRUNCATE = (1 << 5),
CGE_FILE_CREATE = (1 << 6),
CGE_FILE_EXIST = (1 << 7),
};
enum CgeFileSeek {
CGE_FILE_SET,
CGE_FILE_CUR,
CGE_FILE_END,
};
enum CgeFileFlags {
CGE_FILE_OK = (1 << 0),
CGE_FILE_ERROR = (1 << 1),
CGE_FILE_EOF = (1 << 2),
};
enum CgeFileCode {
CGE_FILE_EOK, /* Success */
CGE_FILE_EUNKNOWN, /* Unknown or unexpected error */
CGE_FILE_EOOM, /* Out of memory */
CGE_FILE_ENOSPC, /* No space left on device */
CGE_FILE_EINVAL, /* Invalid mode/flag combination */
CGE_FILE_ENOENT, /* No such file or directory */
CGE_FILE_EACCES, /* Permission denied */
CGE_FILE_EROFS, /* Read-only file system */
CGE_FILE_EEXIST, /* File already exists */
CGE_FILE_EISDIR, /* Is a directory */
CGE_FILE_EMFILE, /* Too many open files */
CGE_FILE_ENAMETOOLONG /* File name too long */
};
typedef struct CgeFile CgeFile;
CgeFile *CgeFileNew(const char *path, int mode, int *result);
void CgeFileFree(CgeFile *file);
int CgeFileRead(CgeFile *file, char *buffer, size_t size, size_t *actual);
int CgeFileWrite(CgeFile *file, const char *buffer, size_t size,
size_t *actual);
int CgeFilePeek(CgeFile *file, char *buffer, size_t size, size_t *actual);
int CgeFileTell(CgeFile *file, int64_t *offset);
int CgeFileSeek(CgeFile *file, int64_t offset, int whence);
int CgeFileFlush(CgeFile *file);
int CgeFileSize(CgeFile *file, int64_t *size);
int CgeFileFlags(CgeFile *file, int *flags);
int CgeFileError(CgeFile *file);
int CgeFileEndOfFile(CgeFile *file);
int CgeFileClear(CgeFile *file);
int CgeFileErrorCode(CgeFile *file);
#endif /* CGE_FILE_H */

31
File.c Normal file
View File

@@ -0,0 +1,31 @@
#include "CgeFile.h"
int CgeFileError(CgeFile *file) {
int flags;
CgeFileFlags(file, &flags);
return flags & CGE_FILE_ERROR;
}
int CgeFileEndOfFile(CgeFile *file) {
int flags;
CgeFileFlags(file, &flags);
return flags & CGE_FILE_EOF;
}
int CgeFilePeek(CgeFile *file, char *buffer, size_t size, size_t *actual) {
int64_t pos;
size_t n = 0;
if (CgeFileError(file) || CgeFileEndOfFile(file))
return 0;
if (!CgeFileTell(file, &pos) ||
!CgeFileRead(file, buffer, size, &n) ||
!CgeFileSeek(file, pos, CGE_FILE_SET))
return 0;
if (actual) *actual = n;
return 1;
}

12
LICENSE Normal file
View File

@@ -0,0 +1,12 @@
Copyright (C) 2026 by blankhex me@blankhex.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

29
Makefile.lint Normal file
View File

@@ -0,0 +1,29 @@
# Makefile.lint - Individual linting targets
.PHONY: all
all: cppcheck clang-tidy scan-build security-check
@echo "All linting scheduled"
.PHONY: cppcheck
cppcheck:
@echo "Running cppcheck..."
@cppcheck --enable=warning,performance,portability --std=c99 --quiet -Iinclude -I. .
.PHONY: clang-tidy
clang-tidy:
@echo "Running clang-tidy..."
@for file in $$(find . -name "*.c" -type f); do \
clang-tidy "$$file" -- -I. -Iinclude -std=c99; \
done
.PHONY: scan-build
scan-build:
@echo "Running scan-build..."
@scan-build --status-bugs make -f Makefile.posix clean all CC=clang
.PHONY: security-check
security-check:
@echo "Scanning for unsafe C functions..."
@grep -rnE '\b(strcpy|strcat|sprintf|gets|scanf|sscanf|realpath|mktemp|tempnam|tmpnam|getwd|getlogin)\b' \
. --include="*.c" --include="*.h" | grep -v "^Binary file" && \
exit 1 || exit 0

23
Makefile.mingw Normal file
View File

@@ -0,0 +1,23 @@
# MinGW Makefile for CgeFile
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra
ARFLAGS = rcs
TARGET = libCgeFile.a
SOURCES = win32/File.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeFile.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
del $(OBJECTS) $(TARGET) 2>nul || exit 0

33
Makefile.posix Normal file
View File

@@ -0,0 +1,33 @@
# POSIX Makefile for CgeFile
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra -fPIC
ARFLAGS = rcs
TARGET = libCgeFile.a
SOURCES = posix/File.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean install
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeFile.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/lib/
cp CgeFile.h /usr/local/include/
ldconfig || echo "Run ldconfig manually if needed"
uninstall:
rm -f /usr/local/lib/libCgeFile.a
rm -f /usr/local/include/CgeFile.h
ldconfig || true

23
Makefile.win32 Normal file
View File

@@ -0,0 +1,23 @@
# Makefile.win32 for MSVC (NMake)
# Usage: Open "x86 Native Tools Command Prompt", then:
# nmake -f Makefile.win32
CC = cl
LIB = lib
CFLAGS = /c /nologo /W3 /O2
LIBFLAGS = /nologo
TARGET = CgeFile.lib
SOURCES = win32/File.c
OBJECTS = $(SOURCES:.c=.obj)
$(TARGET): $(OBJECTS)
$(LIB) $(LIBFLAGS) /OUT:$(TARGET) $(OBJECTS)
{.}.c{}.obj:
$(CC) $(CFLAGS) /Fo$@ $<
clean:
del $(OBJECTS) $(TARGET) 2>nul
.PHONY: clean

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# CgeFile - File Access Library for C
A lightweight, dependency-free C library providing a consistent interface for
file operations across different operating systems. The library abstracts
platform-specific details and offers a unified API for reading, writing, and
manipulating files
## Features
- Cross-platform compatibility (Linux, macOS, Windows, POSIX systems)
- Multiple file modes (read, write, read/write, append, truncate, create,
existence checking)
- Flexible seeking (relative, absolute, end-based positioning)
- Comprehensive error handling with detailed error codes
- File metadata retrieval (size, current position, flags)
- Peek functionality (read data without advancing the file pointer)
- Buffering and flushing (control data persistence)
- EOF detection and error clearing
## Usage Example
```c
#include "CgeFile.h"
#include <stdio.h>
#include <string.h>
int main(void) {
int result;
CgeFile *file = CgeFileNew("example.txt", CGE_FILE_READWRITE | CGE_FILE_CREATE, &result);
if (!file) {
fprintf(stderr, "Failed to open file: error code %d\n", result);
return 1;
}
const char *data = "Hello, CgeFile!";
size_t written;
if (!CgeFileWrite(file, data, strlen(data), &written)) {
fprintf(stderr, "Write failed\n");
CgeFileFree(file);
return 1;
}
char buffer[100];
size_t actual;
if (CgeFileSeek(file, 0, CGE_FILE_SET) &&
CgeFileRead(file, buffer, sizeof(buffer) - 1, &actual)) {
buffer[actual] = '\0';
printf("Read: %s\n", buffer);
}
CgeFileFree(file);
return 0;
}
```
## Build Systems
- **CMake**: supports Linux, macOS, Windows (MSVC, MinGW)
- **Makefile.posix**: for GCC/Clang on POSIX systems
- **Makefile.mingw**: for MinGW on Windows
- **Makefile.win32**: for MSVC with NMake
Builds a static library. No shared library or external dependencies
## Portability
- **Standard compliance**: written in C89 + stdint.h
- **No dynamic memory allocation**: all memory management is explicit
- **No external dependencies**: relies only on standard C libraries
## License
0BSD - a permissive license with no attribution required.

251
posix/File.c Normal file
View File

@@ -0,0 +1,251 @@
#define _FILE_OFFSET_BITS 64
#include "../CgeFile.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static const int64_t off_t_max =
(sizeof(off_t) == 8) ? INT64_MAX :
(sizeof(off_t) == 4) ? INT32_MAX : 0;
static const int64_t off_t_min =
(sizeof(off_t) == 8) ? INT64_MIN :
(sizeof(off_t) == 4) ? INT32_MIN : 0;
struct CgeFile {
int flags;
int handle;
int lastError;
};
static int errnoToFileCode(int err) {
switch (err) {
case 0:
return CGE_FILE_EOK;
case ENOMEM:
return CGE_FILE_EOOM;
case ENOSPC:
return CGE_FILE_ENOSPC;
case EINVAL:
return CGE_FILE_EINVAL;
case ENOENT:
return CGE_FILE_ENOENT;
case EACCES:
case EPERM:
return CGE_FILE_EACCES;
case EROFS:
return CGE_FILE_EROFS;
case EEXIST:
return CGE_FILE_EEXIST;
case EISDIR:
return CGE_FILE_EISDIR;
case EMFILE:
case ENFILE:
return CGE_FILE_EMFILE;
case ENAMETOOLONG:
return CGE_FILE_ENAMETOOLONG;
default:
return CGE_FILE_EUNKNOWN;
}
}
static int fileOpenFlags(int mode) {
int flags = 0;
if ((mode & CGE_FILE_READWRITE) == CGE_FILE_READWRITE)
flags |= O_RDWR;
else if (mode & CGE_FILE_WRITE)
flags |= O_WRONLY;
else if (mode & CGE_FILE_READ)
flags |= O_RDONLY;
else
return -1;
if (!(mode & CGE_FILE_EXIST))
{
flags |= O_CREAT;
if (mode & CGE_FILE_CREATE)
flags |= O_EXCL;
}
if (mode & CGE_FILE_APPEND)
flags |= O_APPEND;
if (mode & CGE_FILE_TRUNCATE)
flags |= O_TRUNC;
return flags;
}
static int fileInit(CgeFile *file, const char *path, int mode) {
static const mode_t openMode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
int flags;
if (!path)
return CGE_FILE_EINVAL;
flags = fileOpenFlags(mode);
if (flags == -1)
return CGE_FILE_EINVAL;
file->flags = 0;
file->handle = open(path, flags, openMode);
if (file->handle == -1)
return errnoToFileCode(errno);
return CGE_FILE_EOK;
}
CgeFile *CgeFileNew(const char *path, int mode, int *result) {
CgeFile *file;
int code = 0;
if ((file = malloc(sizeof(*file)))) {
if ((code = fileInit(file, path, mode))) {
free(file);
file = NULL;
}
}
if (result) *result = code;
return file;
}
void CgeFileFree(CgeFile *file) {
close(file->handle);
free(file);
}
int CgeFileRead(CgeFile *file, char *buffer, size_t size, size_t *actual) {
ssize_t readed;
if (file->flags & CGE_FILE_ERROR)
return 0;
readed = read(file->handle, buffer, size);
if (readed < 0)
goto error;
if (readed > 0) file->flags &= ~CGE_FILE_EOF;
else file->flags |= CGE_FILE_EOF;
if (actual) *actual = readed;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileWrite(CgeFile *file, const char *buffer, size_t size,
size_t *actual) {
ssize_t written;
if (file->flags & CGE_FILE_ERROR)
return 0;
written = write(file->handle, buffer, size);
if (written < 0)
goto error;
if (actual) *actual = written;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileTell(CgeFile *file, int64_t *offset) {
if (file->flags & CGE_FILE_ERROR)
return 0;
if ((*offset = lseek(file->handle, 0, SEEK_CUR)) == -1)
goto error;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileSeek(CgeFile *file, int64_t offset, int whence) {
if (file->flags & CGE_FILE_ERROR)
return 0;
if (offset < off_t_min || offset > off_t_max) {
file->flags |= CGE_FILE_ERROR;
file->lastError = CGE_FILE_EINVAL;
return 0;
}
if (lseek(file->handle, offset, whence) == -1)
goto error;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileFlush(CgeFile *file) {
if (fsync(file->handle))
goto error;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileSize(CgeFile *file, int64_t *size) {
struct stat sb;
if (fstat(file->handle, &sb))
goto error;
*size = sb.st_size;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errnoToFileCode(errno);
return 0;
}
int CgeFileFlags(CgeFile *file, int *flags) {
*flags = file->flags;
return 1;
}
int CgeFileClear(CgeFile *file) {
file->flags &= ~CGE_FILE_ERROR;
return 1;
}
int CgeFileErrorCode(CgeFile *file) {
return file->lastError;
}

242
win32/File.c Normal file
View File

@@ -0,0 +1,242 @@
#include "../CgeFile.h"
#include <windows.h>
struct CgeFile {
HANDLE handle;
int flags;
int mode;
int lastError;
};
static int errorCodeFromError(void) {
DWORD error;
error = GetLastError();
switch (error) {
case ERROR_NOT_ENOUGH_MEMORY:
return CGE_FILE_EOOM;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
return CGE_FILE_ENOENT;
case ERROR_ACCESS_DENIED:
return CGE_FILE_EACCES;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
return CGE_FILE_EEXIST;
case ERROR_DIRECTORY:
return CGE_FILE_EISDIR;
case ERROR_WRITE_PROTECT:
return CGE_FILE_EROFS;
case ERROR_TOO_MANY_OPEN_FILES:
return CGE_FILE_EMFILE;
case ERROR_INVALID_NAME:
case ERROR_FILENAME_EXCED_RANGE:
return CGE_FILE_ENAMETOOLONG;
case ERROR_DISK_FULL:
case ERROR_HANDLE_DISK_FULL:
return CGE_FILE_ENOSPC;
default:
return CGE_FILE_EUNKNOWN;
}
}
static int fileInit(CgeFile *file, const char *path, int mode) {
DWORD access = 0, how = 0;
if (!path)
return CGE_FILE_EINVAL;
if (mode & CGE_FILE_READ)
access |= GENERIC_READ;
if (mode & CGE_FILE_WRITE)
access |= GENERIC_WRITE;
if (!access)
return CGE_FILE_EINVAL;
if (mode & CGE_FILE_TRUNCATE) {
switch (mode & (CGE_FILE_CREATE | CGE_FILE_EXIST)) {
case 0: how = CREATE_ALWAYS; break;
case CGE_FILE_CREATE: how = CREATE_NEW; break;
case CGE_FILE_EXIST: how = TRUNCATE_EXISTING; break;
default: return CGE_FILE_EINVAL;
}
} else {
switch (mode & (CGE_FILE_CREATE | CGE_FILE_EXIST)) {
case 0: how = OPEN_ALWAYS; break;
case CGE_FILE_CREATE: how = CREATE_NEW; break;
case CGE_FILE_EXIST: how = OPEN_EXISTING; break;
default: return CGE_FILE_EINVAL;
}
}
file->flags = 0;
file->mode = mode;
file->handle = CreateFileA(path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL);
if (file->handle == INVALID_HANDLE_VALUE)
return errorCodeFromError();
if (mode & CGE_FILE_TRUNCATE)
SetEndOfFile(file->handle);
return CGE_FILE_EOK;
}
CgeFile *CgeFileNew(const char *path, int mode, int *result) {
CgeFile *file;
int code = 0;
if ((file = malloc(sizeof(*file)))) {
if ((code = fileInit(file, path, mode))) {
free(file);
file = NULL;
}
}
if (result) *result = code;
return file;
}
void CgeFileFree(CgeFile *file) {
CloseHandle(file->handle);
free(file);
}
int CgeFileRead(CgeFile *file, char *buffer, size_t size, size_t *actual) {
DWORD readed;
if (file->flags & CGE_FILE_ERROR)
return 0;
if (!ReadFile(file->handle, buffer, (DWORD)size, &readed, NULL))
goto error;
if (!readed) file->flags |= CGE_FILE_EOF;
else file->flags &= ~CGE_FILE_EOF;
if (actual) *actual = readed;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileWrite(CgeFile *file, const char *buffer, size_t size,
size_t *actual) {
DWORD written;
LARGE_INTEGER position;
if (file->flags & CGE_FILE_ERROR)
return 0;
if (file->mode & CGE_FILE_APPEND) {
position.QuadPart = 0;
if (!SetFilePointerEx(file->handle, position, NULL, FILE_END))
goto error;
}
if (!WriteFile(file->handle, buffer, (DWORD)size, &written, NULL))
goto error;
if (actual) *actual = written;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileTell(CgeFile *file, int64_t *offset) {
LARGE_INTEGER dummy, position;
if (file->flags & CGE_FILE_ERROR)
return 0;
dummy.QuadPart = 0;
if (!SetFilePointerEx(file->handle, dummy, &position, FILE_CURRENT))
goto error;
*offset = position.QuadPart;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileSeek(CgeFile *file, int64_t offset, int whence) {
LARGE_INTEGER position;
if (file->flags & CGE_FILE_ERROR)
return 0;
position.QuadPart = offset;
if (!SetFilePointerEx(file->handle, position, NULL, whence))
goto error;
file->flags &= ~CGE_FILE_EOF;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileFlush(CgeFile *file) {
if (!FlushFileBuffers(file->handle))
goto error;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileSize(CgeFile *file, int64_t *size) {
LARGE_INTEGER dummy;
if (file->flags & CGE_FILE_ERROR)
return 0;
if (!GetFileSizeEx(file->handle, &dummy))
goto error;
*size = dummy.QuadPart;
return 1;
error:
file->flags |= CGE_FILE_ERROR;
file->lastError = errorCodeFromError();
return 0;
}
int CgeFileFlags(CgeFile *file, int *flags) {
*flags = file->flags;
return 1;
}
int CgeFileClear(CgeFile *file) {
file->flags &= ~CGE_FILE_ERROR;
return 1;
}
int CgeFileErrorCode(CgeFile *file) {
return file->lastError;
}