395 lines
7.5 KiB
C
Executable file
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);
|
|
}
|