Luna/libs/libc/src/printf.cpp
apio 0f47f59364 libc: make fprintf actually write to the chosen file
Also, printf now is kind of an alias for fprintf(stdout,...), as it should be.
2022-10-11 21:10:19 +02:00

373 lines
12 KiB
C++

#include <errno.h>
#include <luna.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
typedef long int ssize_t;
static void __strrev(char* arr, int size)
{
int left = 0;
int right = size - 1;
for (int i = left; i < right; i++)
{
char temp = arr[i];
arr[i] = arr[right];
arr[right] = temp;
right--;
}
}
template <typename IntegerType> static char* __unsignedtoa(IntegerType number, char* arr, int base)
{
int i = 0;
if (number == 0)
{
arr[i] = '0';
arr[i + 1] = '\0';
return arr;
}
while (number != 0)
{
IntegerType r = number % (IntegerType)base;
arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0');
i++;
number /= base;
}
__strrev(arr, i);
arr[i] = '\0';
return arr;
}
template <typename IntegerType> static char* __signedtoa(IntegerType number, char* arr, int base)
{
int i = 0, negative = 0;
if (number == 0)
{
arr[i] = '0';
arr[i + 1] = '\0';
return arr;
}
if (number < 0 && base == 10)
{
number *= -1;
negative = 1;
}
while (number != 0)
{
IntegerType r = number % base;
arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0');
i++;
number /= base;
}
if (negative)
{
arr[i] = '-';
i++;
}
__strrev(arr, i);
arr[i] = '\0';
return arr;
}
#pragma GCC push_options
#pragma GCC optimize("O0")
template <typename PutString>
static int internal_printf(const char* format, PutString put_string_callback, ssize_t max, va_list ap)
{
char buffer[1025]; // 1024 with null terminator
size_t format_size = strlen(format);
size_t format_index = 0;
size_t buffer_insert_index = 0;
ssize_t max_remaining = max;
size_t written = 0;
auto flush_buffer = [&]() {
size_t buffer_length = buffer_insert_index;
written += buffer_length;
buffer_insert_index = 0;
if (max_remaining < 0)
{
buffer[buffer_length] = 0;
put_string_callback(buffer);
return;
}
if (max_remaining == 0) { return; }
if (buffer_length <= (size_t)max_remaining)
{
max_remaining -= buffer_length;
buffer[buffer_length] = 0;
put_string_callback(buffer);
}
else
{
buffer[max_remaining] = 0;
max_remaining = 0;
put_string_callback(buffer);
}
};
bool is_long = false;
bool is_unsigned_long = false;
bool preserve_format = false;
while (format_index < format_size)
{
char current_char = format[format_index];
if (current_char == '%' || preserve_format)
{
if (!preserve_format && format_index + 1 == format_size) // end of format string
{
format_index++;
continue;
}
else
{
if (!preserve_format) format_index++;
preserve_format = false;
current_char = format[format_index];
switch (current_char)
{
case 'c': {
buffer[buffer_insert_index++] = (char)va_arg(ap, int);
if (buffer_insert_index == 1024) flush_buffer();
break;
}
case '%': {
buffer[buffer_insert_index++] = '%';
if (buffer_insert_index == 1024) flush_buffer();
break;
}
case 'z': {
is_unsigned_long = true;
preserve_format = true;
break;
}
case 'l': {
is_long = true;
preserve_format = true;
break;
}
case 'd': {
if (is_unsigned_long)
{
char result[25];
__unsignedtoa<unsigned long>(va_arg(ap, uint64_t), result, 10);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
is_unsigned_long = is_long = false;
}
else if (is_long)
{
char result[25];
__signedtoa<long>(va_arg(ap, int64_t), result, 10);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
is_unsigned_long = is_long = false;
}
else
{
char result[25];
__signedtoa<int>(va_arg(ap, int32_t), result, 10);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
}
break;
}
case 'u': {
if (is_unsigned_long || is_long)
{
char result[25];
__unsignedtoa<unsigned long>(va_arg(ap, uint64_t), result, 10);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
is_unsigned_long = is_long = false;
}
else
{
char result[25];
__unsignedtoa<unsigned int>(va_arg(ap, uint32_t), result, 10);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
}
break;
}
case 'x': {
if (is_unsigned_long || is_long)
{
char result[25];
__unsignedtoa<unsigned long>(va_arg(ap, uint64_t), result, 16);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
is_unsigned_long = is_long = false;
}
else
{
char result[25];
__unsignedtoa<unsigned int>(va_arg(ap, uint32_t), result, 16);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
}
break;
}
case 'p': {
char result[25];
__unsignedtoa<unsigned long>(va_arg(ap, uint64_t), result, 16);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
break;
}
case 'm': {
const char* str = strerror(errno);
while (strlen(str) > 1024)
{
flush_buffer();
memcpy(buffer, str, 1024);
str += 1024;
buffer_insert_index = 1024;
}
if (buffer_insert_index + strlen(str) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, str, strlen(str));
buffer_insert_index += strlen(str);
if (buffer_insert_index == 1024) flush_buffer();
break;
}
case 's': {
const char* str = va_arg(ap, const char*);
while (strlen(str) > 1024)
{
flush_buffer();
memcpy(buffer, str, 1024);
str += 1024;
buffer_insert_index = 1024;
}
if (buffer_insert_index + strlen(str) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, str, strlen(str));
buffer_insert_index += strlen(str);
if (buffer_insert_index == 1024) flush_buffer();
break;
}
default: {
NOT_IMPLEMENTED("internal_printf: unknown format specifier");
}
}
}
}
else
{
buffer[buffer_insert_index++] = current_char;
if (buffer_insert_index == 1024) flush_buffer();
}
format_index++;
}
if (buffer_insert_index > 0) flush_buffer();
return (int)written;
}
#pragma GCC pop_options
extern "C"
{
int vprintf(const char* format, va_list ap)
{
return vfprintf(stdout, format, ap);
}
int vsprintf(char* str, const char* format, va_list ap)
{
return internal_printf(
format,
[&](const char* s) {
if (str) strncat(str, s, 1025);
},
-1, ap);
}
int vsnprintf(char* str, size_t max, const char* format, va_list ap)
{
return internal_printf(
format,
[&](const char* s) {
if (str) strncat(str, s, 1025);
},
max == 0 ? 0 : max - 1, ap);
}
int snprintf(char* str, size_t max, const char* format, ...)
{
va_list ap;
va_start(ap, format);
int written = vsnprintf(str, max, format, ap);
va_end(ap);
return written;
}
int sprintf(char* str, const char* format, ...)
{
va_list ap;
va_start(ap, format);
int written = vsprintf(str, format, ap);
va_end(ap);
return written;
}
int printf(const char* format, ...)
{
va_list ap;
va_start(ap, format);
int written = vfprintf(stdout, format, ap);
va_end(ap);
return written;
}
int fprintf(FILE* stream, const char* format, ...)
{
va_list ap;
va_start(ap, format);
int written = vfprintf(stream, format, ap);
va_end(ap);
return written;
}
int vfprintf(FILE* stream, const char* format, va_list ap)
{
return internal_printf(
format, [&](const char* s) { fwrite(s, strlen(s), 1, stream); }, -1, ap);
}
}