summaryrefslogtreecommitdiffstats
path: root/src/common/io.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/io.h')
-rw-r--r--src/common/io.h300
1 files changed, 300 insertions, 0 deletions
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 <fileapi.h>
+ #include <handleapi.h>
+ #include <io.h>
+ #include "common/windows/nt.h"
+typedef HANDLE FFNativeFD;
+ #define FF_INVALID_FD INVALID_HANDLE_VALUE
+#else
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include <errno.h>
+ #include <limits.h>
+ #include <fcntl.h>
+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);