27 #if defined(_MSC_VER) || defined(__MINGW32__)
28 #define DR_NO_MSVC_COMPAT
32 #define DR_INLINE static __inline
34 #define DR_INLINE static inline
43 #ifndef DR_NO_MSVC_COMPAT
47 #ifndef DR_SIZED_TYPES_DEFINED
48 #define DR_SIZED_TYPES_DEFINED
49 #if defined(_MSC_VER) && _MSC_VER < 1600
76 #define STRINGIFY(x) #x
77 #define TOSTRING(x) STRINGIFY(x)
92 #define UNUSED(x) ((void)(x))
100 #define dr_min(x, y) (((x) < (y)) ? (x) : (y))
104 #define dr_max(x, y) (((x) > (y)) ? (x) : (y))
108 #define dr_clamp(x, low, high) (dr_max(low, dr_min(x, high)))
112 #define dr_round_up(x, multiple) ((((x) + ((multiple) - 1)) / (multiple)) * (multiple))
115 #ifndef dr_round_up_signed
116 #define dr_round_up_signed(x, multiple) ((((x) + (((x) >= 0)*((multiple) - 1))) / (multiple)) * (multiple))
122 value = (value >> 1) | value;
123 value = (value >> 2) | value;
124 value = (value >> 4) | value;
125 value = (value >> 8) | value;
126 value = (value >> 16) | value;
131 #define dr_abs(x) (((x) < 0) ? (-(x)) : (x))
138 int dr_strcpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src);
141 int dr_strncpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src,
size_t count);
144 int dr_strcat_s(
char* dst,
size_t dstSizeInBytes,
const char* src);
147 int dr_strncat_s(
char* dst,
size_t dstSizeInBytes,
const char* src,
size_t count);
150 int dr_itoa_s(
int value,
char* dst,
size_t dstSizeInBytes,
int radix);
152 #ifndef DR_NO_MSVC_COMPAT
154 #define _TRUNCATE ((size_t)-1)
180 return strcasecmp(string1, string2);
186 return dr_itoa_s(value, dst, dstSizeInBytes, radix);
204 static const char*
dr_rtrim(
const char* str);
216 size_t dr_copy_line(
const char* str,
char* lineOut,
size_t lineOutSize);
219 char*
dr_string_replace(
const char* src,
const char* query,
const char* replacement);
243 if (utf32 < 0xD800 || (utf32 >= 0xE000 && utf32 <= 0xFFFF))
245 utf16[0] = (
unsigned short)utf32;
251 if (utf32 >= 0x10000 && utf32 <= 0x10FFFF)
253 utf16[0] = (
unsigned short)(0xD7C0 + (
unsigned short)(utf32 >> 10));
254 utf16[1] = (
unsigned short)(0xDC00 + (
unsigned short)(utf32 & 0x3FF));
274 if (utf16[0] < 0xD800 || utf16[0] > 0xDFFF)
280 if ((utf16[0] & 0xFC00) == 0xD800 && (utf16[1] & 0xFC00) == 0xDC00)
282 return ((
unsigned int)utf16[0] << 10) + utf16[1] - 0x35FDC00;
295 unsigned short utf16[2];
305 #ifndef DRUTIL_NO_ALIGNED_MALLOC
308 #if defined(_WIN32) || defined(_WIN64)
309 return _aligned_malloc(size, alignment);
312 if (posix_memalign(&pResult, alignment, size) == 0) {
322 #if defined(_WIN32) || defined(_WIN64)
328 #endif // !DRUTIL_NO_ALIGNED_MALLOC
379 const char*
dr_next_token(
const char* tokens,
char* tokenOut,
size_t tokenOutSize);
426 FILE*
dr_fopen(
const char* fileName,
const char* openMode);
491 void dr_win32_make_dpi_aware();
495 void dr_win32_get_base_dpi(
int* pDPIXOut,
int* pDPIYOut);
498 void dr_win32_get_system_dpi(
int* pDPIXOut,
int* pDPIYOut);
506 void dr_win32_get_monitor_dpi(
int monitor,
int* pDPIXOut,
int* pDPIYOut);
512 int dr_win32_get_monitor_count();
623 void dr_sleep(
unsigned int milliseconds);
750 #define dr_zero_object(pObject) memset(pObject, 0, sizeof(*pObject));
765 #define NO_COPY(classname) \
767 classname(const classname &); \
768 classname & operator=(const classname &);
771 #ifndef DR_NO_MSVC_COMPAT
775 template <
size_t dstSizeInBytes>
776 int strcpy_s(
char (&dst)[dstSizeInBytes],
const char* src)
778 return strcpy_s(dst, dstSizeInBytes, src);
781 template <
size_t dstSizeInBytes>
782 int strncpy_s(
char (&dst)[dstSizeInBytes],
const char* src,
size_t count)
787 template <
size_t dstSizeInBytes>
788 int strcat_s(
char (&dst)[dstSizeInBytes],
const char* src)
790 return strcat_s(dst, dstSizeInBytes, src);
793 template <
size_t dstSizeInBytes>
794 int strncat_s(
char (&dst)[dstSizeInBytes],
const char* src,
size_t count)
820 #ifdef DR_IMPLEMENTATION
831 #include <sys/syscall.h>
832 #include <sys/types.h>
835 #include <semaphore.h>
839 int dr_strcpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src)
844 if (dstSizeInBytes == 0) {
853 for (i = 0; i < dstSizeInBytes && src[i] !=
'\0'; ++i) {
857 if (i < dstSizeInBytes) {
866 int dr_strncpy_s(
char* dst,
size_t dstSizeInBytes,
const char* src,
size_t count)
871 if (dstSizeInBytes == 0) {
879 size_t maxcount =
count;
880 if (
count == ((
size_t)-1) ||
count >= dstSizeInBytes) {
881 maxcount = dstSizeInBytes - 1;
885 for (i = 0; i < maxcount && src[i] !=
'\0'; ++i) {
889 if (src[i] ==
'\0' || i ==
count ||
count == ((
size_t)-1)) {
898 int dr_strcat_s(
char* dst,
size_t dstSizeInBytes,
const char* src)
903 if (dstSizeInBytes == 0) {
913 while (dstSizeInBytes > 0 && dst[0] !=
'\0') {
918 if (dstSizeInBytes == 0) {
923 while (dstSizeInBytes > 0 && src[0] !=
'\0') {
928 if (dstSizeInBytes > 0) {
938 int dr_strncat_s(
char* dst,
size_t dstSizeInBytes,
const char* src,
size_t count)
943 if (dstSizeInBytes == 0) {
952 while (dstSizeInBytes > 0 && dst[0] !=
'\0') {
957 if (dstSizeInBytes == 0) {
962 if (
count == ((
size_t)-1)) {
963 count = dstSizeInBytes - 1;
966 while (dstSizeInBytes > 0 && src[0] !=
'\0' &&
count > 0)
973 if (dstSizeInBytes > 0) {
983 int dr_itoa_s(
int value,
char* dst,
size_t dstSizeInBytes,
int radix)
985 if (dst ==
NULL || dstSizeInBytes == 0) {
988 if (radix < 2 || radix > 36) {
993 int sign = (value < 0 && radix == 10) ? -1 : 1;
1005 int remainder = valueU % radix;
1006 if (remainder > 9) {
1007 *dstEnd = (char)((remainder - 10) +
'a');
1009 *dstEnd = (char)(remainder +
'0');
1013 dstSizeInBytes -= 1;
1015 }
while (dstSizeInBytes > 0 && valueU > 0);
1017 if (dstSizeInBytes == 0) {
1024 dstSizeInBytes -= 1;
1027 if (dstSizeInBytes == 0) {
1037 while (dst < dstEnd) {
1054 return utf32 ==
' ' || utf32 ==
'\t' || utf32 ==
'\n' || utf32 ==
'\v' || utf32 ==
'\f' || utf32 ==
'\r';
1062 while (src[0] !=
'\0')
1082 while (str[0] !=
'\0' && !(str[0] !=
' ' && str[0] !=
'\t' && str[0] !=
'\n' && str[0] !=
'\v' && str[0] !=
'\f' && str[0] !=
'\r')) {
1095 while (str[0] !=
'\0' && (str[0] !=
' ' && str[0] !=
'\t' && str[0] !=
'\n' && str[0] !=
'\v' && str[0] !=
'\f' && str[0] !=
'\r')) {
1102 const char*
dr_rtrim(
const char* str)
1108 const char* rstr = str;
1109 while (str[0] !=
'\0') {
1132 memmove(str, lstr, rstr-lstr);
1135 str[rstr-lstr] =
'\0';
1144 while (str[0] !=
'\0' && (str[0] !=
'\n' && !(str[0] ==
'\r' && str[1] ==
'\n'))) {
1148 if (str[0] ==
'\0') {
1152 if (str[0] ==
'\r') {
1159 size_t dr_copy_line(
const char* str,
char* lineOut,
size_t lineOutSize)
1170 while (lineOutSize > 0 && str[0] !=
'\0' && (str[0] !=
'\n' && !(str[0] ==
'\r' && str[1] ==
'\n'))) {
1171 *lineOut++ = *str++;
1176 if (lineOutSize == 0) {
1184 char*
dr_string_replace(
const char* src,
const char* query,
const char* replacement)
1188 if (src ==
NULL || query ==
NULL) {
1192 if (replacement ==
NULL) {
1196 int queryLen = (int)strlen(query);
1197 int replacementLen = (int)strlen(replacement);
1199 size_t replacementCount = 0;
1201 const char* temp = src;
1203 temp = strstr(temp, query);
1209 replacementCount += 1;
1213 char* result = (
char*)malloc(strlen(src) + (replacementLen - queryLen)*replacementCount + 1);
1214 if (result ==
NULL) {
1218 char* runningResult = result;
1219 for (
size_t i = 0; i < replacementCount; ++i) {
1220 size_t len = strstr(src, query) - src;
1221 for (
size_t j = 0; j < len; ++j) {
1222 runningResult[j] = src[j];
1225 runningResult += len;
1226 for (
int j = 0; j < replacementLen; ++j) {
1227 runningResult[j] = replacement[j];
1230 runningResult += replacementLen;
1231 src += len + queryLen;
1235 strcpy_s(runningResult, strlen(src)+1, src);
1260 if (onRead ==
NULL) {
1265 size_t chunkSize = 0;
1267 unsigned int currentLine = 1;
1276 chunkSize = onRead(pUserData, pChunk,
sizeof(pChunk));
1277 if (chunkSize == 0) {
1282 char* pChunkEnd = pChunk + chunkSize;
1285 if (moveToNextLineBeforeProcessing)
1288 while (pC < pChunkEnd && pC[0] !=
'\n') {
1292 if (pC == pChunkEnd) {
1294 moveToNextLineBeforeProcessing =
DR_TRUE;
1300 moveToNextLineBeforeProcessing =
DR_FALSE;
1303 if (skipWhitespaceBeforeProcessing)
1305 while (pC < pChunkEnd && (pC[0] ==
' ' || pC[0] ==
'\t' || pC[0] ==
'\r')) {
1309 if (pC == pChunkEnd) {
1311 skipWhitespaceBeforeProcessing =
DR_TRUE;
1315 skipWhitespaceBeforeProcessing =
DR_FALSE;
1320 while (pC < pChunkEnd)
1325 while (pC < pChunkEnd && (pC[0] ==
' ' || pC[0] ==
'\t' || pC[0] ==
'\r')) {
1329 if (pC == pChunkEnd) {
1331 skipWhitespaceBeforeProcessing =
DR_TRUE;
1335 if (pC[0] ==
'\n') {
1344 goto move_to_next_line;
1348 while (pC < pChunkEnd && pC[0] !=
' ' && pC[0] !=
'\t' && pC[0] !=
'\r' && pC[0] !=
'\n' && pC[0] !=
'#') {
1352 if (pC == pChunkEnd)
1355 if (chunkSize ==
sizeof(pChunk))
1357 size_t lineSizeSoFar = pC - pK;
1358 memmove(pChunk, pK, lineSizeSoFar);
1360 chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar,
sizeof(pChunk) - lineSizeSoFar);
1361 pChunkEnd = pChunk + chunkSize;
1364 pC = pChunk + lineSizeSoFar;
1365 while (pC < pChunkEnd && pC[0] !=
' ' && pC[0] !=
'\t' && pC[0] !=
'\r' && pC[0] !=
'\n' && pC[0] !=
'#') {
1370 if (pC == pChunkEnd) {
1371 if (chunkSize ==
sizeof(pChunk)) {
1373 onError(pUserData,
"Line is too long. A single line cannot exceed 4KB.", currentLine);
1376 goto move_to_next_line;
1381 onPair(pUserData, pK,
NULL);
1394 while (pC < pChunkEnd && (pC[0] ==
' ' || pC[0] ==
'\t' || pC[0] ==
'\r')) {
1398 if (pC == pChunkEnd)
1401 if (chunkSize ==
sizeof(pChunk))
1403 size_t lineSizeSoFar = pC - pK;
1404 memmove(pChunk, pK, lineSizeSoFar);
1406 chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar,
sizeof(pChunk) - lineSizeSoFar);
1407 pChunkEnd = pChunk + chunkSize;
1409 pKEnd = pChunk + (pKEnd - pK);
1411 pC = pChunk + lineSizeSoFar;
1412 while (pC < pChunkEnd && (pC[0] ==
' ' || pC[0] ==
'\t' || pC[0] ==
'\r')) {
1417 if (pC == pChunkEnd) {
1418 if (chunkSize ==
sizeof(pChunk)) {
1420 onError(pUserData,
"Line is too long. A single line cannot exceed 4KB.", currentLine);
1423 goto move_to_next_line;
1428 onPair(pUserData, pK,
NULL);
1436 if (pC[0] ==
'\n') {
1440 onPair(pUserData, pK,
NULL);
1452 onPair(pUserData, pK,
NULL);
1455 goto move_to_next_line;
1462 while (pC < pChunkEnd && pC[0] !=
'\n' && pC[0] !=
'#') {
1463 if (pC[0] !=
' ' && pC[0] !=
'\t' && pC[0] !=
'\r') {
1470 if (pC == pChunkEnd)
1473 if (chunkSize ==
sizeof(pChunk))
1475 size_t lineSizeSoFar = pC - pK;
1476 memmove(pChunk, pK, lineSizeSoFar);
1478 chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar,
sizeof(pChunk) - lineSizeSoFar);
1479 pChunkEnd = pChunk + chunkSize;
1481 pVEnd = pChunk + (pVEnd - pK);
1482 pKEnd = pChunk + (pKEnd - pK);
1483 pV = pChunk + (pV - pK);
1485 pC = pChunk + lineSizeSoFar;
1486 while (pC < pChunkEnd && pC[0] !=
'\n' && pC[0] !=
'#') {
1487 if (pC[0] !=
' ' && pC[0] !=
'\t' && pC[0] !=
'\r') {
1495 if (pC == pChunkEnd) {
1496 if (chunkSize ==
sizeof(pChunk)) {
1498 onError(pUserData,
"Line is too long. A single line cannot exceed 4KB.", currentLine);
1501 goto move_to_next_line;
1513 onPair(pUserData, pK, pV);
1526 goto move_to_next_line;
1538 void* pOriginalUserData;
1539 } dr_parse_key_value_pairs_from_file_data;
1541 size_t dr_parse_key_value_pairs_from_file__on_read(
void* pUserData,
void* pDataOut,
size_t bytesToRead)
1543 dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1544 assert(pData !=
NULL);
1546 return fread(pDataOut, 1, bytesToRead, pData->pFile);
1549 void dr_parse_key_value_pairs_from_file__on_pair(
void* pUserData,
const char* key,
const char* value)
1551 dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1552 assert(pData !=
NULL);
1554 pData->onPair(pData->pOriginalUserData, key, value);
1557 void dr_parse_key_value_pairs_from_file__on_error(
void* pUserData,
const char* message,
unsigned int line)
1559 dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1560 assert(pData !=
NULL);
1562 pData->onError(pData->pOriginalUserData, message, line);
1567 dr_parse_key_value_pairs_from_file_data data;
1568 data.pFile =
dr_fopen(filePath,
"rb");
1569 if (data.pFile ==
NULL) {
1570 if (onError) onError(pUserData,
"Could not open file.", 0);
1574 data.onPair = onPair;
1575 data.onError = onError;
1576 data.pOriginalUserData = pUserData;
1577 dr_parse_key_value_pairs(dr_parse_key_value_pairs_from_file__on_read, dr_parse_key_value_pairs_from_file__on_pair, dr_parse_key_value_pairs_from_file__on_error, &data);
1587 const char*
dr_next_token(
const char* tokens,
char* tokenOut,
size_t tokenOutSize)
1589 if (tokenOut) tokenOut[0] =
'\0';
1591 if (tokens ==
NULL) {
1596 while (tokens[0] !=
'\0' && !(tokens[0] !=
' ' && tokens[0] !=
'\t' && tokens[0] !=
'\n' && tokens[0] !=
'\v' && tokens[0] !=
'\f' && tokens[0] !=
'\r')) {
1600 if (tokens[0] ==
'\0') {
1605 const char* strBeg = tokens;
1606 const char* strEnd = strBeg;
1608 if (strEnd[0] ==
'\"')
1617 char prevChar =
'\0';
1618 while (strEnd[0] !=
'\0' && (strEnd[0] !=
'\"' || prevChar ==
'\\'))
1620 prevChar = strEnd[0];
1627 while (strEnd[0] !=
'\0' && (strEnd[0] !=
' ' && strEnd[0] !=
'\t' && strEnd[0] !=
'\n' && strEnd[0] !=
'\v' && strEnd[0] !=
'\f' && strEnd[0] !=
'\r')) {
1637 while (tokenOutSize > 1 && strBeg < strEnd)
1639 if (strBeg[0] ==
'\\' && strBeg[1] ==
'\"' && strBeg < strEnd) {
1643 *tokenOut++ = *strBeg++;
1648 if (tokenOutSize > 0) {
1654 if (strEnd[0] ==
'\"') {
1668 #if defined(_MSC_VER)
1669 #pragma warning(push)
1670 #pragma warning(disable:4091) // 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared
1673 #if defined(_MSC_VER)
1674 #pragma warning(pop)
1680 if (pathOut ==
NULL || pathOutSize == 0) {
1684 DWORD length = GetModuleFileNameA(
NULL, pathOut, (DWORD)pathOutSize);
1691 if (length == pathOutSize) {
1692 pathOut[length - 1] =
'\0';
1696 while (pathOut[0] !=
'\0') {
1697 if (pathOut[0] ==
'\\') {
1711 if (pathOutSize >= MAX_PATH)
1713 SHGetFolderPathA(
NULL, CSIDL_LOCAL_APPDATA,
NULL, 0, pathOut);
1717 char pathOutTemp[MAX_PATH];
1718 SHGetFolderPathA(
NULL, CSIDL_LOCAL_APPDATA,
NULL, 0, pathOutTemp);
1720 if (
strcpy_s(pathOut, pathOutSize, pathOutTemp) != 0) {
1727 while (pathOut[0] !=
'\0') {
1728 if (pathOut[0] ==
'\\') {
1745 DWORD result = GetCurrentDirectoryA((DWORD)pathOutSize, pathOut);
1755 return SetCurrentDirectoryA(path) != 0;
1759 #include <sys/types.h>
1764 if (pathOut ==
NULL || pathOutSize == 0) {
1768 ssize_t length = readlink(
"/proc/self/exe", pathOut, pathOutSize);
1774 if ((
size_t)length == pathOutSize) {
1775 pathOut[length - 1] =
'\0';
1777 pathOut[length] =
'\0';
1785 const char* configdir = getenv(
"XDG_CONFIG_HOME");
1786 if (configdir !=
NULL)
1788 return strcpy_s(pathOut, pathOutSize, configdir) == 0;
1792 const char* homedir = getenv(
"HOME");
1793 if (homedir ==
NULL) {
1794 homedir = getpwuid(getuid())->pw_dir;
1797 if (homedir !=
NULL)
1799 if (
strcpy_s(pathOut, pathOutSize, homedir) == 0)
1801 size_t homedirLength = strlen(homedir);
1802 pathOut += homedirLength;
1803 pathOutSize -= homedirLength;
1805 if (pathOutSize > 0)
1811 return strcpy_s(pathOut, pathOutSize,
".config") == 0;
1822 return strcpy_s(pathOut, pathOutSize,
"var/log") == 0;
1827 return getcwd(pathOut, pathOutSize);
1832 return chdir(path) == 0;
1843 char* lastSlash = pathOut;
1844 while (pathOut[0] !=
'\0') {
1845 if (pathOut[0] ==
'/' || pathOut[0] ==
'\\') {
1846 lastSlash = pathOut;
1851 lastSlash[0] =
'\0';
1861 #include <sys/types.h>
1862 #include <sys/stat.h>
1866 FILE*
dr_fopen(
const char* filePath,
const char* openMode)
1870 if (fopen_s(&pFile, filePath, openMode) != 0) {
1874 pFile = fopen(filePath, openMode);
1875 if (pFile ==
NULL) {
1885 if (fileName ==
NULL) {
1889 DWORD dwCreationDisposition;
1891 dwCreationDisposition = CREATE_NEW;
1893 dwCreationDisposition = CREATE_ALWAYS;
1896 HANDLE hFile = CreateFileA(fileName, FILE_GENERIC_WRITE, 0,
NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL,
NULL);
1897 if (hFile == INVALID_HANDLE_VALUE) {
1904 int flags = O_WRONLY | O_CREAT;
1910 int fd = open(fileName, flags, 0666);
1922 if (pFileSizeOut) *pFileSizeOut = 0;
1924 if (filePath ==
NULL) {
1930 FILE* pFile =
dr_fopen(filePath,
"rb");
1931 if (pFile ==
NULL) {
1935 fseek(pFile, 0, SEEK_END);
1937 fseek(pFile, 0, SEEK_SET);
1939 if (fileSize + extraBytes > SIZE_MAX) {
1944 void* pFileData = malloc((
size_t)fileSize + extraBytes);
1945 if (pFileData ==
NULL) {
1950 size_t bytesRead = fread(pFileData, 1, (
size_t)fileSize, pFile);
1951 if (bytesRead != fileSize) {
1959 if (pFileSizeOut) *pFileSizeOut = (size_t)fileSize;
1970 if (pFileSizeOut) *pFileSizeOut = 0;
1974 if (pFileData ==
NULL) {
1978 pFileData[fileSize] =
'\0';
1980 if (pFileSizeOut) *pFileSizeOut = fileSize;
1986 if (filePath ==
NULL) {
1992 FILE* pFile =
dr_fopen(filePath,
"wb");
1993 if (pFile ==
NULL) {
1997 if (pData !=
NULL && dataSize > 0) {
1998 fwrite(pData, 1, dataSize, pFile);
2016 free(valueReturnedByOpenAndReadFile);
2021 if (filePath ==
NULL) {
2026 DWORD attributes = GetFileAttributesA(filePath);
2027 return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2030 if (stat(filePath, &info) != 0) {
2034 return (info.st_mode & S_IFDIR) == 0;
2040 if (directoryPath ==
NULL) {
2045 DWORD attributes = GetFileAttributesA(directoryPath);
2046 return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2049 if (stat(directoryPath, &info) != 0) {
2053 return (info.st_mode & S_IFDIR) != 0;
2059 if (oldPath ==
NULL || newPath ==
NULL) {
2064 return MoveFileExA(oldPath, newPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) != 0;
2066 return rename(oldPath, newPath) == 0;
2072 if (srcPath ==
NULL || dstPath ==
NULL) {
2077 return CopyFileA(srcPath, dstPath, failIfExists) != 0;
2079 int fdSrc = open(srcPath, O_RDONLY, 0666);
2084 int fdDst = open(dstPath, O_WRONLY | O_TRUNC | O_CREAT | ((failIfExists) ? O_EXCL : 0), 0666);
2092 if (fstat(fdSrc, &info) == 0) {
2093 char buffer[BUFSIZ];
2095 while ((bytesRead = read(fdSrc, buffer,
sizeof(buffer))) > 0) {
2096 if (write(fdDst, buffer, bytesRead) != bytesRead) {
2109 chmod(dstPath, info.st_mode & 07777);
2117 if (filePath ==
NULL || filePath[0] ==
'\0') {
2122 DWORD attributes = GetFileAttributesA(filePath);
2123 return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_READONLY) != 0;
2125 return access(filePath, W_OK) == -1;
2131 if (filePath ==
NULL || filePath[0] ==
'\0') {
2136 HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0,
NULL);
2137 if (hFile == INVALID_HANDLE_VALUE) {
2142 BOOL wasSuccessful = GetFileTime(hFile,
NULL,
NULL, &fileTime);
2145 if (!wasSuccessful) {
2149 ULARGE_INTEGER result;
2150 result.HighPart = fileTime.dwHighDateTime;
2151 result.LowPart = fileTime.dwLowDateTime;
2152 return result.QuadPart;
2155 if (stat(filePath, &info) != 0) {
2159 return info.st_mtime;
2165 if (filePath ==
NULL) {
2170 return DeleteFileA(filePath) != 0;
2172 return remove(filePath) == 0;
2178 if (directoryPath ==
NULL) {
2183 return CreateDirectoryA(directoryPath,
NULL) != 0;
2185 return mkdir(directoryPath, 0777) == 0;
2191 if (directoryPath ==
NULL || directoryPath[0] ==
'\0') {
2196 char runningPath[4096];
2197 memset(runningPath, 0,
sizeof(runningPath));
2201 if (i >=
sizeof(runningPath)-1) {
2205 if (directoryPath[0] ==
'\0' || directoryPath[0] ==
'/' || directoryPath[0] ==
'\\') {
2206 if (runningPath[0] !=
'\0' && !(runningPath[1] ==
':' && runningPath[2] ==
'\0')) {
2215 runningPath[i++] =
'/';
2216 runningPath[i] =
'\0';
2218 if (directoryPath[0] ==
'\0') {
2222 runningPath[i++] = directoryPath[0];
2234 char searchQuery[MAX_PATH];
2235 strcpy_s(searchQuery,
sizeof(searchQuery), directory);
2237 unsigned int searchQueryLength = (
unsigned int)strlen(searchQuery);
2238 if (searchQueryLength >= MAX_PATH - 3) {
2242 searchQuery[searchQueryLength + 0] =
'\\';
2243 searchQuery[searchQueryLength + 1] =
'*';
2244 searchQuery[searchQueryLength + 2] =
'\0';
2246 WIN32_FIND_DATAA ffd;
2247 HANDLE hFind = FindFirstFileA(searchQuery, &ffd);
2248 if (hFind == INVALID_HANDLE_VALUE) {
2255 if (strcmp(ffd.cFileName,
".") == 0 || strcmp(ffd.cFileName,
"..") == 0) {
2259 char filePath[MAX_PATH];
2260 strcpy_s(filePath,
sizeof(filePath), directory);
2261 strcat_s(filePath,
sizeof(filePath),
"/");
2262 strcat_s(filePath,
sizeof(filePath), ffd.cFileName);
2264 if (!proc(filePath, pUserData)) {
2268 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2276 }
while (FindNextFileA(hFind, &ffd));
2280 DIR* dir = opendir(directory);
2285 struct dirent* info =
NULL;
2286 while ((info = readdir(dir)) !=
NULL)
2289 if (strcmp(info->d_name,
".") == 0 || strcmp(info->d_name,
"..") == 0) {
2293 char filePath[4096];
2294 strcpy_s(filePath,
sizeof(filePath), directory);
2295 strcat_s(filePath,
sizeof(filePath),
"/");
2296 strcat_s(filePath,
sizeof(filePath), info->d_name);
2298 struct stat fileinfo;
2299 if (stat(filePath, &fileinfo) != 0) {
2303 if (!proc(filePath, pUserData)) {
2307 if (fileinfo.st_mode & S_IFDIR) {
2327 #if defined(_WIN32) || defined(_WIN64)
2329 typedef enum PROCESS_DPI_AWARENESS {
2330 PROCESS_DPI_UNAWARE = 0,
2331 PROCESS_SYSTEM_DPI_AWARE = 1,
2332 PROCESS_PER_MONITOR_DPI_AWARE = 2
2333 } PROCESS_DPI_AWARENESS;
2335 typedef enum MONITOR_DPI_TYPE {
2336 MDT_EFFECTIVE_DPI = 0,
2337 MDT_ANGULAR_DPI = 1,
2339 MDT_DEFAULT = MDT_EFFECTIVE_DPI
2342 typedef BOOL (__stdcall * PFN_SetProcessDPIAware) (void);
2343 typedef HRESULT (__stdcall * PFN_SetProcessDpiAwareness) (PROCESS_DPI_AWARENESS);
2344 typedef HRESULT (__stdcall * PFN_GetDpiForMonitor) (HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
2346 void dr_win32_make_dpi_aware()
2352 HMODULE hSHCoreDLL = LoadLibraryW(
L"shcore.dll");
2353 if (hSHCoreDLL !=
NULL)
2355 PFN_SetProcessDpiAwareness _SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness)GetProcAddress(hSHCoreDLL,
"SetProcessDpiAwareness");
2356 if (_SetProcessDpiAwareness !=
NULL)
2358 if (_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK)
2360 fallBackToDiscouragedAPI =
DR_FALSE;
2365 fallBackToDiscouragedAPI =
DR_FALSE;
2368 FreeLibrary(hSHCoreDLL);
2372 fallBackToDiscouragedAPI =
DR_FALSE;
2376 if (fallBackToDiscouragedAPI)
2378 HMODULE hUser32DLL = LoadLibraryW(
L"user32.dll");
2379 if (hUser32DLL !=
NULL)
2381 PFN_SetProcessDPIAware _SetProcessDPIAware = (PFN_SetProcessDPIAware)GetProcAddress(hUser32DLL,
"SetProcessDPIAware");
2382 if (_SetProcessDPIAware !=
NULL) {
2383 _SetProcessDPIAware();
2386 FreeLibrary(hUser32DLL);
2391 void dr_win32_get_base_dpi(
int* pDPIXOut,
int* pDPIYOut)
2393 if (pDPIXOut !=
NULL) {
2397 if (pDPIYOut !=
NULL) {
2402 void dr_win32_get_system_dpi(
int* pDPIXOut,
int* pDPIYOut)
2404 if (pDPIXOut !=
NULL) {
2405 *pDPIXOut = GetDeviceCaps(GetDC(
NULL), LOGPIXELSX);
2408 if (pDPIYOut !=
NULL) {
2409 *pDPIYOut = GetDeviceCaps(GetDC(
NULL), LOGPIXELSY);
2420 PFN_GetDpiForMonitor _GetDpiForMonitor;
2422 } win32_get_monitor_dpi_data;
2424 static BOOL
CALLBACK win32_get_monitor_dpi_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
2429 win32_get_monitor_dpi_data* pData = (win32_get_monitor_dpi_data*)dwData;
2430 if (pData->monitorIndex == pData->i)
2434 if (pData->_GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
2436 pData->dpiX = (int)dpiX;
2437 pData->dpiY = (int)dpiY;
2441 dr_win32_get_system_dpi(&pData->dpiX, &pData->dpiY);
2451 void dr_win32_get_monitor_dpi(
int monitor,
int* pDPIXOut,
int* pDPIYOut)
2454 HMODULE hSHCoreDLL = LoadLibraryW(
L"shcore.dll");
2455 if (hSHCoreDLL ==
NULL) {
2456 dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
2460 PFN_GetDpiForMonitor _GetDpiForMonitor = (PFN_GetDpiForMonitor)GetProcAddress(hSHCoreDLL,
"GetDpiForMonitor");
2461 if (_GetDpiForMonitor ==
NULL) {
2462 dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
2463 FreeLibrary(hSHCoreDLL);
2468 win32_get_monitor_dpi_data data;
2469 data.monitorIndex = monitor;
2473 data._GetDpiForMonitor = _GetDpiForMonitor;
2474 EnumDisplayMonitors(
NULL,
NULL, win32_get_monitor_dpi_callback, (LPARAM)&data);
2477 *pDPIXOut = data.dpiX;
2481 *pDPIYOut = data.dpiY;
2485 FreeLibrary(hSHCoreDLL);
2489 static BOOL
CALLBACK win32_get_monitor_count_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
2495 int *
count = (
int*)dwData;
2501 int dr_win32_get_monitor_count()
2504 if (EnumDisplayMonitors(
NULL,
NULL, win32_get_monitor_count_callback, (LPARAM)&
count)) {
2523 #if defined(_MSC_VER)
2525 localtime_s(&local, &t);
2526 strftime(strOut, strOutSize,
"%x %H:%M:%S", &local);
2528 struct tm *local = localtime(&t);
2529 strftime(strOut, strOutSize,
"%x %H:%M:%S", local);
2535 #if defined(_MSC_VER)
2537 localtime_s(&local, &t);
2538 strftime(strOut, strOutSize,
"%Y%m%d", &local);
2540 struct tm *local = localtime(&t);
2541 strftime(strOut, strOutSize,
"%Y%m%d", local);
2556 char* win32_payload;
2562 } dr_cmdline_iterator;
2564 dr_cmdline_iterator dr_cmdline_begin(
dr_cmdline* pCmdLine)
2566 dr_cmdline_iterator i;
2567 i.pCmdLine = pCmdLine;
2569 i.win32_payload =
NULL;
2575 size_t length = strlen(pCmdLine->
win32);
2576 i.win32_payload = (
char*)malloc(length + 2);
2578 i.win32_payload[length + 1] =
'\0';
2580 i.valueEnd = i.win32_payload;
2586 dr_bool32 dr_cmdline_next(dr_cmdline_iterator* i)
2588 if (i !=
NULL && i->pCmdLine !=
NULL)
2590 if (i->pCmdLine->win32 !=
NULL)
2593 if (i->value ==
NULL) {
2594 i->value = i->win32_payload;
2595 i->valueEnd = i->value;
2597 i->value = i->valueEnd + 1;
2602 while (i->value[0] ==
' ') {
2608 if (i->value[0] ==
'\0')
2610 free(i->win32_payload);
2611 i->win32_payload =
NULL;
2622 if (i->value[0] ==
'\"')
2626 i->valueEnd = i->value + 1;
2628 while (i->valueEnd[0] !=
'\0' && i->valueEnd[0] !=
'\"')
2630 if (i->valueEnd[0] ==
'\\') {
2633 if (i->valueEnd[0] ==
'\0') {
2640 i->valueEnd[0] =
'\0';
2645 i->valueEnd = i->value + 1;
2647 while (i->valueEnd[0] !=
'\0' && i->valueEnd[0] !=
' ')
2651 i->valueEnd[0] =
'\0';
2660 if (i->iarg < i->pCmdLine->argc)
2662 i->value = i->pCmdLine->argv[i->iarg];
2679 if (pCmdLine ==
NULL) {
2683 pCmdLine->
argc = argc;
2692 if (pCmdLine ==
NULL) {
2705 if (pCmdLine ==
NULL || callback ==
NULL) {
2710 char pTemp[2] = {0};
2715 dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
2716 if (dr_cmdline_next(&arg))
2718 if (!callback(
"[path]", arg.value, pUserData)) {
2723 while (dr_cmdline_next(&arg))
2725 if (arg.value[0] ==
'-')
2732 if (!callback(pKey, pVal, pUserData)) {
2747 if (arg.value[1] ==
'-')
2750 pKey = arg.value + 2;
2755 if (arg.value[1] !=
'\0')
2757 if (arg.value[2] ==
'\0')
2760 pTemp[0] = arg.value[1];
2768 while (arg.value[i] !=
'\0')
2770 pTemp[0] = arg.value[i];
2772 if (!callback(pTemp,
NULL, pUserData)) {
2790 if (!callback(pKey, pVal, pUserData)) {
2798 if (pKey !=
NULL && pVal ==
NULL) {
2799 callback(pKey, pVal, pUserData);
2807 } dr_cmdline_key_exists_data;
2809 dr_bool32 dr_cmdline_key_exists_callback(
const char* key,
const char* value,
void* pUserData)
2813 dr_cmdline_key_exists_data* pData = (dr_cmdline_key_exists_data*)pUserData;
2814 assert(pData !=
NULL);
2816 if (key !=
NULL && strcmp(pData->key, key) == 0) {
2826 dr_cmdline_key_exists_data data;
2836 if (argvOut ==
NULL)
return 0;
2841 size_t cmdlineLen = 0;
2847 dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
2848 while (dr_cmdline_next(&arg)) {
2849 cmdlineLen += strlen(arg.value) + 1;
2859 char* data = (
char*)malloc((argc *
sizeof(
char**)) + (cmdlineLen *
sizeof(char)));
2864 argv = (
char**)data;
2865 char* cmdlineStr = data + (argc *
sizeof(
char**));
2873 arg = dr_cmdline_begin(pCmdLine);
2874 while (dr_cmdline_next(&arg)) {
2875 argv[argc] = cmdlineStr + cmdlineLen;
2878 while (arg.value[i] !=
'\0') {
2879 argv[argc][i] = arg.value[i];
2882 argv[argc][i] =
'\0';
2885 cmdlineLen += strlen(arg.value) + 1;
2919 void dr_sleep(
unsigned int milliseconds)
2921 Sleep((DWORD)milliseconds);
2931 SYSTEM_INFO sysinfo;
2932 GetSystemInfo(&sysinfo);
2934 return (
unsigned int)sysinfo.dwNumberOfProcessors;
2954 static DWORD WINAPI dr_thread_entry_proc_win32(LPVOID pUserData)
2956 dr_thread_win32* pThreadWin32 = (dr_thread_win32*)pUserData;
2957 assert(pThreadWin32 !=
NULL);
2959 void* pEntryProcData = pThreadWin32->pData;
2961 assert(entryProc !=
NULL);
2963 pThreadWin32->isInEntryProc =
DR_TRUE;
2965 return (DWORD)entryProc(pEntryProcData);
2970 if (entryProc ==
NULL) {
2974 dr_thread_win32* pThreadWin32 = (dr_thread_win32*)malloc(
sizeof(*pThreadWin32));
2975 if (pThreadWin32 !=
NULL)
2977 pThreadWin32->entryProc = entryProc;
2978 pThreadWin32->pData = pData;
2979 pThreadWin32->isInEntryProc =
DR_FALSE;
2981 pThreadWin32->hThread = CreateThread(
NULL, 0, dr_thread_entry_proc_win32, pThreadWin32, 0,
NULL);
2982 if (pThreadWin32 ==
NULL) {
2992 while (!pThreadWin32->isInEntryProc) {
dr_sleep(0); }
3000 dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
3001 if (pThreadWin32 !=
NULL)
3003 CloseHandle(pThreadWin32->hThread);
3011 dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
3012 if (pThreadWin32 !=
NULL)
3014 WaitForSingleObject(pThreadWin32->hThread, INFINITE);
3025 #ifdef DR_UTIL_WIN32_USE_CRITICAL_SECTION_MUTEX
3028 dr_mutex mutex = malloc(
sizeof(CRITICAL_SECTION));
3031 InitializeCriticalSection(mutex);
3039 DeleteCriticalSection(mutex);
3045 EnterCriticalSection(mutex);
3050 LeaveCriticalSection(mutex);
3060 CloseHandle((HANDLE)mutex);
3065 WaitForSingleObject((HANDLE)mutex, INFINITE);
3070 SetEvent((HANDLE)mutex);
3077 return (
void*)CreateSemaphoreA(
NULL, initialValue, LONG_MAX,
NULL);
3082 CloseHandle(semaphore);
3087 return WaitForSingleObject((HANDLE)semaphore, INFINITE) == WAIT_OBJECT_0;
3092 return ReleaseSemaphore((HANDLE)semaphore, 1,
NULL) != 0;
3095 void dr_sleep(
unsigned int milliseconds)
3097 usleep(milliseconds * 1000);
3107 return (
unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
3127 static void* dr_thread_entry_proc_posix(
void* pDataIn)
3129 dr_thread_posix* pThreadPosix = (dr_thread_posix*)pDataIn;
3130 assert(pThreadPosix !=
NULL);
3132 void* pEntryProcData = pThreadPosix->pData;
3134 assert(entryProc !=
NULL);
3136 pThreadPosix->isInEntryProc =
DR_TRUE;
3138 return (
void*)(size_t)entryProc(pEntryProcData);
3143 if (entryProc ==
NULL) {
3147 dr_thread_posix* pThreadPosix = (dr_thread_posix*)malloc(
sizeof(*pThreadPosix));
3148 if (pThreadPosix !=
NULL)
3150 pThreadPosix->entryProc = entryProc;
3151 pThreadPosix->pData = pData;
3152 pThreadPosix->isInEntryProc =
DR_FALSE;
3154 if (pthread_create(&pThreadPosix->pthread,
NULL, dr_thread_entry_proc_posix, pThreadPosix) != 0) {
3161 while (!pThreadPosix->isInEntryProc) {}
3174 dr_thread_posix* pThreadPosix = (dr_thread_posix*)thread;
3175 if (pThreadPosix !=
NULL)
3177 pthread_join(pThreadPosix->pthread,
NULL);
3185 pthread_mutex_t* mutex = (pthread_mutex_t*)malloc(
sizeof(*mutex));
3186 if (pthread_mutex_init(mutex,
NULL) != 0) {
3196 pthread_mutex_destroy((pthread_mutex_t*)mutex);
3201 pthread_mutex_lock((pthread_mutex_t*)mutex);
3206 pthread_mutex_unlock((pthread_mutex_t*)mutex);
3213 sem_t* semaphore = (sem_t*)malloc(
sizeof(*semaphore));
3214 if (sem_init(semaphore, 0, (
unsigned int)initialValue) == -1) {
3224 sem_close((sem_t*)semaphore);
3229 return sem_wait((sem_t*)semaphore) != -1;
3234 return sem_post((sem_t*)semaphore) != -1;
3245 #include <AvailabilityMacros.h>
3246 #ifndef MAC_OS_X_VERSION_10_12
3247 #define MAC_OS_X_VERSION_10_12 101200
3249 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
3250 #include <mach/mach_time.h>
3251 #define CLOCK_REALTIME 0
3252 #define CLOCK_MONOTONIC 0
3253 int clock_gettime(
int clk_id,
struct timespec* t)
3255 mach_timebase_info_data_t timebase;
3256 mach_timebase_info(&timebase);
3258 time = mach_absolute_time();
3259 double nseconds = ((double)time * (
double)timebase.numer) / ((
double)timebase.denom);
3260 double seconds = ((double)time * (
double)timebase.numer) / ((
double)timebase.denom * 1e9);
3261 t->tv_sec = seconds;
3262 t->tv_nsec = nseconds;
3269 static LARGE_INTEGER g_DRTimerFrequency = {{0}};
3272 if (g_DRTimerFrequency.QuadPart == 0) {
3273 QueryPerformanceFrequency(&g_DRTimerFrequency);
3276 LARGE_INTEGER counter;
3277 QueryPerformanceCounter(&counter);
3283 LARGE_INTEGER counter;
3284 if (!QueryPerformanceCounter(&counter)) {
3288 dr_uint64 newTimeCounter = counter.QuadPart;
3291 pTimer->
counter = newTimeCounter;
3293 return (newTimeCounter - oldTimeCounter) / (double)g_DRTimerFrequency.QuadPart;
3298 struct timespec newTime;
3299 clock_gettime(CLOCK_MONOTONIC, &newTime);
3301 pTimer->
counter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
3306 struct timespec newTime;
3307 clock_gettime(CLOCK_MONOTONIC, &newTime);
3309 dr_uint64 newTimeCounter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
3312 pTimer->
counter = newTimeCounter;
3314 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
3324 return (
double)rand() / (double)RAND_MAX;
3338 if (usernameOut !=
NULL && usernameOutSize > 0) {
3339 usernameOut[0] =
'\0';
3343 DWORD dwSize = (DWORD)usernameOutSize;
3344 if (!GetUserNameA(usernameOut, &dwSize)) {
3350 struct passwd *pw = getpwuid(geteuid());
3355 if (usernameOut !=
NULL) {
3356 strcpy_s(usernameOut, usernameOutSize, pw->pw_name);
3359 return strlen(pw->pw_name);
3366 return GetProcessId(GetCurrentProcess());
3368 return (
unsigned int)getpid();
3379 if (ascii >=
'0' && ascii <=
'9') {
3380 if (out) *out = ascii -
'0';
3384 if (ascii >=
'A' && ascii <=
'F') {
3385 if (out) *out = 10 + (ascii -
'A');
3389 if (ascii >=
'a' && ascii <=
'f') {
3390 if (out) *out = 10 + (ascii -
'a');
3398 #endif //DR_IMPLEMENTATION
3686 size_t drpath_clean(
const char* path,
char* pathOut,
size_t pathOutSizeInBytes);
3715 dr_bool32 drpath_to_relative(
const char* absolutePathToMakeRelative,
const char* absolutePathToMakeRelativeTo,
char* relativePathOut,
size_t relativePathOutSizeInBytes);
3723 dr_bool32 drpath_to_absolute(
const char* relativePathToMakeAbsolute,
const char* basePath,
char* absolutePathOut,
size_t absolutePathOutSizeInBytes);
3738 #ifdef DR_IMPLEMENTATION
3750 if (path == 0 || path[0] ==
'\0') {
3768 if (path == 0 || path[0] ==
'\0') {
3859 if (s0Path ==
NULL || s1Path ==
NULL) {
3896 if (((i.
path[0] >=
'a' && i.
path[0] <=
'z') || (i.
path[0] >=
'A' && i.
path[0] <=
'Z')) && i.
path[1] ==
':') {
3911 while (path[0] !=
'\0') {
3912 if (path[0] ==
'\\') {
3926 while (path[0] !=
'\0') {
3927 if (path[0] ==
'/') {
3998 char* pathorig = path;
3999 char* baseend = path;
4002 while (path[0] !=
'\0') {
4003 if (path[0] ==
'/' || path[0] ==
'\\') {
4012 while (baseend > path) {
4013 if (baseend[0] !=
'/' && baseend[0] !=
'\\') {
4029 if (path ==
NULL || baseOut ==
NULL || baseSizeInBytes == 0) {
4033 strcpy_s(baseOut, baseSizeInBytes, path);
4043 const char* fileName = path;
4046 while (path[0] !=
'\0') {
4047 if (path[0] ==
'/' || path[0] ==
'\\') {
4055 while (fileName[0] !=
'\0' && (fileName[0] ==
'/' || fileName[0] ==
'\\')) {
4065 if (fileName !=
NULL) {
4066 strcpy_s(fileNameOut, fileNameSizeInBytes, fileName);
4079 const char* lastoccurance = 0;
4082 while (extension[0] !=
'\0')
4084 if (extension[0] ==
'.') {
4086 lastoccurance = extension;
4092 return (lastoccurance != 0) ? lastoccurance : extension;
4098 if (path1 ==
NULL || path2 ==
NULL) {
4102 if (path1 == path2 || (path1[0] ==
'\0' && path2[0] ==
'\0')) {
4122 }
while (isPath1Valid && isPath2Valid);
4133 if (path ==
NULL || extension ==
NULL) {
4137 const char* ext1 = extension;
4143 return strcasecmp(ext1, ext2) == 0;
4172 if (base ==
NULL || other ==
NULL) {
4176 size_t path1Length = strlen(base);
4177 size_t path2Length = strlen(other);
4179 if (path1Length >= baseBufferSizeInBytes) {
4185 if (path1Length > 0 && base[path1Length - 1] !=
'/' && base[path1Length - 1] !=
'\\') {
4186 base[path1Length] =
'/';
4191 if (path1Length + path2Length >= baseBufferSizeInBytes) {
4192 path2Length = baseBufferSizeInBytes - path1Length - 1;
4195 strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, other, path2Length);
4206 size_t path1Length = strlen(base);
4209 if (path1Length >= baseBufferSizeInBytes) {
4214 if (baseBufferSizeInBytes > 1) {
4222 if (path1Length > 0 && base[path1Length - 1] !=
'/' && base[path1Length - 1] !=
'\\') {
4223 base[path1Length] =
'/';
4228 if (path1Length + path2Length >= baseBufferSizeInBytes) {
4229 path2Length = baseBufferSizeInBytes - path1Length - 1;
4239 if (base ==
NULL || extension ==
NULL) {
4243 size_t baseLength = strlen(base);
4244 size_t extLength = strlen(extension);
4246 if (baseLength >= baseBufferSizeInBytes) {
4250 base[baseLength] =
'.';
4253 if (baseLength + extLength >= baseBufferSizeInBytes) {
4254 extLength = baseBufferSizeInBytes - baseLength - 1;
4257 strncpy_s(base + baseLength, baseBufferSizeInBytes - baseLength, extension, extLength);
4264 if (dst ==
NULL || dstSizeInBytes == 0) {
4268 strcpy_s(dst, dstSizeInBytes, base);
4274 if (dst ==
NULL || dstSizeInBytes == 0) {
4278 strcpy_s(dst, dstSizeInBytes, base);
4284 if (dst ==
NULL || dstSizeInBytes == 0) {
4288 strcpy_s(dst, dstSizeInBytes, base);
4298 size_t _drpath_clean_trywrite(
drpath_iterator* iterators,
unsigned int iteratorCount,
char* pathOut,
size_t pathOutSizeInBytes,
unsigned int ignoreCounter)
4300 if (iteratorCount == 0) {
4308 int ignoreThisSegment = ignoreCounter > 0 && isegment.
segment.
length > 0;
4313 ignoreThisSegment = 1;
4320 ignoreThisSegment = 1;
4326 if (ignoreCounter > 0) {
4334 size_t bytesWritten = 0;
4339 if (iteratorCount > 1)
4342 prev = iterators[iteratorCount - 1];
4354 iterators[iteratorCount - 1] = prev;
4355 bytesWritten = _drpath_clean_trywrite(iterators, iteratorCount, pathOut, pathOutSizeInBytes, ignoreCounter);
4359 if (!ignoreThisSegment)
4361 if (pathOutSizeInBytes > 0)
4363 pathOut += bytesWritten;
4364 pathOutSizeInBytes -= bytesWritten;
4366 if (bytesWritten > 0)
4372 pathOutSizeInBytes -= 1;
4383 bytesWritten += pathOutSizeInBytes;
4388 return bytesWritten;
4391 size_t drpath_clean(
const char* path,
char* pathOut,
size_t pathOutSizeInBytes)
4400 size_t bytesWritten = 0;
4401 if (path[0] ==
'/') {
4402 if (pathOut !=
NULL && pathOutSizeInBytes > 1) {
4408 bytesWritten += _drpath_clean_trywrite(&last, 1, pathOut + bytesWritten, pathOutSizeInBytes - bytesWritten - 1, 0);
4409 if (pathOutSizeInBytes > bytesWritten) {
4410 pathOut[bytesWritten] =
'\0';
4413 return bytesWritten + 1;
4421 if (base ==
NULL || other ==
NULL) {
4433 int iteratorCount = !isPathEmpty0 + !isPathEmpty1;
4434 if (iteratorCount == 0) {
4438 size_t bytesWritten = 0;
4439 if (base[0] ==
'/') {
4440 if (dst !=
NULL && dstSizeInBytes > 1) {
4446 bytesWritten += _drpath_clean_trywrite(last, 2, dst + bytesWritten, dstSizeInBytes - bytesWritten - 1, 0);
4447 if (dstSizeInBytes > bytesWritten) {
4448 dst[bytesWritten] =
'\0';
4451 return bytesWritten + 1;
4462 if (extension !=
NULL) {
4464 path[(extension - path)] =
'\0';
4472 if (dst ==
NULL || dstSizeInBytes == 0 || path ==
NULL) {
4477 if (extension !=
NULL) {
4481 strncpy_s(dst, dstSizeInBytes, path, (
size_t)(extension - path));
4529 if (dstSizeInBytes == 0) {
4547 if (dstSizeInBytes > 1) {
4548 dst[0] =
'/'; dst[1] =
'\0';
4566 return strcpy_s(dst, dstSizeInBytes,
"/") == 0;
4580 dr_bool32 drpath_to_relative(
const char* absolutePathToMakeRelative,
const char* absolutePathToMakeRelativeTo,
char* relativePathOut,
size_t relativePathOutSizeInBytes)
4585 if (relativePathOut == 0) {
4588 if (relativePathOutSizeInBytes == 0) {
4602 if (isPathEmpty && isBaseEmpty)
4605 relativePathOut[0] =
'\0';
4611 int isPathAtEnd = 0;
4612 int isBaseAtEnd = 0;
4622 relativePathOut[0] =
'\0';
4628 char* pDst = relativePathOut;
4629 size_t bytesAvailable = relativePathOutSizeInBytes;
4635 if (pDst != relativePathOut)
4637 if (bytesAvailable <= 3)
4640 relativePathOut[0] =
'\0';
4649 bytesAvailable -= 3;
4654 if (bytesAvailable <= 2)
4657 relativePathOut[0] =
'\0';
4665 bytesAvailable -= 2;
4677 if (pDst != relativePathOut)
4679 if (bytesAvailable <= 1)
4682 relativePathOut[0] =
'\0';
4689 bytesAvailable -= 1;
4696 relativePathOut[0] =
'\0';
4703 relativePathOut[0] =
'\0';
4722 dr_bool32 drpath_to_absolute(
const char* relativePathToMakeAbsolute,
const char* basePath,
char* absolutePathOut,
size_t absolutePathOutSizeInBytes)
4724 return drpath_append_and_clean(absolutePathOut, absolutePathOutSizeInBytes, basePath, relativePathToMakeAbsolute) != 0;
4726 #endif //DR_IMPLEMENTATION