597 lines
13 KiB
C
597 lines
13 KiB
C
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <brados/printk.h>
|
|
#include <brados/string.h>
|
|
#include <video/vga.h>
|
|
|
|
const char *BASE16 = "0123456789ABCDEF";
|
|
const char *BASE16L = "0123456789abcdef";
|
|
|
|
// Print an unsigned number as a string; return the number of characters printed
|
|
static int printNum(struct vgastate *term, uintmax_t num, unsigned int base, const char *digits)
|
|
{
|
|
if (base > strlen(digits))
|
|
return 0;
|
|
|
|
int numPrinted = 0;
|
|
int digit = num % base;
|
|
if (num >= base)
|
|
numPrinted += printNum(term, num / base, base, digits);
|
|
term_putChar(term, digits[digit]);
|
|
numPrinted++;
|
|
return numPrinted;
|
|
}
|
|
|
|
// Determines the printed length of a number
|
|
static unsigned int numLen(uintmax_t num, unsigned int base)
|
|
{
|
|
unsigned int len = 1;
|
|
while (num >= base) {
|
|
len++;
|
|
num /= base;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
// Print a formatted string to the given terminal
|
|
int printk(struct vgastate *term, const char *fmt, ...)
|
|
{
|
|
int numPrinted = 0;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
// The bits of 'mode' explained:
|
|
// 0: Whether we are printing characters normally or parsing the format
|
|
// 1: Whether we are still parsing flags
|
|
// 2: Whether we are looking at width or precision (have we seen a .)
|
|
// 3: + flag
|
|
// 4: space flag
|
|
// 5: - flag
|
|
// 6: # flag
|
|
// 7: 0 flag
|
|
uint8_t mode = 0;
|
|
int length = 0;
|
|
unsigned int width = 0, precision = 0;
|
|
|
|
for (size_t i = 0; i < strlen(fmt); i++) {
|
|
if (mode % 2 == 0) {
|
|
// Begin parsing format if we reach a '%'
|
|
if (fmt[i] == '%') {
|
|
mode |= 0x01;
|
|
// Or just print the character if we're not parsing
|
|
} else {
|
|
term_putChar(term, fmt[i]);
|
|
numPrinted++;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
switch (fmt[i]) {
|
|
case '%':
|
|
// Print a percent sign
|
|
term_putChar(term, '%');
|
|
numPrinted++;
|
|
mode = 0;
|
|
break;
|
|
case '+':
|
|
// Set the plus flag
|
|
if ((mode >> 1) % 2 == 0)
|
|
mode |= 0x08;
|
|
|
|
break;
|
|
case ' ':
|
|
// Set the space flag
|
|
if ((mode >> 1) % 2 == 0)
|
|
mode |= 0x10;
|
|
|
|
break;
|
|
case '-':
|
|
// Set the left-align flag
|
|
if ((mode >> 1) % 2 == 0)
|
|
mode |= 0x20;
|
|
|
|
break;
|
|
case '#':
|
|
// Set the alternate flag
|
|
if ((mode >> 1) % 2 == 0)
|
|
mode |= 0x40;
|
|
|
|
break;
|
|
case '0':
|
|
// Set the zero-pad flag
|
|
if ((mode >> 1) % 2 == 0)
|
|
mode |= 0x80;
|
|
// Next digit in width specifier
|
|
else if ((mode >> 2) % 2 == 0)
|
|
width *= 10;
|
|
// Next digit in precision specifier
|
|
else
|
|
precision *= 10;
|
|
|
|
break;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
// Next digit in width specifier
|
|
if ((mode >> 2) % 2 == 0) {
|
|
mode |= 0x02;
|
|
width *= 10;
|
|
width += (fmt[i] - 48); // 0 = ascii 48
|
|
// Next digit in precision specifier
|
|
} else {
|
|
precision *= 10;
|
|
precision += (fmt[i] - 48);
|
|
}
|
|
|
|
break;
|
|
case '.':
|
|
// Delimit width and precision
|
|
if ((mode >> 2) % 2 == 0)
|
|
mode |= 0x06;
|
|
|
|
break;
|
|
case 'h':
|
|
// Denotes a short or char argument
|
|
mode |= 0x06;
|
|
length--;
|
|
break;
|
|
case 'l':
|
|
// Denotes a long or long long argument
|
|
mode |= 0x06;
|
|
length++;
|
|
break;
|
|
case 'z':
|
|
// Denotes a size_t argument
|
|
mode |= 0x06;
|
|
length = 3;
|
|
break;
|
|
case 'j':
|
|
// Denotes a intmax_t argument
|
|
mode |= 0x06;
|
|
length = 4;
|
|
break;
|
|
case 't':
|
|
// Denotes a ptrdiff_t argument
|
|
mode |= 0x06;
|
|
length = 5;
|
|
break;
|
|
case 'd':
|
|
// Print a signed integer
|
|
case 'i': {
|
|
intmax_t num;
|
|
uintmax_t unum;
|
|
|
|
// Pull the argument from the list
|
|
switch (length) {
|
|
case 1:
|
|
num = va_arg(args, long);
|
|
break;
|
|
case 2:
|
|
num = va_arg(args, long long);
|
|
break;
|
|
case 3:
|
|
num = va_arg(args, size_t);
|
|
break;
|
|
case 4:
|
|
num = va_arg(args, intmax_t);
|
|
break;
|
|
case 5:
|
|
num = va_arg(args, ptrdiff_t);
|
|
break;
|
|
default:
|
|
num = va_arg(args, int);
|
|
break;
|
|
}
|
|
|
|
// Get unsigned version
|
|
if (num < 0)
|
|
unum = num * -1;
|
|
else
|
|
unum = num;
|
|
|
|
// Get the length of the printed number
|
|
unsigned int len = numLen(unum, 10);
|
|
|
|
// Get the length of the printed number with formatting
|
|
unsigned int flen = len;
|
|
if (precision > len)
|
|
flen += (precision - len);
|
|
if (num < 0)
|
|
flen++;
|
|
if (num >= 0 && ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1))
|
|
flen++;
|
|
|
|
// If specified, print preceding space padding
|
|
if ((mode >> 5) % 2 == 0) {
|
|
if (precision > 0 || (mode >> 7) % 2 == 0) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If specified print the sign
|
|
if (num < 0) {
|
|
term_putChar(term, '-');
|
|
numPrinted++;
|
|
} else if ((mode >> 3) % 2 == 1) {
|
|
term_putChar(term, '+');
|
|
numPrinted++;
|
|
} else if ((mode >> 4) % 2 == 1) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
|
|
// If specified, print the precision 0s
|
|
if (precision > 0) {
|
|
for (int j = precision - len; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
} else if ((mode >> 7) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
// Print the number
|
|
numPrinted += printNum(term, unum, 10, BASE16);
|
|
|
|
// If specified, print succeeding spaces
|
|
if ((mode >> 5) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
mode = 0;
|
|
break;
|
|
// Print an unsigned integer
|
|
} case 'u': {
|
|
uintmax_t num;
|
|
|
|
// Pull the argument from the list
|
|
switch (length) {
|
|
case 1:
|
|
num = va_arg(args, unsigned long);
|
|
break;
|
|
case 2:
|
|
num = va_arg(args, unsigned long long);
|
|
break;
|
|
case 3:
|
|
num = va_arg(args, size_t);
|
|
break;
|
|
case 4:
|
|
num = va_arg(args, intmax_t);
|
|
break;
|
|
case 5:
|
|
num = va_arg(args, ptrdiff_t);
|
|
break;
|
|
default:
|
|
num = va_arg(args, unsigned int);
|
|
break;
|
|
}
|
|
|
|
// Get the length of the printed number
|
|
unsigned int len = numLen(num, 10);
|
|
|
|
// Get the length of the printed number with formatting
|
|
unsigned int flen = len;
|
|
if (precision > len)
|
|
flen += (precision - len);
|
|
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
|
|
flen++;
|
|
|
|
// If specified, print preceding space padding
|
|
if ((mode >> 5) % 2 == 0) {
|
|
if (precision > 0 || (mode >> 7) % 2 == 0) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If specified print the sign
|
|
if ((mode >> 3) % 2 == 1) {
|
|
term_putChar(term, '+');
|
|
numPrinted++;
|
|
} else if ((mode >> 4) % 2 == 1) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
|
|
// If specified, print the precision 0s
|
|
if (precision > 0) {
|
|
for (int j = precision - len; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
} else if ((mode >> 7) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
// Print the number
|
|
numPrinted += printNum(term, num, 10, BASE16);
|
|
|
|
// If specified, print succeeding spaces
|
|
if ((mode >> 5) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
mode = 0;
|
|
break;
|
|
// Print an unsigned integer in hexadecimal
|
|
} case 'x':
|
|
case 'X': {
|
|
uintmax_t num;
|
|
|
|
// Pull the argument from the list
|
|
switch (length) {
|
|
case 1:
|
|
num = va_arg(args, unsigned long);
|
|
break;
|
|
case 2:
|
|
num = va_arg(args, unsigned long long);
|
|
break;
|
|
case 3:
|
|
num = va_arg(args, size_t);
|
|
break;
|
|
case 4:
|
|
num = va_arg(args, intmax_t);
|
|
break;
|
|
case 5:
|
|
num = va_arg(args, ptrdiff_t);
|
|
break;
|
|
default:
|
|
num = va_arg(args, unsigned int);
|
|
break;
|
|
}
|
|
|
|
// Get the length of the printed number
|
|
unsigned int len = numLen(num, 10);
|
|
|
|
// Get the length of the printed number with formatting
|
|
unsigned int flen = len;
|
|
if (precision > len)
|
|
flen += (precision - len);
|
|
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
|
|
flen++;
|
|
if ((mode >> 6) % 2 == 1)
|
|
flen += 2;
|
|
|
|
// If specified, print preceding space padding
|
|
if ((mode >> 5) % 2 == 0) {
|
|
if (precision > 0 || (mode >> 7) % 2 == 0) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If specified print the sign
|
|
if ((mode >> 3) % 2 == 1) {
|
|
term_putChar(term, '+');
|
|
numPrinted++;
|
|
} else if ((mode >> 4) % 2 == 1) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
|
|
// If specified, print the 0x
|
|
if ((mode >> 6) % 2 == 1) {
|
|
term_putChar(term, '0');
|
|
if (fmt[i] == 'x')
|
|
term_putChar(term, 'x');
|
|
else
|
|
term_putChar(term, 'X');
|
|
numPrinted += 2;
|
|
}
|
|
|
|
// If specified, print the precision 0s
|
|
if (precision > 0) {
|
|
for (int j = precision - len; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
} else if ((mode >> 7) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
// Print the number
|
|
if (fmt[i] == 'x')
|
|
numPrinted += printNum(term, num, 16, BASE16L);
|
|
else
|
|
numPrinted += printNum(term, num, 16, BASE16);
|
|
|
|
// If specified, print succeeding spaces
|
|
if ((mode >> 5) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
mode = 0;
|
|
break;
|
|
// Print an unsigned integer in octal
|
|
} case 'o': {
|
|
uintmax_t num;
|
|
|
|
// Pull the argument from the list
|
|
switch (length) {
|
|
case 1:
|
|
num = va_arg(args, unsigned long);
|
|
break;
|
|
case 2:
|
|
num = va_arg(args, unsigned long long);
|
|
break;
|
|
case 3:
|
|
num = va_arg(args, size_t);
|
|
break;
|
|
case 4:
|
|
num = va_arg(args, intmax_t);
|
|
break;
|
|
case 5:
|
|
num = va_arg(args, ptrdiff_t);
|
|
break;
|
|
default:
|
|
num = va_arg(args, unsigned int);
|
|
break;
|
|
}
|
|
|
|
// Get the length of the printed number
|
|
unsigned int len = numLen(num, 10);
|
|
|
|
// Get the length of the printed number with formatting
|
|
unsigned int flen = len;
|
|
if (precision > len)
|
|
flen += (precision - len);
|
|
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
|
|
flen++;
|
|
if ((mode >> 6) % 2 == 1)
|
|
flen++;
|
|
|
|
// If specified, print preceding space padding
|
|
if ((mode >> 5) % 2 == 0) {
|
|
if (precision > 0 || (mode >> 7) % 2 == 0) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If specified print the sign
|
|
if ((mode >> 3) % 2 == 1) {
|
|
term_putChar(term, '+');
|
|
numPrinted++;
|
|
} else if ((mode >> 4) % 2 == 1) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
|
|
// If specified, print the 0x
|
|
if ((mode >> 6) % 2 == 1) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
|
|
// If specified, print the precision 0s
|
|
if (precision > 0) {
|
|
for (int j = precision - len; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
} else if ((mode >> 7) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
// Print the number
|
|
numPrinted += printNum(term, num, 8, BASE16);
|
|
|
|
// If specified, print succeeding spaces
|
|
if ((mode >> 5) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
mode = 0;
|
|
break;
|
|
// Print a string
|
|
} case 's': {
|
|
char *s = va_arg(args, char *);
|
|
term_writeStr(term, s);
|
|
numPrinted += strlen(s);
|
|
mode = 0;
|
|
break;
|
|
// Print a character
|
|
} case 'c':
|
|
if (mode % 2 == 0)
|
|
term_putChar(term, 'c');
|
|
else
|
|
term_putChar(term, (char) va_arg(args, int));
|
|
|
|
numPrinted++;
|
|
mode = 0;
|
|
break;
|
|
// Print a pointer
|
|
case 'p': {
|
|
uintmax_t num = va_arg(args, void *);
|
|
|
|
// Get the length of the printed number
|
|
unsigned int len = numLen(num, 16);
|
|
unsigned int flen = len + 2;
|
|
precision = sizeof(void *) * 2; // A byte is 2 hex chars
|
|
|
|
// If specified, print preceding space padding
|
|
if ((mode >> 5) % 2 == 0) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
term_putChar(term, '0');
|
|
term_putChar(term, 'x');
|
|
numPrinted += 2;
|
|
|
|
// If specified, print the precision 0s
|
|
for (int j = precision - len; j > 0; j--) {
|
|
term_putChar(term, '0');
|
|
numPrinted++;
|
|
}
|
|
|
|
// Print the number
|
|
numPrinted += printNum(term, num, 16, BASE16);
|
|
|
|
// If specified, print succeeding spaces
|
|
if ((mode >> 5) % 2 == 1) {
|
|
for (int j = width - flen; j > 0; j--) {
|
|
term_putChar(term, ' ');
|
|
numPrinted++;
|
|
}
|
|
}
|
|
|
|
mode = 0;
|
|
break;
|
|
} default:
|
|
term_putChar(term, fmt[i]);
|
|
numPrinted++;
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
return numPrinted;
|
|
}
|