From 76424950e373d3b04ac3dd13019151bfba3e8423 Mon Sep 17 00:00:00 2001 From: sumuel Date: Mon, 17 Aug 2026 20:44:55 +0000 Subject: Add the files --- src/common/io.h | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 src/common/io.h (limited to 'src/common/io.h') diff --git a/src/common/io.h b/src/common/io.h new file mode 100644 index 0000000..f7aec0b --- /dev/null +++ b/src/common/io.h @@ -0,0 +1,300 @@ +#pragma once + +#include "common/FFstrbuf.h" +#include "common/FFlist.h" + +#ifdef _WIN32 + #include + #include + #include + #include "common/windows/nt.h" +typedef HANDLE FFNativeFD; + #define FF_INVALID_FD INVALID_HANDLE_VALUE +#else + #include + #include + #include + #include + #include + #include +typedef int FFNativeFD; + #define FF_INVALID_FD (-1) + // procfs's file can be changed between read calls such as /proc/meminfo and /proc/uptime. + // one safe way to read correct data is reading the whole file in a single read syscall + #define PROC_FILE_BUFFSIZ (32 * 1024) +#endif + +#ifdef _WIN32 + #ifndef O_CLOEXEC + #define O_CLOEXEC 0 + #endif + #ifndef O_RDONLY + #define O_RDONLY 0 + #endif + #ifndef O_DIRECTORY + #define O_DIRECTORY 0200000 + #endif + +// Only O_RDONLY is supported +HANDLE openat(HANDLE dfd, const char* fileName, int oflag); +HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory); +#endif + +static inline bool ffIsValidNativeFD(FFNativeFD fd) { +#ifndef _WIN32 + return fd >= 0; +#else + // https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + return fd != INVALID_HANDLE_VALUE && fd != NULL; +#endif +} + +FF_A_NONNULL(1) static inline bool wrapClose(FFNativeFD* pfd) { + assert(pfd); + + if (!ffIsValidNativeFD(*pfd)) { + return false; + } + +#ifndef _WIN32 + close(*pfd); +#else + NtClose(*pfd); +#endif + + return true; +} +#define FF_AUTO_CLOSE_FD FF_A_CLEANUP(wrapClose) + +static inline FFNativeFD FFUnixFD2NativeFD(int unixfd) { +#ifndef _WIN32 + return unixfd; +#else + return (FFNativeFD) _get_osfhandle(unixfd); +#endif +} + +FF_A_NONNULL(3) static inline bool ffWriteFDData(FFNativeFD fd, size_t dataSize, const void* data) { +#ifndef _WIN32 + return write(fd, data, dataSize) != -1; +#else + DWORD written; + return WriteFile(fd, data, (DWORD) dataSize, &written, NULL) && written == dataSize; +#endif +} + +FF_A_NONNULL(2) static inline bool ffWriteFDBuffer(FFNativeFD fd, const FFstrbuf* content) { + return ffWriteFDData(fd, content->length, content->chars); +} + +FF_A_NONNULL(1, 3) bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data); + +FF_A_NONNULL(1, 2) static inline bool ffWriteFileBuffer(const char* fileName, const FFstrbuf* buffer) { + return ffWriteFileData(fileName, buffer->length, buffer->chars); +} + +FF_A_NONNULL(3) static inline ssize_t ffReadFDData(FFNativeFD fd, size_t dataSize, void* data) { +#ifndef _WIN32 + return read(fd, data, dataSize); +#else + DWORD bytesRead; + if (!ReadFile(fd, data, (DWORD) dataSize, &bytesRead, NULL)) { + return -1; + } + + return (ssize_t) bytesRead; +#endif +} + +FF_A_NONNULL(2) bool ffAppendFDBuffer(FFNativeFD fd, FFstrbuf* buffer); + +FF_A_NONNULL(1, 3) static inline ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data) { + FFNativeFD FF_AUTO_CLOSE_FD fd = +#ifndef _WIN32 + open(fileName, O_RDONLY | O_CLOEXEC); +#else + CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + + if (!ffIsValidNativeFD(fd)) { + return -1; + } + + return ffReadFDData(fd, dataSize, data); +} + +FF_A_NONNULL(2, 4) static inline ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data) { + FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC); + if (!ffIsValidNativeFD(fd)) { + return -1; + } + + return ffReadFDData(fd, dataSize, data); +} + +FF_A_NONNULL(1, 2) static inline bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer) { + FFNativeFD FF_AUTO_CLOSE_FD fd = +#ifndef _WIN32 + open(fileName, O_RDONLY | O_CLOEXEC); +#else + CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + + if (!ffIsValidNativeFD(fd)) { + return false; + } + + return ffAppendFDBuffer(fd, buffer); +} + +FF_A_NONNULL(2, 3) static inline bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer) { + FFNativeFD FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC); + if (!ffIsValidNativeFD(fd)) { + return false; + } + + return ffAppendFDBuffer(fd, buffer); +} + +FF_A_NONNULL(2) static inline bool ffReadFDBuffer(FFNativeFD fd, FFstrbuf* buffer) { + ffStrbufClear(buffer); + return ffAppendFDBuffer(fd, buffer); +} + +FF_A_NONNULL(1, 2) static inline bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer) { + ffStrbufClear(buffer); + return ffAppendFileBuffer(fileName, buffer); +} + +FF_A_NONNULL(2, 3) static inline bool ffReadFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer) { + ffStrbufClear(buffer); + return ffAppendFileBufferRelative(dfd, fileName, buffer); +} + +typedef enum FF_A_PACKED FFPathType { + FF_PATHTYPE_FILE = 1 << 0, + FF_PATHTYPE_DIRECTORY = 1 << 1, + FF_PATHTYPE_ANY = FF_PATHTYPE_FILE | FF_PATHTYPE_DIRECTORY, + FF_PATHTYPE_FORCE_UNSIGNED = UINT8_MAX, +} FFPathType; + +FF_A_NONNULL(1) static inline bool ffPathExists(const char* path, FFPathType pathType) { +#ifdef _WIN32 + + wchar_t wPath[MAX_PATH]; + if (!NT_SUCCESS(RtlUTF8ToUnicodeN(wPath, (ULONG) sizeof(wPath), NULL, path, (ULONG) strlen(path) + 1))) { + return false; + } + + DWORD attr = GetFileAttributesW(wPath); + + if (attr == INVALID_FILE_ATTRIBUTES) { + return false; + } + + if (pathType & FF_PATHTYPE_FILE && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { + return true; + } + + if (pathType & FF_PATHTYPE_DIRECTORY && (attr & FILE_ATTRIBUTE_DIRECTORY)) { + return true; + } + +#else + + if (pathType == FF_PATHTYPE_ANY) { + // Zero overhead + return access(path, F_OK) == 0; + } else { + struct stat fileStat; + if (stat(path, &fileStat) != 0) { + return false; + } + + unsigned int mode = fileStat.st_mode & S_IFMT; + + if (pathType & FF_PATHTYPE_FILE && mode != S_IFDIR) { + return true; + } + + if (pathType & FF_PATHTYPE_DIRECTORY && mode == S_IFDIR) { + return true; + } + } + +#endif + + return false; +} + +FF_A_NONNULL(1, 2) bool ffPathExpandEnv(const char* in, FFstrbuf* out); + +#define FF_IO_TERM_RESP_WAIT_MS 100 // #554 + +FF_A_SCANF(3, 4) +FF_A_NONNULL(1, 3) +const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...); + +// Not thread safe! +bool ffSuppressIO(bool suppress); + +static inline void ffUnsuppressIO(bool* suppressed) { + if (!*suppressed) { + return; + } + ffSuppressIO(false); + *suppressed = false; +} + +#define FF_SUPPRESS_IO() bool FF_A_CLEANUP(ffUnsuppressIO) FF_A_UNUSED io_suppressed__ = ffSuppressIO(true) + +void ffListFilesRecursively(const char* path, bool pretty); + +FF_A_NONNULL(1) static inline bool wrapFclose(FILE** pfile) { + assert(pfile); + if (!*pfile) { + return false; + } + fclose(*pfile); + return true; +} +#define FF_AUTO_CLOSE_FILE FF_A_CLEANUP(wrapFclose) + +FF_A_NONNULL(1) +#ifndef _WIN32 +static inline bool wrapClosedir(DIR** pdir) { + assert(pdir); + if (!*pdir) { + return false; + } + closedir(*pdir); + return true; +} +#else +static inline bool wrapClosedir(HANDLE* pdir) { + assert(pdir); + if (!*pdir) { + return false; + } + FindClose(*pdir); + return true; +} +#endif +#define FF_AUTO_CLOSE_DIR FF_A_CLEANUP(wrapClosedir) + +FF_A_NONNULL(1, 2, 3) static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char* fileSubpath, FFstrbuf* result) { + // configDirs is a list of FFstrbufs include the trailing slash + FF_LIST_FOR_EACH (FFstrbuf, dir, *configDirs) { + ffStrbufClear(result); + ffStrbufAppend(result, dir); + ffStrbufAppendS(result, fileSubpath); + if (ffPathExists(result->chars, FF_PATHTYPE_FILE)) { + return true; + } + } + + return false; +} + +FFNativeFD ffGetNullFD(void); +bool ffRemoveFile(const char* fileName); -- cgit v1.2.3