1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00
Beef/BeefySysLib/platform/posix/PosixCommon.cpp

2575 lines
64 KiB
C++
Raw Normal View History

#include "Common.h"
#include "BFPlatform.h"
#include <sys/stat.h>
2019-10-29 05:01:04 -07:00
#ifndef BF_PLATFORM_DARWIN
#include <sys/sysinfo.h>
2019-10-29 05:01:04 -07:00
#endif
#include <sys/wait.h>
#include <wchar.h>
#include <fcntl.h>
#include <time.h>
2019-10-29 05:01:04 -07:00
#ifdef BFP_HAS_DLINFO
#include <link.h>
2019-10-29 05:01:04 -07:00
#endif
#include <dirent.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
#include <spawn.h>
#include <dlfcn.h>
#include "../PlatformInterface.h"
#include "../PlatformHelper.h"
2020-08-06 09:24:37 -07:00
#include "../../util/CritSect.h"
#include "../../util/Dictionary.h"
#include "../../util/Hash.h"
#include "../../third_party/putty/wildcard.h"
#ifdef BFP_HAS_EXECINFO
#include <execinfo.h>
#endif
#ifdef BFP_HAS_BACKTRACE
2020-05-21 06:58:26 -07:00
#ifdef BFP_BACKTRACE_PATH
#include BFP_BACKTRACE_PATH
2020-06-10 07:13:48 -07:00
#elif BFP_HAS_BACKTRACE
#include "backtrace.h"
#include "backtrace-supported.h"
2020-05-21 06:58:26 -07:00
#endif
#endif
2021-06-28 10:28:32 -07:00
#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name
2020-08-06 09:24:37 -07:00
#include "../../third_party/stb/stb_sprintf.h"
#include <cxxabi.h>
#include <random>
#ifndef BFP_PRINTF
#define BFP_PRINTF(...) printf (__VA_ARGS__)
#define BFP_ERRPRINTF(...) fprintf (stderr, __VA_ARGS__)
#endif
2020-06-10 07:13:48 -07:00
//#include <cxxabi.h>
//using __cxxabiv1::__cxa_demangle;
USING_NS_BF;
struct BfpPipeInfo
{
String mPipePath;
int mWriteHandle;
};
struct BfpFile
{
BfpPipeInfo* mPipeInfo;
int mHandle;
bool mNonBlocking;
bool mAllowTimeout;
bool mIsStd;
BfpFile()
{
mPipeInfo = NULL;
mHandle = -1;
mNonBlocking = false;
mAllowTimeout = false;
mIsStd = false;
}
BfpFile(int handle)
{
mPipeInfo = NULL;
mHandle = handle;
mNonBlocking = false;
mAllowTimeout = false;
mIsStd = false;
}
~BfpFile()
{
delete mPipeInfo;
}
};
class FileWatchManager;
static FileWatchManager* gFileWatchManager = NULL;
class FileWatchManager
{
public:
virtual bool Init() = 0;
virtual void Shutdown() = 0;
virtual BfpFileWatcher* WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult) = 0;
virtual void Remove(BfpFileWatcher* watcher) = 0;
static FileWatchManager* Get();
};
class NullFilewatchManager : public FileWatchManager
{
virtual bool Init() { return false; }
virtual void Shutdown() {}
virtual BfpFileWatcher* WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult) { NOT_IMPL; return NULL; }
virtual void Remove(BfpFileWatcher* watcher) { NOT_IMPL; }
};
#ifndef BFP_HAS_FILEWATCHER
FileWatchManager* FileWatchManager::Get()
{
if (gFileWatchManager == NULL)
{
gFileWatchManager = new NullFilewatchManager();
gFileWatchManager->Init();
}
return gFileWatchManager;
}
#endif
BfpTimeStamp BfpToTimeStamp(const timespec& ts)
{
return (int64)(ts.tv_sec * 10000000) + (int64)(ts.tv_nsec / 100) + 116444736000000000;
}
int gBFPlatformLastError = 0;
uint32 Beefy::BFTickCount()
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now))
return 0;
return (uint32)((uint64)now.tv_sec * 1000.0 + (uint64)now.tv_nsec / 1000000);
}
int64 Beefy::EndianSwap(int64 val)
{
return __builtin_bswap64(val);
}
/*int* GetStdHandle(int32 handleId)
{
if (handleId == STD_INPUT_HANDLE)
return (int*)STDIN_FILENO;
if (handleId == STD_OUTPUT_HANDLE)
return (int*)STDOUT_FILENO;
return (int*)STDERR_FILENO;
}*/
/*int32 GetFileType(HANDLE fileHandle)
{
if (isatty(file->mHandleHandle))
return FILE_TYPE_CHAR;
return FILE_TYPE_DISK;
}*/
/*bool WriteFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToWrite, uint32* lpNumberOfBytesWritten, OVERLAPPED* lpOverlapped)
{
#ifdef BF_PLATFORM_IOS
int logType = -1;
if (hFile == (int*)STDOUT_FILENO)
logType = LOG_WARNING;
else if (hFile == (int*)STDERR_FILENO)
logType = LOG_ERR;
if (logType != -1)
{
static std::string strOut;
strOut.resize(nNumberOfBytesToWrite);
memcpy(&strOut[0], lpBuffer, nNumberOfBytesToWrite);
if ((strOut[0] != '\r') && (strOut[0] != '\n'))
syslog(LOG_WARNING, "%s", strOut.c_str());
}
#endif
int writeCount = (int)::write((int)(intptr)hFile, lpBuffer, nNumberOfBytesToWrite);
if (writeCount == -1)
{
//TODO: set gBFPlatformLastError
lpNumberOfBytesWritten = 0;
return false;
}
*lpNumberOfBytesWritten = (uint32)writeCount;
return true;
}*/
int64 Beefy::GetFileTimeWrite(const StringImpl& path)
{
struct stat statbuf = {0};
int result = stat(path.c_str(), &statbuf);
if (result == -1)
return 0;
return statbuf.st_mtime;
}
/*DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation)
{
std::wstring tzName0 = Beefy::UTF8Decode(tzname[0]);
std::wstring tzName1 = Beefy::UTF8Decode(tzname[1]);
bool isDST = false;
time_t timeNow;
time(&timeNow);
tm tmNow = *gmtime(&timeNow);
isDST = tmNow.tm_isdst;
struct tm checkTM;
memset(&checkTM, 0, sizeof(tm));
checkTM.tm_mday = 1;
checkTM.tm_year = tmNow.tm_year;
time_t checkTime = mktime(&checkTM);
time_t lastOffset = 0;
time_t minOffset = 0;
time_t maxOffset = 0;
for (int pass = 0; pass < 2; pass++)
{
int searchDir = 60*60*24;
int thresholdCount = 0;
while (true)
{
checkTime += searchDir;
tm checkTM = *gmtime(&checkTime);
if (checkTM.tm_year != tmNow.tm_year)
break; // No DST
mktime(&checkTM);
time_t offset = checkTM.tm_gmtoff;
if (lastOffset != offset)
{
if (thresholdCount == 0)
{
minOffset = offset;
maxOffset = offset;
}
else if (thresholdCount == 3)
{
SYSTEMTIME* sysTimeP = (offset == minOffset) ?
&lpTimeZoneInformation->StandardDate :
&lpTimeZoneInformation->DaylightDate;
if (offset == minOffset)
tzName0 = Beefy::UTF8Decode(checkTM.tm_zone);
else
tzName1 = Beefy::UTF8Decode(checkTM.tm_zone);
sysTimeP->wDay = 0;
sysTimeP->wDayOfWeek = 0;
sysTimeP->wYear = checkTM.tm_year + 1900;
sysTimeP->wMonth = checkTM.tm_mon;
sysTimeP->wDay = checkTM.tm_mday + 1;
sysTimeP->wHour = checkTM.tm_hour;
sysTimeP->wMinute = checkTM.tm_min;
sysTimeP->wSecond = checkTM.tm_sec;
sysTimeP->wMilliseconds = 0;
break;
}
else
{
if (thresholdCount == 1)
searchDir /= -24;
else
searchDir /= -60;
minOffset = std::min(minOffset, offset);
maxOffset = std::max(maxOffset, offset);
}
thresholdCount++;
lastOffset = offset;
}
}
}
wcsncpy(lpTimeZoneInformation->StandardName, tzName0.c_str(), 32);
wcsncpy(lpTimeZoneInformation->DaylightName, tzName1.c_str(), 32);
lpTimeZoneInformation->DaylightBias = (int32)maxOffset;
lpTimeZoneInformation->StandardBias = (int32)minOffset;
if (minOffset == maxOffset)
return 0;
return isDST ? 2 : 1;
}*/
bool Beefy::FileExists(const StringImpl& path, String* outActualName)
{
struct stat statbuf = {0};
int result = stat(path.c_str(), &statbuf);
if (result != 0)
return false;
return !S_ISDIR(statbuf.st_mode);
}
bool Beefy::DirectoryExists(const StringImpl& path, String* outActualName)
{
struct stat statbuf = {0};
int result = stat(path.c_str(), &statbuf);
if (result != 0)
return false;
return S_ISDIR(statbuf.st_mode);
}
uint64 Beefy::BFGetTickCountMicro()
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now))
return 0;
return ((uint64)now.tv_sec * 1000000.0 + (uint64)now.tv_nsec / 1000);
}
uint64 Beefy::BFGetTickCountMicroFast()
{
return BFGetTickCountMicro();
}
/*
int64 abs(int64 val)
{
return llabs(val);
}
*/
void mkdir(const char* path)
{
mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}
typedef void(*CrashInfoFunc)();
2020-06-30 12:13:20 -07:00
struct BfpGlobalData
{
CritSect mSysCritSect;
String mCrashInfo;
Array<CrashInfoFunc> mCrashInfoFuncs;
};
static BfpGlobalData* gBfpGlobal;
static BfpGlobalData* BfpGetGlobalData()
{
if (gBfpGlobal == NULL)
gBfpGlobal = new BfpGlobalData();
return gBfpGlobal;
}
#ifdef BFP_HAS_BACKTRACE
struct bt_ctx {
struct backtrace_state *state;
int error;
};
static void error_callback(void *data, const char *msg, int errnum)
{
struct bt_ctx *ctx = (bt_ctx*)data;
BFP_ERRPRINTF("ERROR: %s (%d)", msg, errnum);
ctx->error = 1;
}
static void syminfo_callback (void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize)
{
char str[4096];
if (symname)
2021-06-28 10:28:32 -07:00
BF_stbsp_snprintf(str, 4096, "%@ %s\n", pc, symname);
else
2021-06-28 10:28:32 -07:00
BF_stbsp_snprintf(str, 4096, "%@\n", pc);
BFP_ERRPRINTF("%s", str);
}
static int full_callback(void *data, uintptr_t pc, const char* filename, int lineno, const char* function)
{
struct bt_ctx *ctx = (bt_ctx*)data;
if (function)
{
int status = -1;
char* demangledName = abi::__cxa_demangle(function, NULL, NULL, &status );
const char* showName = (demangledName != NULL) ? demangledName : function;
char str[4096];
2021-06-28 10:28:32 -07:00
BF_stbsp_snprintf(str, 4096, "%@ %s %s:%d\n", pc, showName, filename?filename:"??", lineno);
BFP_ERRPRINTF("%s", str);
if (demangledName != NULL)
free(demangledName);
}
else
backtrace_syminfo (ctx->state, pc, syminfo_callback, error_callback, data);
return 0;
}
static int simple_callback(void *data, uintptr_t pc)
{
struct bt_ctx *ctx = (bt_ctx*)data;
backtrace_pcinfo(ctx->state, pc, full_callback, error_callback, data);
return 0;
}
static inline void bt(struct backtrace_state *state)
{
struct bt_ctx ctx = {state, 0};
//backtrace_print(state, 0, stdout);
backtrace_simple(state, 2, simple_callback, error_callback, &ctx);
}
#endif
typedef void(*CrashInfoFunc)();
static String gCmdLine;
static String gExePath;
typedef struct _Unwind_Context _Unwind_Context; // opaque
typedef enum {
_URC_NO_REASON = 0,
_URC_OK = 0,
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
_URC_FATAL_PHASE2_ERROR = 2,
_URC_FATAL_PHASE1_ERROR = 3,
_URC_NORMAL_STOP = 4,
_URC_END_OF_STACK = 5,
_URC_HANDLER_FOUND = 6,
_URC_INSTALL_CONTEXT = 7,
_URC_CONTINUE_UNWIND = 8,
_URC_FAILURE = 9
} _Unwind_Reason_Code;
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void *);
extern "C" _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
extern "C" uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
static String gUnwindExecStr;
static int gUnwindIdx = 0;
static _Unwind_Reason_Code UnwindHandler(struct _Unwind_Context* context, void* ref)
{
gUnwindIdx++;
if (gUnwindIdx < 2)
return _URC_NO_REASON;
void* addr = (void*)_Unwind_GetIP(context);
#if BFP_HAS_ATOS
gUnwindExecStr += StrFormat(" %p", addr);
#else
Dl_info info;
2020-06-10 07:13:48 -07:00
if (dladdr(addr, &info))
{
2020-06-10 07:13:48 -07:00
if (info.dli_sname)
BFP_ERRPRINTF("0x%p %s\n", addr, info.dli_sname);
else if (info.dli_fname)
BFP_ERRPRINTF("0x%p %s\n", addr, info.dli_fname);
else
BFP_ERRPRINTF("0x%p\n", addr);
}
#endif
return _URC_NO_REASON;
}
static bool FancyBacktrace()
{
gUnwindExecStr += StrFormat("atos -p %d", getpid());
_Unwind_Backtrace(&UnwindHandler, NULL);
#if BFP_HAS_ATOS
return system(gUnwindExecStr.c_str()) == 0;
#else
return true;
#endif
}
static void Crashed()
{
//
{
2020-06-30 12:13:20 -07:00
AutoCrit autoCrit(BfpGetGlobalData()->mSysCritSect);
String debugDump;
debugDump += "**** FATAL APPLICATION ERROR ****\n";
2020-06-30 12:13:20 -07:00
for (auto func : BfpGetGlobalData()->mCrashInfoFuncs)
func();
2020-06-30 12:13:20 -07:00
if (!BfpGetGlobalData()->mCrashInfo.IsEmpty())
{
2020-06-30 12:13:20 -07:00
debugDump += BfpGetGlobalData()->mCrashInfo;
debugDump += "\n";
}
BFP_ERRPRINTF("%s", debugDump.c_str());
}
if (!FancyBacktrace())
{
#ifdef BFP_HAS_EXECINFO
void* array[64];
size_t size;
char** strings;
size_t i;
size = backtrace(array, 64);
strings = backtrace_symbols(array, size);
for (i = 0; i < size; i++)
BFP_ERRPRINTF("%s\n", strings[i]);
free(strings);
#endif
}
exit(1);
}
static void SigHandler(int sig)
{
//printf("SigHandler paused...\n");
const char* sigName = NULL;
switch (sig)
{
case SIGFPE:
sigName = "SIGFPE";
break;
case SIGSEGV:
sigName = "SIGSEGV";
break;
case SIGABRT:
sigName = "SIGABRT";
break;
case SIGILL:
sigName = "SIGILL";
break;
}
if (sigName != NULL)
2020-06-30 12:13:20 -07:00
BfpGetGlobalData()->mCrashInfo += StrFormat("Signal: %s\n", sigName);
else
2020-06-30 12:13:20 -07:00
BfpGetGlobalData()->mCrashInfo += StrFormat("Signal: %d\n", sig);
Crashed();
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_Init(int version, BfpSystemInitFlags flags)
{
2020-06-30 12:13:20 -07:00
BfpGetGlobalData();
if (version != BFP_VERSION)
{
BfpSystem_FatalError(StrFormat("Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version).c_str(), "BFP FATAL ERROR");
}
struct sigaction ignoreAction = { SIG_IGN };
sigaction(SIGPIPE, &ignoreAction, NULL);
2020-06-10 07:13:48 -07:00
//if (ptrace(PTRACE_TRACEME, 0, 1, 0) != -1)
{
//ptrace(PTRACE_DETACH, 0, 1, 0);
//signal(SIGSEGV, SigHandler);
//signal(SIGFPE, SigHandler);
//signal(SIGABRT, SigHandler);
/*struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = signal_segv;
action.sa_flags = SA_SIGINFO;
if(sigaction(SIGSEGV, &action, NULL) < 0)
perror("sigaction");*/
}
}
2024-03-17 06:57:00 -04:00
BFP_EXPORT void BFP_CALLTYPE BfpSystem_InitCrashCatcher(BfpSystemInitFlags flags)
{
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_ShutdownCrashCatcher()
{
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCommandLine(int argc, char** argv)
{
2020-07-07 13:08:45 -07:00
char exePath[PATH_MAX] = { 0 };
int nchar = readlink("/proc/self/exe", exePath, PATH_MAX);
if (nchar > 0)
{
gExePath = exePath;
}
else
{
char* relPath = argv[0];
char* cwd = getcwd(NULL, 0);
gExePath = GetAbsPath(relPath, cwd);
free(cwd);
}
for (int i = 0; i < argc; i++)
{
if (i != 0)
gCmdLine.Append(' ');
String arg = argv[i];
if (arg.IsEmpty() || arg.Contains(' ') || arg.Contains('\t') || arg.Contains('\r') || arg.Contains('\n') || arg.Contains('\"'))
{
arg.Replace("\"", "\\\"");
gCmdLine.Append("\"");
gCmdLine.Append(arg);
gCmdLine.Append("\"");
}
else
gCmdLine.Append(arg);
}
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashReportKind(BfpCrashReportKind crashReportKind)
{
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
{
2020-06-30 12:13:20 -07:00
AutoCrit autoCrit(BfpGetGlobalData()->mSysCritSect);
BfpGetGlobalData()->mCrashInfoFuncs.Add(crashInfoFunc);
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str) // Can do at any time, or during CrashInfoFunc callbacks
{
2020-06-30 12:13:20 -07:00
AutoCrit autoCrit(BfpGetGlobalData()->mSysCritSect);
BfpGetGlobalData()->mCrashInfo.Append(str);
}
2020-09-17 14:15:38 -07:00
BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashRelaunchCmd(const char* str)
{
2020-09-17 14:15:38 -07:00
}
void BfpSystem_Shutdown()
{
if (gFileWatchManager != NULL)
{
gFileWatchManager->Shutdown();
gFileWatchManager = NULL;
}
}
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_TickCount()
{
return Beefy::BFTickCount();
}
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpSystem_GetTimeStamp()
{
struct timeval tv;
BfpTimeStamp result = 11644473600LL;
gettimeofday(&tv, NULL);
result += tv.tv_sec;
result *= 10000000LL;
result += tv.tv_usec * 10;
return result;
}
BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_EndianSwap16(uint16 val)
{
return __builtin_bswap16(val);
}
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_EndianSwap32(uint32 val)
{
return __builtin_bswap32(val);
}
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_EndianSwap64(uint64 val)
{
return __builtin_bswap64(val);
}
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchange32(uint32* ptr, uint32 val)
{
// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
uint32 prevVal = __sync_lock_test_and_set(ptr, val);
__sync_synchronize();
return prevVal;
}
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchange64(uint64* ptr, uint64 val)
{
// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
uint64 prevVal = __sync_lock_test_and_set(ptr, val);
__sync_synchronize();
return prevVal;
}
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd32(uint32* ptr, uint32 val)
{
return __sync_fetch_and_add(ptr, val);
}
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd64(uint64* ptr, uint64 val)
{
return __sync_fetch_and_add(ptr, val);
}
BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange32(uint32* ptr, uint32 oldVal, uint32 newVal)
{
return __sync_val_compare_and_swap(ptr, oldVal, newVal);
}
BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange64(uint64* ptr, uint64 oldVal, uint64 newVal)
{
return __sync_val_compare_and_swap(ptr, oldVal, newVal);
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_FatalError(const char* error, const char* title)
{
BFP_ERRPRINTF("%s\n", error);
fflush(stderr);
Crashed();
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetCommandLine(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
{
TryStringOut(gCmdLine, outStr, inOutStrSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetExecutablePath(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
{
#ifdef BF_PLATFORM_DARWIN
if (gExePath.IsEmpty())
{
char path[4096];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
gExePath = path;
// When when running with a './file', we end up with an annoying '/./' in our path
gExePath.Replace("/./", "/");
}
#endif
TryStringOut(gExePath, outStr, inOutStrSize, (BfpResult*)outResult);
}
extern char **environ;
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetEnvironmentStrings(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
{
String env;
char** envPtr = environ;
while (true)
{
char* envStr = *envPtr;
if (envStr == NULL)
break;
env.Append(envStr, strlen(envStr) + 1);
++envPtr;
}
TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
}
BFP_EXPORT int BFP_CALLTYPE BfpSystem_GetNumLogicalCPUs(BfpSystemResult* outResult)
{
#ifdef BF_PLATFORM_ANDROID
//TODO: Handle this
OUTRESULT(BfpSystemResult_Ok);
return 1;
#elif defined BF_PLATFORM_DARWIN
OUTRESULT(BfpSystemResult_Ok);
int count = 1;
size_t count_len = sizeof(count);
sysctlbyname("hw.logicalcpu", &count, &count_len, NULL, 0);
return count;
#else
OUTRESULT(BfpSystemResult_Ok);
return get_nprocs_conf();
#endif
}
BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTick()
{
return 10000000;
}
BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTickFreq()
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return (now.tv_sec * 10000000LL) + now.tv_nsec / 100;
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_CreateGUID(BfpGUID* outGuid)
{
// uuid_t guid;
// uuid_generate(guid);
// BfpGUID bfpGuid;
// memcpy(&bfpGuid, guid, 16);
// return bfpGuid;
uint8* ptr = (uint8*)outGuid;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<uint8> dis(0, 255);
for (int i = 0; i < 16; i++)
ptr[i] = dis(gen);
// variant must be 10xxxxxx
ptr[8] &= 0xBF;
ptr[8] |= 0x80;
// version must be 0100xxxx
ptr[6] &= 0x4F;
ptr[6] |= 0x40;
}
BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
{
char hostName[1024];
gethostname(hostName, 1024);
TryStringOut(hostName, outStr, inOutStrSize, (BfpResult*)outResult);
}
// BfpProcess
BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
{
return getpid();
}
BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
{
return false;
}
BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
{
NOT_IMPL;
return NULL;
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
{
NOT_IMPL;
return 0;
}
// BfpSpawn
struct BfpSpawn
{
int mPid;
bool mExited;
int mStatus;
int mStdInFD;
int mStdOutFD;
int mStdErrFD;
};
BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
{
Beefy::Array<Beefy::StringView> stringViews;
//printf("BfpSpawn_Create: %s %s %x\n", inTargetPath, args, flags);
2021-07-15 05:58:59 -07:00
char* prevWorkingDir = NULL;
if ((workingDir != NULL) && (workingDir[0] != 0))
{
if (chdir(workingDir) != 0)
{
//printf("CHDIR failed %s\n", workingDir);
OUTRESULT(BfpSpawnResult_UnknownError);
return NULL;
}
2021-07-15 05:58:59 -07:00
prevWorkingDir = getcwd(NULL, 0);
}
2021-07-15 05:58:59 -07:00
defer(
{
if (prevWorkingDir != NULL)
{
chdir(prevWorkingDir);
free(prevWorkingDir);
}
});
String newArgs;
String tempFileName;
if ((flags & BfpSpawnFlag_UseArgsFile) != 0)
{
char tempFileNameStr[256];
int size = 256;
BfpFileResult fileResult;
BfpFile_GetTempFileName(tempFileNameStr, &size, &fileResult);
if (fileResult == BfpFileResult_Ok)
{
tempFileName = tempFileNameStr;
BfpFileResult fileResult;
BfpFile* file = BfpFile_Create(tempFileNameStr, BfpFileCreateKind_CreateAlways, BfpFileCreateFlag_Write, BfpFileAttribute_Normal, &fileResult);
if (file == NULL)
{
OUTRESULT(BfpSpawnResult_TempFileError);
return NULL;
}
if ((flags & BfpSpawnFlag_UseArgsFile_Native) != 0)
{
UTF16String wStr = UTF8Decode(args);
if ((flags & BfpSpawnFlag_UseArgsFile_BOM) != 0)
{
uint8 bom[2] = { 0xFF, 0xFE };
BfpFile_Write(file, bom, 2, -1, NULL);
}
BfpFile_Write(file, wStr.c_str(), wStr.length() * 2, -1, NULL);
}
else
BfpFile_Write(file, args, strlen(args), -1, NULL);
BfpFile_Release(file);
newArgs.Append("@");
newArgs.Append(tempFileName);
if (newArgs.Contains(' '))
{
newArgs.Insert(0, '\"');
newArgs.Append('\"');
}
args = newArgs.c_str();
}
}
int32 firstCharIdx = -1;
bool inQuote = false;
String targetPath = inTargetPath;
String verb;
if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
{
String target = targetPath;
int barPos = (int)target.IndexOf('|');
if (barPos != -1)
{
verb = targetPath.Substring(barPos + 1);
targetPath.RemoveToEnd(barPos);
}
}
// When executing in a shell the arguments are not split
if ((flags & BfpSpawnFlag_UseShellExecute) == 0)
{
int32 i = 0;
for ( ; true; i++)
{
char c = args[i];
if (c == '\0')
break;
if ((c == ' ') && (!inQuote))
{
if (firstCharIdx != -1)
{
stringViews.Add(Beefy::StringView(args + firstCharIdx, i - firstCharIdx));
firstCharIdx = -1;
}
}
else
{
if (firstCharIdx == -1)
firstCharIdx = i;
if (c == '"')
inQuote = !inQuote;
else if ((inQuote) && (c == '\\'))
{
c = args[i + 1];
if (c == '"')
i++;
}
}
}
if (firstCharIdx != -1)
stringViews.Add(Beefy::StringView(args + firstCharIdx, i - firstCharIdx));
}
Beefy::Array<char*> argvArr;
if ((flags & BfpSpawnFlag_UseShellExecute) == 0 && (flags & BfpSpawnFlag_ArgsIncludesTarget) == 0)
argvArr.Add(strdup(targetPath.c_str()));
// When executing in a shell the arguments are not split nor are the quotes removed
if ((flags & BfpSpawnFlag_UseShellExecute) == 0)
{
for (int32 i = 0; i < (int32)stringViews.size(); i++)
{
Beefy::StringView stringView = stringViews[i];
char* str = NULL;
for (int32 pass = 0; pass < 2; pass++)
{
char* strPtr = str;
int32 strPos = 0;
for (int32 char8Idx = 0; char8Idx < stringView.mLength; char8Idx++)
{
char c = stringView.mPtr[char8Idx];
if (c == '"')
inQuote = !inQuote;
else
{
if ((inQuote) && (c == '\\') && (char8Idx < stringView.mLength - 1))
{
char nextC = stringView.mPtr[char8Idx + 1];
if (nextC == '"')
{
c = nextC;
char8Idx++;
}
}
if (strPtr != NULL)
*(strPtr++) = c;
strPos++;
}
}
if (pass == 0)
str = (char*)malloc(strPos + 1);
else
*(strPtr++) = 0;
}
argvArr.Add(str);
}
}
// Handle shell execution
if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
{
// Create command string
int argsLength = strlen(args);
int length = targetPath.mLength + 1 + argsLength;
char* commandStr = (char*)malloc(length + 1);
memcpy(commandStr, targetPath.c_str(), targetPath.mLength);
commandStr[targetPath.mLength] = ' ';
memcpy(&commandStr[targetPath.mLength + 1], args, argsLength);
commandStr[length] = '\0';
// Replace target path
targetPath.Clear();
targetPath.Append("/bin/sh");
// Add arguments
argvArr.Add(strdup("sh"));
argvArr.Add(strdup("-c"));
argvArr.Add(commandStr);
}
argvArr.Add(NULL);
char** argv = NULL;
//pid_t pid = 0;
//int status = posix_spawn(&pid, targetPath, NULL, NULL, &argvArr[0], environ);
Beefy::Array<char*> envArr;
if (env != NULL)
{
char* envPtr = (char*)env;
while (true)
{
if (*envPtr == 0)
break;
envArr.Add(envPtr);
envPtr += strlen(envPtr) + 1;
}
}
envArr.Add(NULL);
int stdInFD[2];
int stdOutFD[2];
int stdErrFD[2];
bool failed = false;
if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
if (pipe(stdInFD) != 0)
failed = true;
if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
if (pipe(stdOutFD) != 0)
failed = true;
if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
if (pipe(stdErrFD) != 0)
failed = true;
if (failed)
{
//printf("Pipe failed\n");
OUTRESULT(BfpSpawnResult_UnknownError);
return NULL;
}
BfpSpawn* spawn;
pid_t pid = fork();
if (pid == -1) // Error
{
OUTRESULT(BfpSpawnResult_UnknownError);
return NULL;
}
else if (pid == 0) // Child
{
if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
2019-10-29 05:01:04 -07:00
{
close(stdInFD[1]);
while ((dup2(stdInFD[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
close(stdInFD[0]);
2019-10-29 05:01:04 -07:00
}
if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
2019-10-29 05:01:04 -07:00
{
close(stdOutFD[0]);
while ((dup2(stdOutFD[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
2019-10-29 05:01:04 -07:00
close(stdOutFD[1]);
}
if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
2019-10-29 05:01:04 -07:00
{
close(stdErrFD[0]);
while ((dup2(stdErrFD[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
2019-10-29 05:01:04 -07:00
close(stdErrFD[0]);
}
// If successful then this shouldn't return at all:
int result;
if (env != NULL)
result = execve(targetPath.c_str(), (char* const*)&argvArr[0], (char* const*)&envArr[0]);
else
result = execv(targetPath.c_str(), (char* const*)&argvArr[0]);
BFP_ERRPRINTF("Couldn't execute %s\n", targetPath.c_str());
exit(-1);
}
else // Parent
{
spawn = new BfpSpawn();
if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
{
spawn->mStdInFD = stdInFD[1];
close(stdInFD[0]);
}
else
spawn->mStdInFD = 0;
if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
{
spawn->mStdOutFD = stdOutFD[0];
close(stdOutFD[1]);
}
else
spawn->mStdOutFD = 0;
if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
{
spawn->mStdErrFD = stdErrFD[0];
close(stdErrFD[1]);
}
else
spawn->mStdErrFD = 0;
}
for (auto val : argvArr)
free(val);
//printf("Spawn pid:%d status:%d\n", pid, status);
spawn->mPid = pid;
spawn->mExited = false;
spawn->mStatus = 0;
return spawn;
}
void BfpSpawn_Release(BfpSpawn* spawn)
{
// We don't support 'detaching' currently- this can create zombie processes since we
// don't have a reaper strategy
BfpSpawn_WaitFor(spawn, -1, NULL, NULL);
delete spawn;
}
BFP_EXPORT void BFP_CALLTYPE BfpSpawn_GetStdHandles(BfpSpawn* spawn, BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
{
if (outStdIn != NULL)
{
*outStdIn = new BfpFile(spawn->mStdInFD);
spawn->mStdInFD = 0;
}
if (outStdOut != NULL)
{
*outStdOut = new BfpFile(spawn->mStdOutFD);
spawn->mStdOutFD = 0;
}
if (outStdErr != NULL)
{
*outStdErr = new BfpFile(spawn->mStdErrFD);
spawn->mStdErrFD = 0;
}
}
BFP_EXPORT int BFP_CALLTYPE BfpSpawn_GetProcessId(BfpSpawn* spawn)
2024-07-19 10:31:33 +02:00
{
return spawn->mPid;
}
bool BfpSpawn_WaitFor(BfpSpawn* spawn, int waitMS, int* outExitCode, BfpSpawnResult* outResult)
{
OUTRESULT(BfpSpawnResult_Ok);
if (!spawn->mExited)
{
int flags = 0;
if (waitMS != -1)
{
flags = WNOHANG;
}
//TODO: Implement values other than 0 or -1 for waitMS?
pid_t result = waitpid(spawn->mPid, &spawn->mStatus, flags);
if (result != spawn->mPid)
return false;
spawn->mExited = true;
}
if (!WIFEXITED(spawn->mStatus) && !WIFSIGNALED(spawn->mStatus))
return false;
if (outExitCode != NULL)
*outExitCode = WEXITSTATUS(spawn->mStatus);
return true;
}
// BfpFileWatcher
BFP_EXPORT BfpFileWatcher* BFP_CALLTYPE BfpFileWatcher_WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult)
{
return FileWatchManager::Get()->WatchDirectory(path, callback, flags, userData, outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpFileWatcher_Release(BfpFileWatcher* fileWatcher)
{
FileWatchManager::Get()->Remove(fileWatcher);
}
// BfpThread
struct BfpThread
{
bool mPThreadReleased;
BfpThreadStartProc mStartProc;
void* mThreadParam;
#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
BfpEvent* mDoneEvent;
#endif
pthread_t mPThread;
int mRefCount;
int mPriority;
BfpThread()
{
}
2022-03-18 18:06:14 -07:00
~BfpThread()
{
2022-03-18 18:06:14 -07:00
}
void Release()
{
int refCount = __sync_fetch_and_sub(&mRefCount, 1) - 1;
if (refCount == 0)
delete this;
}
};
struct BfpThreadInfo
{
intptr mStackBase;
int mStackLimit;
2020-05-16 10:07:17 -07:00
pthread_t mPThread;
};
static __thread BfpThread* gCurrentThread;
static __thread BfpThreadInfo gCurrentThreadInfo;
void* ThreadFunc(void* threadParam)
{
BfpThread* thread = (BfpThread*)threadParam;
gCurrentThread = thread;
thread->mStartProc(thread->mThreadParam);
#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
BfpEvent_Set(thread->mDoneEvent, true);
#endif
thread->Release();
return NULL;
}
BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_Create(BfpThreadStartProc startProc, void* threadParam, intptr stackSize, BfpThreadCreateFlags flags, BfpThreadId* outThreadId)
{
BfpThread* thread = new BfpThread();
thread->mPThreadReleased = false;
thread->mStartProc = startProc;
thread->mThreadParam = threadParam;
thread->mRefCount = 2;
thread->mPriority = 0;
#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
thread->mDoneEvent = BfpEvent_Create(BfpEventFlag_None);
#endif
BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));
pthread_attr_t params;
pthread_attr_init(&params);
pthread_attr_setstacksize(&params, stackSize);
//pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
pthread_create(&thread->mPThread, &params, ThreadFunc, (void*)thread);
pthread_attr_destroy(&params);
if (outThreadId != NULL)
*outThreadId = (BfpThreadId)thread->mPThread;
return thread;
}
#define FIXTHREAD() \
pthread_t pt; \
if (((intptr)thread & 1) != 0) \
{ \
pt = (pthread_t)((intptr)thread & ~3); \
thread = NULL; \
} else \
pt = thread->mPThread
BFP_EXPORT void BFP_CALLTYPE BfpThread_Release(BfpThread* thread)
{
FIXTHREAD();
if (thread == NULL)
return;
#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
BfpEvent_Release(thread->mDoneEvent);
#endif
if (!thread->mPThreadReleased)
{
pthread_detach(thread->mPThread);
thread->mPThreadReleased = true;
}
thread->Release();
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_SetName(BfpThread* thread, const char* name, BfpThreadResult* outResult)
{
OUTRESULT(BfpThreadResult_Ok);
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_GetName(BfpThread* thread, char* outName, int* inOutNameSize, BfpThreadResult* outResult)
{
String str = "";
TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
}
BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_GetCurrent()
{
if (gCurrentThread == NULL)
{
// Not a "true" BfpThread, this is either the main thread or a thread we didn't create
return (BfpThread*)((intptr)pthread_self() | 1);
}
return gCurrentThread;
}
BFP_EXPORT BfpThreadId BFP_CALLTYPE BfpThread_GetCurrentId()
{
if (gCurrentThread == NULL)
{
return (BfpThreadId)((intptr)pthread_self());
}
return (BfpThreadId)gCurrentThread->mPThread;
}
BFP_EXPORT bool BFP_CALLTYPE BfpThread_WaitFor(BfpThread* thread, int waitMS)
{
FIXTHREAD();
if (waitMS == -1)
{
pthread_join(pt, NULL);
thread->mPThreadReleased = true;
return true;
}
#ifdef BFP_HAS_PTHREAD_TIMEDJOIN_NP
struct timespec waitTime;
waitTime.tv_sec = waitMS / 1000;
waitTime.tv_nsec = (waitMS % 1000) * 1000000;
int result = pthread_timedjoin_np(pt, NULL, &waitTime);
if (result == 0)
{
if (thread != NULL)
thread->mPThreadReleased = true;
return true;
}
return false;
#else
if (thread == NULL)
BF_FATAL("Invalid thread with non-infinite wait");
return BfpEvent_WaitFor(thread->mDoneEvent, waitMS);
#endif
}
BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Kill(BfpSpawn* spawn, int exitCode, BfpKillFlags killFlags, BfpSpawnResult* outResult)
{
//TODO: Implement
OUTRESULT(BfpSpawnResult_UnknownError);
}
BFP_EXPORT BfpThreadPriority BFP_CALLTYPE BfpThread_GetPriority(BfpThread* thread, BfpThreadResult* outResult)
{
FIXTHREAD();
OUTRESULT(BfpThreadResult_Ok);
if (thread == NULL)
return (BfpThreadPriority)0;
return (BfpThreadPriority)thread->mPriority;
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_SetPriority(BfpThread* thread, BfpThreadPriority threadPriority, BfpThreadResult* outResult)
{
// In effect, we have two 'nice' settings: 0 (normal) or 10 (low)
// High-priority settings just don't do anything
//pid_t tid = syscall(SYS_gettid);
//int ret = setpriority(PRIO_PROCESS, tid, -std::min(nPriority, 0) * 10);
OUTRESULT(BfpThreadResult_Ok);
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_Suspend(BfpThread* thread, BfpThreadResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_Resume(BfpThread* thread, BfpThreadResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpThread_GetIntRegisters(BfpThread* thread, intptr* outStackPtr, intptr* outIntRegs, int* inOutIntRegCount, BfpThreadResult* outResult)
{
NOT_IMPL;
}
2020-05-16 10:07:17 -07:00
BFP_EXPORT void BFP_CALLTYPE BfpThread_Sleep(int sleepMS)
{
2020-05-16 10:07:17 -07:00
usleep(sleepMS * 1000);
}
2020-05-16 10:07:17 -07:00
BFP_EXPORT bool BFP_CALLTYPE BfpThread_Yield()
{
return sched_yield() == 0;
}
2020-05-16 10:07:17 -07:00
///
2020-05-16 10:07:17 -07:00
BFP_EXPORT BfpThreadInfo* BFP_CALLTYPE BfpThreadInfo_Create()
{
BfpThreadInfo* threadInfo = new BfpThreadInfo();
threadInfo->mStackBase = 0;
threadInfo->mStackLimit = 0;
2020-05-16 10:07:17 -07:00
threadInfo->mPThread = pthread_self();
2020-05-21 06:58:26 -07:00
return threadInfo;
}
2020-05-16 10:07:17 -07:00
BFP_EXPORT void BFP_CALLTYPE BfpThreadInfo_Release(BfpThreadInfo* threadInfo)
{
2020-05-16 10:07:17 -07:00
delete threadInfo;
}
2020-05-16 12:26:13 -07:00
BFP_EXPORT void BFP_CALLTYPE BfpThreadInfo_GetStackInfo(BfpThreadInfo* threadInfo, intptr* outStackBase, int* outStackLimit, BfpThreadInfoFlags flags, BfpThreadResult* outResult)
{
2020-05-16 10:07:17 -07:00
#ifdef BFP_HAS_PTHREAD_GETATTR_NP
if (threadInfo == NULL)
{
threadInfo = &gCurrentThreadInfo;
threadInfo->mPThread = pthread_self();
}
if (threadInfo->mStackBase == 0)
{
void* stackBase = 0;
size_t stackLimit = 0;
pthread_attr_t attr;
pthread_getattr_np(threadInfo->mPThread, &attr);
pthread_attr_getstack(&attr, &stackBase, &stackLimit);
threadInfo->mStackBase = (intptr)stackBase + stackLimit;
threadInfo->mStackLimit = (int)stackLimit;
pthread_attr_destroy(&attr);
}
*outStackBase = threadInfo->mStackBase;
*outStackLimit = threadInfo->mStackLimit;
OUTRESULT(BfpThreadResult_Ok);
#else
OUTRESULT(BfpThreadResult_UnknownError);
#endif
}
2020-05-16 10:07:17 -07:00
///
struct BfpCritSect
{
pthread_mutex_t mPMutex;
};
BFP_EXPORT BfpCritSect* BFP_CALLTYPE BfpCritSect_Create()
{
BfpCritSect* critSect = new BfpCritSect();
pthread_mutexattr_t attributes;
pthread_mutexattr_init(&attributes);
pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&critSect->mPMutex, &attributes);
pthread_mutexattr_destroy(&attributes);
return critSect;
}
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Release(BfpCritSect* critSect)
{
pthread_mutex_destroy(&critSect->mPMutex);
2022-03-18 18:06:14 -07:00
delete critSect;
}
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect)
{
pthread_mutex_lock(&critSect->mPMutex);
}
BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS)
{
if (waitMS == -1)
{
BfpCritSect_Enter(critSect);
return true;
}
else if (waitMS == 0)
{
return pthread_mutex_trylock(&critSect->mPMutex) == 0;
}
uint32 start = Beefy::BFTickCount();
while ((int)(Beefy::BFTickCount() - start) < waitMS)
{
if (pthread_mutex_trylock(&critSect->mPMutex) == 0)
{
return true;
}
}
return false;
}
BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
{
pthread_mutex_unlock(&critSect->mPMutex);
}
2022-07-04 10:20:38 -07:00
BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create(BfpTLSProc exitProc)
{
pthread_key_t key = 0;
2022-07-04 10:20:38 -07:00
pthread_key_create(&key, exitProc);
return (BfpTLS*)(intptr)key;
}
BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
{
pthread_key_delete((pthread_key_t)(intptr)tls);
}
BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
{
pthread_setspecific((pthread_key_t)(intptr)tls, value);
}
BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
{
return pthread_getspecific((pthread_key_t)(intptr)tls);
}
struct BfpEvent
{
pthread_mutex_t mMutex;
pthread_cond_t mCondVariable;
bool mSet;
bool mManualReset;
};
BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)
{
BfpEvent* event = new BfpEvent();
pthread_mutex_init(&event->mMutex, NULL);
pthread_cond_init(&event->mCondVariable, NULL);
event->mSet = (flags & (BfpEventFlag_InitiallySet_Auto | BfpEventFlag_InitiallySet_Manual)) != 0;
event->mManualReset = (flags & BfpEventFlag_InitiallySet_Manual) != 0;
return event;
}
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Release(BfpEvent* event)
{
pthread_cond_destroy(&event->mCondVariable);
pthread_mutex_destroy(&event->mMutex);
2022-03-18 18:06:14 -07:00
delete event;
}
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Set(BfpEvent* event, bool requireManualReset)
{
pthread_mutex_lock(&event->mMutex);
event->mSet = true;
if (requireManualReset)
event->mManualReset = true;
if (event->mManualReset)
pthread_cond_broadcast(&event->mCondVariable);
else
pthread_cond_signal(&event->mCondVariable);
pthread_mutex_unlock(&event->mMutex);
}
BFP_EXPORT void BFP_CALLTYPE BfpEvent_Reset(BfpEvent* event, BfpEventResult* outResult)
{
event->mSet = false;
event->mManualReset = false;
}
BFP_EXPORT bool BFP_CALLTYPE BfpEvent_WaitFor(BfpEvent* event, int waitMS)
{
int result = pthread_mutex_lock(&event->mMutex);
BF_ASSERT(result == 0);
while (!event->mSet)
{
if (waitMS == -1)
{
pthread_cond_wait(&event->mCondVariable, &event->mMutex);
}
else
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += waitMS / 1000;
ts.tv_nsec += (waitMS % 1000) * 1000000;
result = pthread_cond_timedwait(&event->mCondVariable, &event->mMutex, &ts);
if (waitMS == (uint32)-1)
BF_ASSERT(result == 0);
if (result != 0)
{
// Timeout
pthread_mutex_unlock(&event->mMutex);
return false;
}
}
}
if (!event->mManualReset)
event->mSet = false;
pthread_mutex_unlock(&event->mMutex);
return true;
}
BFP_EXPORT BfpDynLib* BFP_CALLTYPE BfpDynLib_Load(const char* fileName)
{
BfpDynLib* mod = NULL;
static const char* prefixes[] = {NULL, "lib"};
static const char* suffixes[] = {NULL, ".so", ".dylib"};
for (int prefixIdx = 0; prefixIdx < sizeof(prefixes)/sizeof(prefixes[0]); prefixIdx++)
{
for (int suffixIdx = 0; suffixIdx < sizeof(suffixes)/sizeof(suffixes[0]); suffixIdx++)
{
const char* prefix = prefixes[prefixIdx];
const char* suffix = suffixes[suffixIdx];
Beefy::String checkName = fileName;
if (prefix != NULL)
checkName = Beefy::String(prefix) + checkName;
if (suffix != NULL)
{
int dotPos = checkName.LastIndexOf('.');
if (dotPos != -1)
checkName.RemoveToEnd(dotPos);
checkName += suffix;
}
mod = (BfpDynLib*)dlopen(checkName.c_str(), RTLD_LAZY);
if (mod != NULL)
return mod;
}
}
/*mod = (BfpDynLib*)dlopen("/var/Beef/qt-build/Debug/bin/libIDEHelper.so", RTLD_LAZY);;
if (mod == NULL)
{
printf("Err: %s\n", dlerror());
fflush(stdout);
}*/
return NULL;
}
BFP_EXPORT void BFP_CALLTYPE BfpDynLib_Release(BfpDynLib* lib)
{
dlclose((void*)lib);
}
BFP_EXPORT void BFP_CALLTYPE BfpDynLib_GetFilePath(BfpDynLib* lib, char* outPath, int* inOutPathSize, BfpLibResult* outResult)
{
Beefy::String path;
#ifdef BFP_HAS_DLINFO
link_map* linkMap = NULL;
dlinfo((void*)lib, RTLD_DI_LINKMAP, &linkMap);
if (linkMap == NULL)
{
OUTRESULT(BfpLibResult_UnknownError);
return;
}
path = linkMap->l_name;
#else
Dl_info info;
if (dladdr((void*)lib, &info) == 0)
{
OUTRESULT(BfpLibResult_UnknownError);
return;
}
path = info.dli_fname;
#endif
TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
}
BFP_EXPORT void* BFP_CALLTYPE BfpDynLib_GetProcAddress(BfpDynLib* lib, const char* name)
{
return dlsym((void*)lib, name);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult* outResult)
{
if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
{
switch (errno)
{
case EEXIST:
OUTRESULT(BfpFileResult_AlreadyExists);
break;
case ENOENT:
OUTRESULT(BfpFileResult_NotFound);
break;
default:
OUTRESULT(BfpFileResult_UnknownError);
break;
}
}
else
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
{
if (rmdir(path) != 0)
{
switch (errno)
{
case ENOENT:
OUTRESULT(BfpFileResult_NotFound);
break;
default:
OUTRESULT(BfpFileResult_UnknownError);
break;
}
}
else
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetCurrent(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
{
char* str = getcwd(NULL, 0);
Beefy::String path = str;
free(str);
TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_SetCurrent(const char* path, BfpFileResult* outResult)
{
if (chdir(path) != 0)
OUTRESULT(BfpFileResult_NotFound);
else
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT bool BFP_CALLTYPE BfpDirectory_Exists(const char* path)
{
struct stat statbuf = {0};
int result = stat(path, &statbuf);
if (result != 0)
return false;
return S_ISDIR(statbuf.st_mode);
}
BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sysDirKind, char* outPath, int* inOutPathLen, BfpFileResult* outResult)
{
String path = "~";
TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
}
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* inName, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult)
{
auto _DoCreate = [&](String& name)
{
int flags = 0;
int mode = 0;
int pipePairHandle = -1;
if ((createFlags & (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write)) == (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write))
flags |= O_RDWR;
else if ((createFlags & BfpFileCreateFlag_Read) != 0)
flags |= O_RDONLY;
else if ((createFlags & BfpFileCreateFlag_Write) != 0)
flags |= O_WRONLY;
if ((createFlags & BfpFileCreateFlag_Append) != 0)
flags |= O_APPEND;
if ((createFlags & BfpFileCreateFlag_Truncate) != 0)
flags |= O_TRUNC;
if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
flags |= O_NONBLOCK;
if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
{
name = "/tmp/" + name;
if ((createKind == BfpFileCreateKind_CreateAlways) ||
(createKind == BfpFileCreateKind_CreateIfNotExists))
{
for (int pass = 0; pass < 2; pass++)
{
int result = mknod(name.c_str(), S_IFIFO | 0666, 0);
if (result == 0)
break;
int err = errno;
if (err == EEXIST)
{
err = remove(name.c_str());
if (err == 0)
continue;
OUTRESULT(BfpFileResult_AlreadyExists);
return -1;
}
OUTRESULT(BfpFileResult_UnknownError);
return -1;
}
}
}
else
{
if (createKind == BfpFileCreateKind_CreateAlways)
flags |= O_CREAT;
else if (createKind == BfpFileCreateKind_CreateIfNotExists)
flags |= O_CREAT | O_EXCL;
}
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int result = open(name.c_str(), flags, mode);
//printf("BfpFile_Create %s %d %d %d\n", name.c_str(), result, flags, mode);
if (result <= 0)
{
switch (errno)
{
case EEXIST:
OUTRESULT(BfpFileResult_AlreadyExists);
break;
case ENOENT:
OUTRESULT(BfpFileResult_NotFound);
break;
case EACCES:
OUTRESULT(BfpFileResult_AccessError);
break;
default:
OUTRESULT(BfpFileResult_UnknownError);
break;
}
return -1;
}
return result;
};
2021-08-01 17:30:40 -03:00
// POSIX doesn't need the OpenAlways kind.
if (createKind == BfpFileCreateKind_OpenAlways)
createKind = BfpFileCreateKind_CreateAlways;
BfpFile* bfpFile = NULL;
int result;
if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
{
int readHandle;
int writeHandle;
String name = inName;
String altName = name + "__";
bool isCreating = false;
if ((createKind == BfpFileCreateKind_CreateAlways) ||
(createKind == BfpFileCreateKind_CreateIfNotExists))
{
readHandle = _DoCreate(name);
writeHandle = _DoCreate(altName);
isCreating = true;
}
else
{
readHandle = _DoCreate(altName);
writeHandle = _DoCreate(name);
}
if ((readHandle != -1) && (writeHandle != -1))
{
OUTRESULT(BfpFileResult_Ok);
BfpPipeInfo* pipeInfo = new BfpPipeInfo();
pipeInfo->mWriteHandle = writeHandle;
if (isCreating)
pipeInfo->mPipePath = name;
bfpFile = new BfpFile();
bfpFile->mHandle = readHandle;
bfpFile->mPipeInfo = pipeInfo;
}
else
{
if (readHandle != -1)
close(readHandle);
if (writeHandle != -1)
close(writeHandle);
return NULL;
}
}
else
{
String name = inName;
int handle = _DoCreate(name);
if (handle == -1)
return NULL;
OUTRESULT(BfpFileResult_Ok);
bfpFile = new BfpFile();
bfpFile->mHandle = handle;
}
OUTRESULT(BfpFileResult_Ok);
if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
bfpFile->mNonBlocking = true;
if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)
bfpFile->mAllowTimeout = true;
return bfpFile;
}
BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
{
int h = -1;
switch (kind)
{
case BfpFileStdKind_StdOut:
h = STDOUT_FILENO;
break;
case BfpFileStdKind_StdError:
h = STDERR_FILENO;
break;
case BfpFileStdKind_StdIn:
h = STDIN_FILENO;
break;
}
if (h == -1)
{
OUTRESULT(BfpFileResult_NotFound);
return NULL;
}
BfpFile* bfpFile = new BfpFile();
bfpFile->mHandle = h;
bfpFile->mIsStd = true;
return bfpFile;
}
2021-07-03 05:29:50 -07:00
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_GetSystemHandle(BfpFile* file)
{
return (intptr)file->mHandle;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file)
{
if ((file->mHandle != -1) && (!file->mIsStd))
close(file->mHandle);
if (file->mPipeInfo != NULL)
{
if (file->mPipeInfo->mWriteHandle != -1)
close(file->mPipeInfo->mWriteHandle);
if (!file->mPipeInfo->mPipePath.IsEmpty())
{
int worked = remove(file->mPipeInfo->mPipePath.c_str());
remove((file->mPipeInfo->mPipePath + "__").c_str());
//printf("Removing %s %d\n", file->mPipeInfo->mPipePath.c_str(), worked);
}
}
delete file;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult)
{
if (file->mHandle != -1)
{
close(file->mHandle);
file->mHandle = -1;
if (file->mPipeInfo != NULL)
{
close(file->mPipeInfo->mWriteHandle);
file->mPipeInfo->mWriteHandle = -1;
}
OUTRESULT(BfpFileResult_Ok);
}
else
OUTRESULT(BfpFileResult_UnknownError);
}
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
{
int writeHandle = file->mHandle;
if (file->mPipeInfo != NULL)
writeHandle = file->mPipeInfo->mWriteHandle;
intptr writeCount = ::write(writeHandle, buffer, size);
// if ((writeCount > 0) && (file->mIsPipe))
// {
// ::fsync(file->mHandle);
// }
if (writeCount < 0)
OUTRESULT(BfpFileResult_UnknownError);
else if (writeCount != size)
OUTRESULT(BfpFileResult_PartialData);
else
OUTRESULT(BfpFileResult_Ok);
return writeCount;
}
BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
{
if (file->mNonBlocking)
{
if (!file->mAllowTimeout)
timeoutMS = -1;
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = timeoutMS * 1000;
fd_set readFDSet;
FD_ZERO(&readFDSet);
FD_SET(file->mHandle, &readFDSet);
fd_set errorFDSet;
FD_ZERO(&errorFDSet);
FD_SET(file->mHandle, &errorFDSet);
if (select(file->mHandle + 1, &readFDSet, NULL, &errorFDSet, (timeoutMS == -1) ? NULL : &timeout) < 0)
{
OUTRESULT(BfpFileResult_Timeout);
return 0;
}
}
intptr readCount = ::read(file->mHandle, buffer, size);
if (readCount < 0)
OUTRESULT(BfpFileResult_UnknownError);
else if (readCount != size)
OUTRESULT(BfpFileResult_PartialData);
else
OUTRESULT(BfpFileResult_Ok);
return readCount;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file)
{
::fsync(file->mHandle);
}
BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file)
{
int64 oldPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
int64 size = (int64)lseek64(file->mHandle, 0, SEEK_END);
lseek64(file->mHandle, oldPos, SEEK_SET);
return (int64)size;
}
BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind)
{
int whence;
if (seekKind == BfpFileSeekKind_Absolute)
whence = SEEK_SET;
else if (seekKind == BfpFileSeekKind_Relative)
whence = SEEK_CUR;
else
whence = SEEK_END;
return lseek64(file->mHandle, offset, whence);
}
2021-08-01 17:38:30 -03:00
BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult)
{
int64 curPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
if (ftruncate64(file->mHandle, curPos) != 0)
2021-08-01 17:38:30 -03:00
{
OUTRESULT(BfpFileResult_UnknownError);
return;
}
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path)
{
struct stat statbuf = {0};
int result = stat(path, &statbuf);
if (result != 0)
return 0;
return statbuf.st_mtime;
}
BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult)
{
NOT_IMPL;
return (BfpFileAttributes)0;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Copy(const char* oldPath, const char* newPath, BfpFileCopyKind copyKind, BfpFileResult* outResult)
{
int fd_to, fd_from;
char buf[4096];
ssize_t nread;
fd_from = open(oldPath, O_RDONLY);
if (fd_from < 0)
{
OUTRESULT(BfpFileResult_NotFound);
return;
}
int flags = O_WRONLY | O_CREAT;
if (copyKind == BfpFileCopyKind_IfNotExists)
flags |= O_EXCL;
fd_to = open(newPath, flags, 0666);
if (fd_to < 0)
{
if (errno == EEXIST)
{
OUTRESULT(BfpFileResult_AlreadyExists);
goto out_error;
}
OUTRESULT(BfpFileResult_UnknownError);
goto out_error;
}
while (nread = read(fd_from, buf, sizeof buf), nread > 0)
{
char *out_ptr = buf;
ssize_t nwritten;
do {
nwritten = write(fd_to, out_ptr, nread);
if (nwritten >= 0)
{
nread -= nwritten;
out_ptr += nwritten;
}
else if (errno != EINTR)
{
OUTRESULT(BfpFileResult_UnknownError);
goto out_error;
}
} while (nread > 0);
}
if (nread == 0)
{
if (close(fd_to) < 0)
{
fd_to = -1;
OUTRESULT(BfpFileResult_UnknownError);
goto out_error;
}
close(fd_from);
/* Success! */
OUTRESULT(BfpFileResult_Ok);
return;
}
out_error:
close(fd_from);
if (fd_to >= 0)
close(fd_to);
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Rename(const char* oldPath, const char* newPath, BfpFileResult* outResult)
{
NOT_IMPL;
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_Delete(const char* path, BfpFileResult* outResult)
{
if (remove(path) != 0)
{
switch (errno)
{
case ENOENT:
OUTRESULT(BfpFileResult_NotFound);
break;
default:
OUTRESULT(BfpFileResult_UnknownError);
break;
}
}
else
OUTRESULT(BfpFileResult_Ok);
}
BFP_EXPORT bool BFP_CALLTYPE BfpFile_Exists(const char* path)
{
struct stat statbuf = {0};
int result = stat(path, &statbuf);
if (result != 0)
return false;
return !S_ISDIR(statbuf.st_mode);
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempPath(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
{
NOT_IMPL;
}
static const char cHash64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' };
static void HashEncode64(StringImpl& outStr, uint64 val)
{
for (int i = 0; i < 10; i++)
{
int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1;
if (charIdx != -1)
outStr.Append(cHash64bToChar[charIdx]);
}
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempFileName(char* outName, int* inOutNameSize, BfpFileResult* outResult)
{
static uint32 uniqueIdx = 0;
BfpSystem_InterlockedExchangeAdd32(&uniqueIdx, 1);
Beefy::HashContext ctx;
ctx.Mixin(uniqueIdx);
ctx.Mixin(getpid());
ctx.Mixin(Beefy::BFGetTickCountMicro());
uint64 hash = ctx.Finish64();
String str = "/tmp/bftmp_";
HashEncode64(str, hash);
TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
{
String str;
if (inPath[0] == '/')
{
str = inPath;
}
else
{
char* cwdPtr = getcwd(NULL, 0);
2022-03-18 18:06:14 -07:00
Beefy::String cwdPath = cwdPtr;
free(cwdPtr);
str = GetAbsPath(inPath, cwdPath);
}
TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
}
BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
{
NOT_IMPL;
}
// BfpFindFileData
struct BfpFindFileData
{
BfpFindFileFlags mFlags;
DIR* mDirStruct;
Beefy::String mWildcard;
Beefy::String mDirPath;
dirent* mDirEnt;
bool mHasStat;
struct stat mStat;
};
BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
{
Beefy::String findStr = path;
Beefy::String wildcard;
int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
if (lastSlashPos != -1)
{
wildcard = findStr.Substring(lastSlashPos + 1);
findStr = findStr.Substring(0, lastSlashPos);
}
if (wildcard == "*.*")
wildcard = "*";
DIR* dir = opendir(findStr.c_str());
if (dir == NULL)
{
OUTRESULT(BfpFileResult_NotFound);
return NULL;
}
BfpFindFileData* findData = new BfpFindFileData();
findData->mFlags = flags;
findData->mDirPath = findStr;
findData->mDirStruct = dir;
findData->mWildcard = wildcard;
findData->mHasStat = false;
findData->mDirEnt = NULL;
if (!BfpFindFileData_FindNextFile(findData))
{
OUTRESULT(BfpFileResult_NoResults);
2022-03-18 18:06:14 -07:00
closedir(findData->mDirStruct);
delete findData;
return NULL;
}
OUTRESULT(BfpFileResult_Ok);
return findData;
}
static void GetStat(BfpFindFileData* findData)
{
if (findData->mHasStat)
return;
Beefy::String filePath = findData->mDirPath + "/" + findData->mDirEnt->d_name;
findData->mStat = { 0 };
int result = stat(filePath.c_str(), &findData->mStat);
findData->mHasStat = true;
}
static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
{
bool isDir = false;
if (findData->mDirEnt->d_type == DT_DIR)
isDir = true;
if (findData->mDirEnt->d_type == DT_LNK)
{
GetStat(findData);
isDir = S_ISDIR(findData->mStat.st_mode);
}
if (isDir)
{
if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)
return false;
if ((strcmp(findData->mDirEnt->d_name, ".") == 0) || (strcmp(findData->mDirEnt->d_name, "..") == 0))
return false;
}
else
{
if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
return false;
}
if (!wc_match(findData->mWildcard.c_str(), findData->mDirEnt->d_name))
return false;
return true;
}
BFP_EXPORT bool BFP_CALLTYPE BfpFindFileData_FindNextFile(BfpFindFileData* findData)
{
while (true)
{
findData->mHasStat = false;
findData->mDirEnt = readdir(findData->mDirStruct);
if (findData->mDirEnt == NULL)
return false;
if (BfpFindFileData_CheckFilter(findData))
break;
}
return true;
}
BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_GetFileName(BfpFindFileData* findData, char* outName, int* inOutNameSize, BfpFileResult* outResult)
{
Beefy::String name = findData->mDirEnt->d_name;
TryStringOut(name, outName, inOutNameSize, (BfpResult*)outResult);
}
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData)
{
GetStat(findData);
#ifdef BF_PLATFORM_DARWIN
2019-10-29 05:01:04 -07:00
return BfpToTimeStamp(findData->mStat.st_mtimespec);
#else
return BfpToTimeStamp(findData->mStat.st_mtim);
2019-10-29 05:01:04 -07:00
#endif
}
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Created(BfpFindFileData* findData)
{
GetStat(findData);
#ifdef BF_PLATFORM_DARWIN
2019-10-29 05:01:04 -07:00
return BfpToTimeStamp(findData->mStat.st_ctimespec);
#else
return BfpToTimeStamp(findData->mStat.st_ctim);
2019-10-29 05:01:04 -07:00
#endif
}
BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Access(BfpFindFileData* findData)
{
GetStat(findData);
2019-10-29 05:01:04 -07:00
#ifdef BF_PLATFORM_DARWIN
return BfpToTimeStamp(findData->mStat.st_atimespec);
#else
return BfpToTimeStamp(findData->mStat.st_atim);
2019-10-29 05:01:04 -07:00
#endif
}
BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFindFileData_GetFileAttributes(BfpFindFileData* findData)
{
BfpFileAttributes flags = BfpFileAttribute_None;
if (S_ISDIR(findData->mStat.st_mode))
flags = (BfpFileAttributes)(flags | BfpFileAttribute_Directory);
if (S_ISREG(findData->mStat.st_mode))
flags = (BfpFileAttributes)(flags | BfpFileAttribute_Normal);
else if (!S_ISLNK(findData->mStat.st_mode))
flags = (BfpFileAttributes)(flags | BfpFileAttribute_Device);
if ((findData->mStat.st_mode & S_IRUSR) == 0)
flags = (BfpFileAttributes)(flags | BfpFileAttribute_ReadOnly);
return flags;
}
2020-09-11 16:39:17 -07:00
BFP_EXPORT int64 BFP_CALLTYPE BfpFindFileData_GetFileSize(BfpFindFileData* findData)
{
return (int64)findData->mStat.st_size;
}
BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_Release(BfpFindFileData* findData)
{
2022-03-18 18:06:14 -07:00
closedir(findData->mDirStruct);
delete findData;
}
BFP_EXPORT int BFP_CALLTYPE BfpStack_CaptureBackTrace(int framesToSkip, intptr* outFrames, int wantFrameCount)
{
//
return 0;
}
BFP_EXPORT void BFP_CALLTYPE BfpOutput_DebugString(const char* str)
{
BFP_PRINTF("%s", str);
fflush(stdout);
}
//////////////////////////////////////////////////////////////////////////
void Beefy::BFFatalError(const StringImpl& message, const StringImpl& file, int line)
{
String error;
error += "ERROR: ";
error += message;
error += " in ";
error += file;
error += StrFormat(" line %d", line);
BfpSystem_FatalError(error.c_str(), "FATAL ERROR");
}