373 lines
12 KiB
C++
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);
|
|
}
|
|
} |