Luna/libc/src/stdlib.cpp

301 lines
6.9 KiB
C++
Raw Normal View History

#include <bits/errno-return.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <luna/Heap.h>
#include <luna/NumberParsing.h>
2023-05-02 18:45:43 +00:00
#include <luna/Sort.h>
#include <luna/Utf8.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
2023-04-08 10:18:52 +00:00
#include <sys/wait.h>
#include <unistd.h>
template <typename ArgT, typename ResultT> static inline ResultT __generic_div(ArgT a, ArgT b)
{
ResultT result;
result.quot = a / b;
result.rem = a % b;
if (a >= 0 && result.rem < 0)
{
result.quot++;
result.rem -= b;
}
return result;
}
extern "C"
{
int abs(int v)
{
return __builtin_abs(v);
}
long labs(long v)
{
return __builtin_labs(v);
}
long long llabs(long long v)
{
return __builtin_llabs(v);
}
div_t div(int num, int den)
{
return __generic_div<int, div_t>(num, den);
}
ldiv_t ldiv(long num, long den)
{
return __generic_div<long, ldiv_t>(num, den);
}
lldiv_t lldiv(long long num, long long den)
{
return __generic_div<long long, lldiv_t>(num, den);
}
int atoi(const char* s)
{
return (int)strtol(s, NULL, 10);
}
long atol(const char* s)
{
return strtol(s, NULL, 10);
}
// Assuming LP64, long long == long.
long long atoll(const char* s)
{
return (long long)strtol(s, NULL, 10);
}
// These checks are only necessary on LLP64 platforms, where long won't match size_t/ssize_t. Probably redundant
// then (since Luna follows the regular LP64 model), but oh well...
long strtol(const char* str, char** endptr, int base)
{
isize rc = parse_signed_integer(str, const_cast<const char**>(endptr), base);
if (rc > (isize)LONG_MAX) return LONG_MAX;
if (rc < (isize)LONG_MIN) return LONG_MIN;
return rc;
}
unsigned long strtoul(const char* str, char** endptr, int base)
{
usize rc = parse_unsigned_integer(str, const_cast<const char**>(endptr), base);
if (rc > (usize)ULONG_MAX) return ULONG_MAX;
return rc;
}
__noreturn void abort()
{
// First, try to unblock SIGABRT and then raise it.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGABRT);
sigprocmask(SIG_UNBLOCK, &set, nullptr);
raise(SIGABRT);
// Still here? The program must have catched it. Reset the disposition to default.
struct sigaction act;
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGABRT, &act, nullptr);
raise(SIGABRT);
// There is no way we could end up here, unless there is some sort of race condition or the kernel decided to
// change the default action for SIGABRT because it's a Tuesday.
__builtin_unreachable();
}
__noreturn void _Exit(int status)
{
syscall(SYS_exit, status);
__builtin_unreachable();
}
size_t mbstowcs(wchar_t* buf, const char* src, size_t max)
{
if (max == 0) return 0;
size_t result = (size_t)-1;
Utf8StringDecoder decoder(src);
if (!buf)
{
decoder.code_points().try_set_value_or_error(result, errno);
return result;
}
decoder.decode(buf, max).try_set_value_or_error(result, errno);
return result;
}
2023-01-14 10:59:08 +00:00
size_t wcstombs(char* buf, const wchar_t* src, size_t max)
{
if (max == 0) return 0;
size_t result = (size_t)-1;
2023-01-14 10:59:08 +00:00
Utf8StringEncoder encoder(src);
if (!buf)
{
encoder.byte_length().try_set_value_or_error(result, errno);
return result;
}
2023-01-14 10:59:08 +00:00
encoder.encode(buf, max).try_set_value_or_error(result, errno);
return result;
2023-01-14 10:59:08 +00:00
}
void* malloc(size_t size)
{
return TRY_OR_SET_ERRNO(malloc_impl(size), void*, nullptr);
}
void* calloc(size_t nmemb, size_t size)
{
return TRY_OR_SET_ERRNO(calloc_impl(nmemb, size), void*, nullptr);
}
void* realloc(void* ptr, size_t size)
{
return TRY_OR_SET_ERRNO(realloc_impl(ptr, size), void*, nullptr);
}
void free(void* ptr)
{
free_impl(ptr);
}
2023-04-08 10:18:52 +00:00
int system(const char* cmd)
{
if (!cmd)
{
struct stat st;
if (stat("/bin/sh", &st) < 0) return 0;
return S_ISREG(st.st_mode);
2023-04-08 10:18:52 +00:00
}
pid_t child = fork();
if (child == 0)
{
execl("/bin/sh", "sh", "-c", cmd, NULL);
_Exit(127);
}
if (child < 0) return -1;
sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, &oldset);
auto old_int = signal(SIGINT, SIG_IGN);
auto old_quit = signal(SIGQUIT, SIG_IGN);
2023-04-08 10:18:52 +00:00
int status;
waitpid(child, &status, 0);
sigprocmask(SIG_SETMASK, &oldset, nullptr);
signal(SIGINT, old_int);
signal(SIGQUIT, old_quit);
2023-04-08 10:18:52 +00:00
return status;
}
2023-05-02 18:45:43 +00:00
void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*))
{
c_quicksort(base, nmemb, size, compar);
}
static unsigned next = 42;
int rand()
{
next = next * 1103515245 + 12345;
return (int)((unsigned)(next / 65536) % 32768);
}
void srand(unsigned int seed)
{
next = seed;
}
static void generate_random_character(char* ptr)
{
constexpr const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
2023-06-03 10:15:57 +00:00
*ptr = chars[rand() % (sizeof(chars) - 1)];
}
static int check_template(char* _template, size_t* len)
{
*len = strlen(_template);
if (*len < 6)
{
errno = EINVAL;
return -1;
}
if (strncmp(_template + (*len - 6), "XXXXXX", 6))
{
errno = EINVAL;
return -1;
}
return 0;
}
static void generate_random_name(char* _template, const size_t* len)
{
size_t index = *len - 6;
while (index < *len) generate_random_character(&_template[index++]);
}
char* mkdtemp(char* _template)
{
size_t len;
if (check_template(_template, &len) < 0) return nullptr;
while (true)
{
generate_random_name(_template, &len);
if (mkdir(_template, 0700) == 0) return _template;
if (errno != EEXIST) return nullptr;
}
}
int mkstemp(char* _template)
{
size_t len;
if (check_template(_template, &len) < 0) return -1;
while (true)
{
generate_random_name(_template, &len);
int fd;
if ((fd = open(_template, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0) return fd;
if (errno != EEXIST) return -1;
}
}
}