#define _CRT_RAND_S #include #include #include #include #include #include #include #include #include static bool expand_wstr(WCHAR **str, size_t *capacity, size_t needed) { if (*capacity >= needed) { return true; } if (needed > UNICODE_STRING_MAX_CHARS) { return false; } size_t new_capacity = *capacity ?: needed; while (new_capacity < needed) { new_capacity *= 2; } WCHAR *new_str = realloc(*str, new_capacity * sizeof(WCHAR)); if (!new_str) { return false; } *str = new_str; *capacity = new_capacity; return true; } static bool append_wstr(WCHAR **str, size_t *capacity, size_t *len, WCHAR *suffix) { size_t suffix_len = wcslen(suffix); if (!expand_wstr(str, capacity, *len + suffix_len)) { return false; } memcpy(*str + *len, suffix, suffix_len * sizeof(WCHAR)); *len += suffix_len; return true; } WCHAR * argv_to_command_line(char **argv) { // This is basically the reverse of CommandLineToArgvW(). We want to convert // an argv array into a command-line compatible with CreateProcessW(). // // See also: // // size_t len = 0, capacity = 0; WCHAR *cmdline = NULL; if (!expand_wstr(&cmdline, &capacity, 256)) { goto error; } for (size_t i = 0; argv[i]; i++) { // Separate arguments with spaces. if (i > 0 && !append_wstr(&cmdline, &capacity, &len, L" ")) { goto error; } // Surround the argument with quotes if it's empty or contains special // characters. char *cur = argv[i]; bool quoted = (*cur == '\0' || cur[strcspn(cur, " \t\n\v\"")] != '\0'); if (quoted && !append_wstr(&cmdline, &capacity, &len, L"\"")) { goto error; } while (*cur != '\0') { if (*cur == '"') { // Quotes must be escaped with a backslash. if (!append_wstr(&cmdline, &capacity, &len, L"\\\"")) { goto error; } cur++; } else if (*cur == '\\') { // Windows treats backslashes differently depending on whether // they're followed by a quote. If the backslashes aren't // followed by a quote, then all slashes are copied into the // argument string. Otherwise, only n/2 slashes are included. // Count the number of slashes and double them if they're // followed by a quote. size_t backslashes = strspn(cur, "\\"); cur += backslashes; // If the argument needs to be surrounded with quotes, we must // also check if the backslashes are at the end of the argument // because the added quote will follow them. if (*cur == '"' || (quoted && *cur == '\0')) { backslashes *= 2; } if (!expand_wstr(&cmdline, &capacity, len + backslashes)) { goto error; } wmemset(&cmdline[len], L'\\', backslashes); len += backslashes; } else { // Widen as many characters as possible. size_t mb_len = strcspn(cur, "\"\\"); int wide_len = MultiByteToWideChar(CP_UTF8, 0, cur, mb_len, NULL, 0); if (wide_len == 0) { goto error; } if (!expand_wstr(&cmdline, &capacity, len + wide_len)) { goto error; } wide_len = MultiByteToWideChar(CP_UTF8, 0, cur, mb_len, &cmdline[len], wide_len); if (wide_len == 0) { goto error; } cur += mb_len; len += wide_len; } } if (quoted && !append_wstr(&cmdline, &capacity, &len, L"\"")) { goto error; } } if (!expand_wstr(&cmdline, &capacity, len + 1)) { goto error; } cmdline[len] = L'\0'; return cmdline; error: free(cmdline); return NULL; } int asprintf(char **strp, const char *format, ...) { va_list arg1; va_start(arg1, format); int len = vsnprintf(NULL, 0, format, arg1); va_end(arg1); if (len >= 0) { size_t size = (size_t)len + 1; *strp = malloc(size); if (!*strp) { return -1; } va_list arg2; va_start(arg2, format); len = vsnprintf(*strp, size, format, arg2); va_end(arg2); } return len; } void filetime_to_timeval(struct timeval *tp, const FILETIME *ft) { int64_t ticks = ft->dwLowDateTime | (((int64_t)ft->dwHighDateTime) << 32); static const int64_t ticks_per_sec = 10LL * 1000LL * 1000LL; static const int64_t ticks_per_usec = 10LL; if (ticks >= 0) { tp->tv_sec = (long)(ticks / ticks_per_sec); tp->tv_usec = (long)((ticks % ticks_per_sec) / ticks_per_usec); } else { tp->tv_sec = (long)((ticks + 1) / ticks_per_sec - 1); tp->tv_usec = (long)((ticks_per_sec - 1 + (ticks + 1) % ticks_per_sec) / ticks_per_usec); } } pid_t getpid(void) { return (pid_t)GetCurrentProcessId(); } int gettimeofday(struct timeval *tp, void *tzp) { (void)tzp; FILETIME ft; GetSystemTimePreciseAsFileTime(&ft); int64_t ticks = ft.dwLowDateTime | (((int64_t)ft.dwHighDateTime) << 32); ticks -= 116444736000000000LL; // Convert to Unix time FILETIME unix_ft = {.dwLowDateTime = (DWORD)ticks, .dwHighDateTime = ticks >> 32}; filetime_to_timeval(tp, &unix_ft); return 0; } typedef void (WINAPI *QueryUnbiasedInterruptTimePreciseT)(PULONGLONG); static QueryUnbiasedInterruptTimePreciseT QueryUnbiasedInterruptTimePrecisePtr; static BOOL WINAPI mach_absolute_time_init(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) { // QueryUnbiasedInterruptTimePrecise() is declared in the Windows headers // but it isn't available in any import libraries. We must manually load it // from KernelBase.dll. HMODULE kernelbase = LoadLibraryW(L"KernelBase.dll"); if (!kernelbase) { print_winapi_error("LoadLibraryW", GetLastError()); abort(); } QueryUnbiasedInterruptTimePrecisePtr = (QueryUnbiasedInterruptTimePreciseT)GetProcAddress(kernelbase, "QueryUnbiasedInterruptTimePrecise"); if (!QueryUnbiasedInterruptTimePrecisePtr) { fprintf(stderr, "QueryUnbiasedInterruptTimePrecise is not available\n"); abort(); } return TRUE; } uint64_t mach_absolute_time(void) { static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; if (!InitOnceExecuteOnce(&init_once, mach_absolute_time_init, NULL, NULL)) { print_winapi_error("InitOnceExecuteOnce", GetLastError()); abort(); } ULONGLONG result = 0; QueryUnbiasedInterruptTimePrecisePtr(&result); return result * 100; // Convert from 100ns units } static void randomize_name(char *out) { static const char chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-"; const size_t num_chars = sizeof(chars) - 1; unsigned int lo, hi; rand_s(&lo); rand_s(&hi); uint64_t val = ((uint64_t)hi << 32) | lo; for (int j = 0; j < 6; j++) { out[j] = chars[val % num_chars]; val /= num_chars; } } #ifndef HAVE_MKSTEMP dispatch_fd_t mkstemp(char *tmpl) { size_t len = strlen(tmpl); if (len < 6) { errno = EINVAL; return -1; } char *replace = &tmpl[len - 6]; for (int i = 0; i < 100; i++) { randomize_name(replace); dispatch_fd_t fd = dispatch_test_fd_open(tmpl, O_RDWR | O_CREAT | O_EXCL); if (fd != -1) { return fd; } } errno = EEXIST; return -1; } #endif void print_winapi_error(const char *function_name, DWORD error) { char *message = NULL; DWORD len = FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, (LPSTR)&message, 0, NULL); if (len > 0) { // Note: FormatMessage includes a newline at the end of the message fprintf(stderr, "%s: %s", function_name, message); LocalFree(message); } else { fprintf(stderr, "%s: error %lu\n", function_name, error); } } intptr_t random(void) { unsigned int x; rand_s(&x); return x & INT_MAX; } unsigned int sleep(unsigned int seconds) { Sleep(seconds * 1000); return 0; } int usleep(unsigned int usec) { Sleep((usec + 999) / 1000); return 0; }