#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { typedef void(*__atexit_handler)(); static int __atexit_handler_count = 0; static __atexit_handler __atexit_handlers[32]; void exit(int status) { for (int i = 0; i < __atexit_handler_count; ++i) __atexit_handlers[i](); extern void _fini(); _fini(); fflush(stdout); fflush(stderr); _exit(status); ASSERT_NOT_REACHED(); } int atexit(void (*handler)()) { ASSERT(__atexit_handler_count < 32); __atexit_handlers[__atexit_handler_count++] = handler; return 0; } void abort() { raise(SIGABRT); } static HashTable s_malloced_environment_variables; static void free_environment_variable_if_needed(const char* var) { if (!s_malloced_environment_variables.contains((char*)var)) return; free((void*)var); s_malloced_environment_variables.remove((char*)var); } char* getenv(const char* name) { size_t vl = strlen(name); for (size_t i = 0; environ[i]; ++i) { const char* decl = environ[i]; char* eq = strchr(decl, '='); if (!eq) continue; size_t varLength = eq - decl; if (vl != varLength) continue; if (strncmp(decl, name, varLength) == 0) { return eq + 1; } } return nullptr; } int unsetenv(const char* name) { auto new_var_len = strlen(name); size_t environ_size = 0; size_t skip = -1; for (; environ[environ_size]; ++environ_size) { char* old_var = environ[environ_size]; char* old_eq = strchr(old_var, '='); ASSERT(old_eq); auto old_var_len = old_eq - old_var; if (new_var_len != old_var_len) continue; // can't match if (strncmp(name, old_var, new_var_len) == 0) skip = environ_size; } if (skip == -1) return 0; // not found: no failure. // Shuffle the existing array down by one. memmove(&environ[skip], &environ[skip+1], ((environ_size-1)-skip) * sizeof(environ[0])); environ[environ_size-1] = nullptr; free_environment_variable_if_needed(name); return 0; } int setenv(const char* name, const char* value, int overwrite) { if (!overwrite && !getenv(name)) return 0; auto length = strlen(name) + strlen(value) + 2; auto* var = (char*)malloc(length); snprintf(var, length, "%s=%s", name, value); s_malloced_environment_variables.set(var); return putenv(var); } int putenv(char* new_var) { char* new_eq = strchr(new_var, '='); if (!new_eq) return unsetenv(new_var); auto new_var_len = new_eq - new_var; size_t environ_size = 0; for (; environ[environ_size]; ++environ_size) { char* old_var = environ[environ_size]; char* old_eq = strchr(old_var, '='); ASSERT(old_eq); auto old_var_len = old_eq - old_var; if (new_var_len != old_var_len) continue; // can't match if (strncmp(new_var, old_var, new_var_len) == 0) { free_environment_variable_if_needed(old_var); environ[environ_size] = new_var; return 0; } } // At this point, we need to append the new var. // 2 here: one for the new var, one for the sentinel value. char **new_environ = (char**)malloc((environ_size + 2) * sizeof(char*)); if (new_environ == nullptr) { errno = ENOMEM; return -1; } for (size_t i = 0; environ[i]; ++i) { new_environ[i] = environ[i]; } new_environ[environ_size] = new_var; new_environ[environ_size + 1] = nullptr; // swap new and old // note that the initial environ is not heap allocated! extern bool __environ_is_malloced; if (__environ_is_malloced) free(environ); __environ_is_malloced = true; environ = new_environ; return 0; } double strtod(const char* str, char** endptr) { (void)str; (void)endptr; dbgprintf("LibC: strtod: '%s'\n", str); ASSERT_NOT_REACHED(); } float strtof(const char* str, char** endptr) { (void)str; (void)endptr; dbgprintf("LibC: strtof: '%s'\n", str); ASSERT_NOT_REACHED(); } double atof(const char* str) { dbgprintf("LibC: atof: '%s'\n", str); ASSERT_NOT_REACHED(); } int atoi(const char* str) { size_t len = strlen(str); int value = 0; bool isNegative = false; for (size_t i = 0; i < len; ++i) { if (i == 0 && str[0] == '-') { isNegative = true; continue; } if (str[i] < '0' || str[i] > '9') return value; value = value * 10; value += str[i] - '0'; } return isNegative ? -value : value; } long atol(const char* str) { static_assert(sizeof(int) == sizeof(long)); return atoi(str); } long long atoll(const char* str) { dbgprintf("FIXME(Libc): atoll('%s') passing through to atol()\n", str); return atol(str); } static char ptsname_buf[32]; char* ptsname(int fd) { if (ptsname_r(fd, ptsname_buf, sizeof(ptsname_buf)) < 0) return nullptr; return ptsname_buf; } int ptsname_r(int fd, char* buffer, size_t size) { int rc = syscall(SC_ptsname_r, fd, buffer, size); __RETURN_WITH_ERRNO(rc, rc, -1); } static unsigned long s_next_rand = 1; int rand() { s_next_rand = s_next_rand * 1103515245 + 12345; return((unsigned)(s_next_rand/((RAND_MAX + 1) * 2)) % (RAND_MAX + 1)); } void srand(unsigned seed) { s_next_rand = seed; } int abs(int i) { return i < 0 ? -i : i; } long int random() { return rand(); } void srandom(unsigned seed) { srand(seed); } int system(const char* command) { auto child = fork(); if (!child) { int rc = execl("/bin/sh", "sh", "-c", command, nullptr); if (rc < 0) perror("execl"); exit(0); } int wstatus; waitpid(child, &wstatus, 0); return WEXITSTATUS(wstatus); } char* mktemp(char* pattern) { int length = strlen(pattern); // FIXME: Check for an invalid template pattern and return EINVAL. if (length < 6) { pattern[0] = '\0'; errno = EINVAL; return pattern; } int start = length - 6; static constexpr char random_characters[] = "abcdefghijklmnopqrstuvwxyz0123456789"; for (int attempt = 0; attempt < 100; ++attempt) { for (int i = 0; i < 6; ++i) pattern[start + i] = random_characters[(rand() % sizeof(random_characters))]; struct stat st; int rc = lstat(pattern, &st); if (rc < 0 && errno == ENOENT) return pattern; } pattern[0] = '\0'; errno = EEXIST; return pattern; } void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { dbgprintf("FIXME(LibC): bsearch(%p, %p, %u, %u, %p)\n", key, base, nmemb, size, compar); ASSERT_NOT_REACHED(); } div_t div(int numerator, int denominator) { div_t result; result.quot = numerator / denominator; result.rem = numerator % denominator; return result; } ldiv_t ldiv(long numerator, long denominator) { ldiv_t result; result.quot = numerator / denominator; result.rem = numerator % denominator; return result; } size_t mbstowcs(wchar_t*, const char*, size_t) { ASSERT_NOT_REACHED(); } long strtol(const char* str, char** endptr, int base) { const char* s = str; unsigned long acc; int c; unsigned long cutoff; int neg = 0; int any; int cutlim; do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; cutlim = cutoff % (unsigned long)base; cutoff /= (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = neg ? LONG_MIN : LONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr) *endptr = const_cast((any ? s - 1 : str)); return acc; } unsigned long strtoul(const char* str, char** endptr, int base) { auto value = strtol(str, endptr, base); ASSERT(value >= 0); return value; } }