diff --git a/UnixCompat/UnixCompat.cpp b/UnixCompat/UnixCompat.cpp new file mode 100644 index 0000000..d34505b --- /dev/null +++ b/UnixCompat/UnixCompat.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +#include + +void ScanDirectoryForExtension +( + std::vector &outPaths, + const char *path, + const char *ending, + bool recursive +) { + DIR *dir = opendir(path); + if (!dir) + { + return; + } + + size_t endingLen = strlen(ending); + + dirent *ent; + while ((ent = readdir(dir))) + { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + continue; + else if (recursive && ent->d_type == DT_DIR) + { + std::string tmpPath(path); + tmpPath.append("/"); + tmpPath.append(ent->d_name); + ScanDirectoryForExtension( + outPaths, + tmpPath.c_str(), + ending, + recursive + ); + } + else + { + size_t nameLen = strlen(ent->d_name); + + if (endingLen <= nameLen && memcmp + ( + ent->d_name + nameLen - endingLen, + ending, + endingLen + ) == 0 + ) { + std::string tmpPath(path); + tmpPath.append("/"); + tmpPath.append(ent->d_name); + outPaths.push_back(std::move(tmpPath)); + } + } + } + closedir(dir); +} diff --git a/UnixCompat/UnixCompat.h b/UnixCompat/UnixCompat.h new file mode 100644 index 0000000..0fa122d --- /dev/null +++ b/UnixCompat/UnixCompat.h @@ -0,0 +1,100 @@ +#pragma once + +// This header provides POSIX-compatible versions of some functions from +// WindowsUnicodeToolShim and the Windows API. + +#include +#include +#include +#include + +#include + +// Linux and macOS (and probably other non-Windows systems in general) do not +// have separate file system or command line APIs for different text encodings. +// On these systems UTF-8 is commonly the system encoding and, if so, argv will +// typically be UTF-8 encoded, though it is the calling process's responsibility +// to ensure this, as argv is passed verbatim as a sequence of byte strings. +// +// Filesystems on Unix-like systems are typically encoding-unaware, in which +// case the actual encoding used should match the system encoding, or else +// manual intervention is required. +// +// On macOS, HFS+ and APFS filenames are encoded as UTF-16 and UTF-8 +// respectively, and the C filesystem API accepts UTF-8 strings. There are some +// gotchas relating to Unicode normalization, where HFS+ enforces a specific +// form of normalization at the filesystem layer but APFS does not, and using +// the C API to access the filesystem may avoid automatic normalization that +// higher-level macOS API functions may perform. +// +// In summary, text encoding is still a hairy problem on every computer system, +// though on the major non-Windows systems, assuming UTF-8 encoding is +// reasonable. For now, this header simply maps the WindowsUnicodeToolShim +// functions to their regular C API counterparts. +#define toolMain main +#define fopen_utf8 fopen +#define fputs_utf8 fputs + +typedef int errno_t; + +inline int mkdir_utf8(const char *path) +{ + return mkdir(path, 0777); +} + +// These use long as the offset type, which is 64 bits on 64-bit Linux and macOS +// systems, so explicitly 64-bit variants aren't necessary. 32-bit systems are +// likely to have a 32-bit long type, but we aren't likely to deal with +// multi-gigabyte files, are we? +#define _fseeki64 fseek +#define _ftelli64 ftell + +// On Windows this is supposed to call an error handler if the buffer is too +// small. I don't think this is likely to happen in Aerofoil's case, but if it +// does, this function will crash the program as a precaution. +template int sprintf_s +( + char (&buffer)[size], + const char *format, + ... +) { + va_list ap; + + va_start(ap, format); + int count = vsnprintf(buffer, size, format, ap); + va_end(ap); + + if (count >= size) + exit(1); + + return count; +} + +inline errno_t fopen_s(FILE **pFile, const char *name, const char *mode) +{ + FILE *f = fopen(name, mode); + if (!f) + { + // Error codes might not match those from Windows, but all are + // non-zero, so *detecting* an error will work fine. + return errno; + } + *pFile = f; + return 0; +} + +inline void TerminateDirectoryPath(std::string &path) +{ + const size_t length = path.length(); + + if (length == 0 || path[length - 1] != '/') + path.push_back('/'); +} + +void ScanDirectoryForExtension +( + std::vector &outPaths, + const char *path, + const char *ending, + bool recursive +);