FENIX_libc/stdio/printf.c
2020-12-18 12:39:22 -06:00

395 lines
7.5 KiB
C
Executable file

#include <limits.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
static bool print(const char * str, size_t len) {
const unsigned char * bytes = (const unsigned char *) str;
for(size_t i = 0; i < len; i++) {
if(putchar(bytes[i]) == EOF) {
return false;
}
}
return true;
}
static bool print_oct(unsigned int strint) {
int i = strint, j = 10;
char str[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
while(j >= 0 && i > 0) {
switch(i % 010) {
case 0: str[j] = '0'; break;
case 1: str[j] = '1'; break;
case 2: str[j] = '2'; break;
case 3: str[j] = '3'; break;
case 4: str[j] = '4'; break;
case 5: str[j] = '5'; break;
case 6: str[j] = '6'; break;
case 7: str[j] = '7'; break;
default: return false;
}
i /= 010;
j--;
}
for(i = 0; str[i] == '\0'; i++) {}
return print(str+i, 11-i);
}
static bool print_ptr(void * ptrstr) {
int i = (int) ptrstr, j = 9;
char str[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
while(j >= 2 && i > 0) {
switch(i % 0x10) {
case 0: str[j] = '0'; break;
case 1: str[j] = '1'; break;
case 2: str[j] = '2'; break;
case 3: str[j] = '3'; break;
case 4: str[j] = '4'; break;
case 5: str[j] = '5'; break;
case 6: str[j] = '6'; break;
case 7: str[j] = '7'; break;
case 8: str[j] = '8'; break;
case 9: str[j] = '9'; break;
case 0xA: str[j] = 'A'; break;
case 0xB: str[j] = 'B'; break;
case 0xC: str[j] = 'C'; break;
case 0xD: str[j] = 'D'; break;
case 0xE: str[j] = 'E'; break;
case 0xF: str[j] = 'F'; break;
default: return false;
}
i /= 0x10;
j--;
}
for(i = 0; str[i] == '\0'; i++) {}
i--;
str[i] = 'x';
i--;
str[i] = '0';
return print(str+i, 10-i);
}
static bool print_caphex(unsigned int strint) {
int i = strint, j = 7;
char str[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
while(j >= 0 && i > 0) {
switch(i % 0x10) {
case 0: str[j] = '0'; break;
case 1: str[j] = '1'; break;
case 2: str[j] = '2'; break;
case 3: str[j] = '3'; break;
case 4: str[j] = '4'; break;
case 5: str[j] = '5'; break;
case 6: str[j] = '6'; break;
case 7: str[j] = '7'; break;
case 8: str[j] = '8'; break;
case 9: str[j] = '9'; break;
case 0xA: str[j] = 'A'; break;
case 0xB: str[j] = 'B'; break;
case 0xC: str[j] = 'C'; break;
case 0xD: str[j] = 'D'; break;
case 0xE: str[j] = 'E'; break;
case 0xF: str[j] = 'F'; break;
default: return false;
}
i /= 0x10;
j--;
}
for(i = 0; str[i] == '\0'; i++) {}
return print(str+i, 8-i);
}
static bool print_hex(unsigned int strint) {
int i = strint, j = 7;
char str[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
while(j >= 0 && i > 0) {
switch(i % 0x10) {
case 0: str[j] = '0'; break;
case 1: str[j] = '1'; break;
case 2: str[j] = '2'; break;
case 3: str[j] = '3'; break;
case 4: str[j] = '4'; break;
case 5: str[j] = '5'; break;
case 6: str[j] = '6'; break;
case 7: str[j] = '7'; break;
case 8: str[j] = '8'; break;
case 9: str[j] = '9'; break;
case 0xA: str[j] = 'a'; break;
case 0xB: str[j] = 'b'; break;
case 0xC: str[j] = 'c'; break;
case 0xD: str[j] = 'd'; break;
case 0xE: str[j] = 'e'; break;
case 0xF: str[j] = 'f'; break;
default: return false;
}
i /= 0x10;
j--;
}
for(i = 0; str[i] == '\0'; i++) {}
return print(str+i, 8-i);
}
static bool print_int(int strint) {
int i = strint, j = 10;
char str[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if(i < 0) {
i = -i;
}
while(j > 0 && i > 0) {
switch(i % 10) {
case 0: str[j] = '0'; break;
case 1: str[j] = '1'; break;
case 2: str[j] = '2'; break;
case 3: str[j] = '3'; break;
case 4: str[j] = '4'; break;
case 5: str[j] = '5'; break;
case 6: str[j] = '6'; break;
case 7: str[j] = '7'; break;
case 8: str[j] = '8'; break;
case 9: str[j] = '9'; break;
default: return false;
}
i /= 10;
j--;
}
for(i = 0; str[i] == '\0'; i++) {}
if(strint < 0) {
i--;
str[i] = '-';
}
return print(str+i, 11-i);
}
static bool print_uint(unsigned int strint) {
int i = strint;
if(i < 0) {
i = -i;
}
return print_int(i);
}
int printf(const char * restrict format, ...) {
int errno;
va_list parameters;
va_start(parameters, format);
int written = 0;
while(*format != '\0') {
size_t maxrem = INT_MAX - written;
if(format[0] != '%' || format[1] == '%') {
if(format[0] == '%') {
format++;
}
size_t amount = 1;
while(format[amount] && format[amount] != '%') {
amount++;
}
if(maxrem < amount) {
errno = EOVERFLOW;
return -1;
}
if(!print(format, amount)) {
return -1;
}
format += amount;
written += amount;
continue;
}
const char * format_begun_at = format++;
/* TODO: Implement all format specifiers (%g, %o, &c.) */
if(*format == 'c') {
format++;
char c = (char) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print(&c, sizeof(c))) {
return -1;
}
written++;
}
else if(*format == 's') {
format++;
const char * str = va_arg(parameters, const char *);
size_t len = strlen(str);
if(maxrem < len) {
errno = EOVERFLOW;
return -1;
}
if(!print(str, len)) {
return -1;
}
written += len;
}
else if(*format == 'd' || *format == 'i') {
format++;
int i = (int) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_int(i)) {
return -1;
}
written += 11;
}
else if(*format == 'u') {
format++;
int i = (int) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_uint(i)) {
return -1;
}
written += 10;
}
else if(*format == 'o') {
format++;
int i = (int) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_oct(i)) {
return -1;
}
written += 11;
}
else if(*format == 'x') {
format++;
int i = (int) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_hex(i)) {
return -1;
}
written += 10;
}
else if(*format == 'X') {
format++;
int i = (int) va_arg(parameters, int);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_caphex(i)) {
return -1;
}
written += 8;
}
else if(*format == 'p') {
format++;
void * ptr = (void *) va_arg(parameters, void *);
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_ptr(ptr)) {
return -1;
}
/* This is gonna need a change on 64-bit systems. */
written += 10;
}else if(*format == 'n') {
format++;
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print_int(written)) {
return -1;
}
written += 11;
}
else if(*format == '%') {
format++;
if(!maxrem) {
errno = EOVERFLOW;
return -1;
}
if(!print("%", 1)) {
return -1;
}
written++;
}
else {
format = format_begun_at;
size_t len = strlen(format);
if(maxrem < len) {
errno = EOVERFLOW;
return -1;
}
if(!print(format, len)) {
return -1;
}
format += len;
}
}
va_end(parameters);
return(written);
}