HelenOS sources
This source file includes following definitions.
- printf_putnchars
- printf_putstr
- printf_putchar
- print_char
- print_str
- print_number
- printf_core
#include <printf_core.h>
#include <printf.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <macros.h>
#include <str.h>
#define __PRINTF_FLAG_PREFIX 0x00000001
#define __PRINTF_FLAG_SIGNED 0x00000002
#define __PRINTF_FLAG_ZEROPADDED 0x00000004
#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
#define __PRINTF_FLAG_SHOWPLUS 0x00000020
#define __PRINTF_FLAG_SPACESIGN 0x00000040
#define __PRINTF_FLAG_BIGCHARS 0x00000080
#define __PRINTF_FLAG_NEGATIVE 0x00000100
#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
#define PRINTF_GET_INT_ARGUMENT(type, ap, flags) \
({ \
unsigned type res; \
\
if ((flags) & __PRINTF_FLAG_SIGNED) { \
signed type arg = va_arg((ap), signed type); \
\
if (arg < 0) { \
res = -arg; \
(flags) |= __PRINTF_FLAG_NEGATIVE; \
} else \
res = arg; \
} else \
res = va_arg((ap), unsigned type); \
\
res; \
})
typedef enum {
PrintfQualifierByte = 0,
PrintfQualifierShort,
PrintfQualifierInt,
PrintfQualifierLong,
PrintfQualifierLongLong,
PrintfQualifierPointer,
PrintfQualifierSize,
PrintfQualifierMax
} qualifier_t;
static const char *nullstr = "(NULL)";
static const char *digits_small = "0123456789abcdef";
static const char *digits_big = "0123456789ABCDEF";
static const char invalch = U_SPECIAL;
static int printf_putnchars(const char *buf, size_t size,
printf_spec_t *ps)
{
return ps->str_write((void *) buf, size, ps->data);
}
static int printf_putstr(const char *str, printf_spec_t *ps)
{
if (str == NULL)
return printf_putnchars(nullstr, str_size(nullstr), ps);
return ps->str_write((void *) str, str_size(str), ps->data);
}
static int printf_putchar(const char ch, printf_spec_t *ps)
{
if (!ascii_check(ch))
return ps->str_write((void *) &invalch, 1, ps->data);
return ps->str_write(&ch, 1, ps->data);
}
static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
{
size_t counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (--width > 0) {
if (printf_putchar(' ', ps) > 0)
counter++;
}
}
if (printf_putchar(ch, ps) > 0)
counter++;
while (--width > 0) {
if (printf_putchar(' ', ps) > 0)
counter++;
}
return (int) (counter);
}
static int print_str(char *str, int width, unsigned int precision,
uint32_t flags, printf_spec_t *ps)
{
if (str == NULL)
return printf_putstr(nullstr, ps);
size_t strw = str_length(str);
if ((precision == 0) || (precision > strw))
precision = strw;
size_t counter = 0;
width -= precision;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
}
int retval;
size_t size = str_lsize(str, precision);
if ((retval = printf_putnchars(str, size, ps)) < 0)
return -counter;
counter += retval;
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
return ((int) counter);
}
static int print_number(uint64_t num, int width, int precision, int base,
uint32_t flags, printf_spec_t *ps)
{
const char *digits;
if (flags & __PRINTF_FLAG_BIGCHARS)
digits = digits_big;
else
digits = digits_small;
char data[PRINT_NUMBER_BUFFER_SIZE];
char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
int size = 0;
*ptr-- = 0;
if (num == 0) {
*ptr-- = '0';
size++;
} else {
do {
*ptr-- = digits[num % base];
size++;
} while (num /= base);
}
int number_size = size;
if (flags & __PRINTF_FLAG_PREFIX) {
switch (base) {
case 2:
size += 2;
break;
case 8:
size++;
break;
case 16:
size += 2;
break;
}
}
char sgn = 0;
if (flags & __PRINTF_FLAG_SIGNED) {
if (flags & __PRINTF_FLAG_NEGATIVE) {
sgn = '-';
size++;
} else if (flags & __PRINTF_FLAG_SHOWPLUS) {
sgn = '+';
size++;
} else if (flags & __PRINTF_FLAG_SPACESIGN) {
sgn = ' ';
size++;
}
}
if (flags & __PRINTF_FLAG_LEFTALIGNED)
flags &= ~__PRINTF_FLAG_ZEROPADDED;
if (flags & __PRINTF_FLAG_ZEROPADDED) {
if ((precision == 0) && (width > size))
precision = width - size + number_size;
}
if (number_size > precision) {
precision = number_size;
}
width -= precision + size - number_size;
size_t counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
}
if (sgn) {
if (printf_putchar(sgn, ps) == 1)
counter++;
}
if (flags & __PRINTF_FLAG_PREFIX) {
switch (base) {
case 2:
if (printf_putchar('0', ps) == 1)
counter++;
if (flags & __PRINTF_FLAG_BIGCHARS) {
if (printf_putchar('B', ps) == 1)
counter++;
} else {
if (printf_putchar('b', ps) == 1)
counter++;
}
break;
case 8:
if (printf_putchar('o', ps) == 1)
counter++;
break;
case 16:
if (printf_putchar('0', ps) == 1)
counter++;
if (flags & __PRINTF_FLAG_BIGCHARS) {
if (printf_putchar('X', ps) == 1)
counter++;
} else {
if (printf_putchar('x', ps) == 1)
counter++;
}
break;
}
}
precision -= number_size;
while (precision-- > 0) {
if (printf_putchar('0', ps) == 1)
counter++;
}
int retval;
if ((retval = printf_putstr(++ptr, ps)) > 0)
counter += retval;
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
return ((int) counter);
}
int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
{
size_t i;
size_t nxt = 0;
size_t j = 0;
size_t counter = 0;
int retval;
while (true) {
i = nxt;
char32_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
if (uc == '%') {
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
counter = -counter;
goto out;
}
counter += retval;
}
j = i;
uint32_t flags = 0;
bool end = false;
do {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
switch (uc) {
case '#':
flags |= __PRINTF_FLAG_PREFIX;
break;
case '-':
flags |= __PRINTF_FLAG_LEFTALIGNED;
break;
case '+':
flags |= __PRINTF_FLAG_SHOWPLUS;
break;
case ' ':
flags |= __PRINTF_FLAG_SPACESIGN;
break;
case '0':
flags |= __PRINTF_FLAG_ZEROPADDED;
break;
default:
end = true;
}
} while (!end);
int width = 0;
if (isdigit(uc)) {
while (true) {
width *= 10;
width += uc - '0';
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
if (!isdigit(uc))
break;
}
} else if (uc == '*') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
width = (int) va_arg(ap, int);
if (width < 0) {
width *= -1;
flags |= __PRINTF_FLAG_LEFTALIGNED;
}
}
int precision = 0;
if (uc == '.') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (isdigit(uc)) {
while (true) {
precision *= 10;
precision += uc - '0';
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
if (!isdigit(uc))
break;
}
} else if (uc == '*') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
precision = (int) va_arg(ap, int);
if (precision < 0) {
precision = 0;
}
}
}
qualifier_t qualifier;
switch (uc) {
case 't':
if (sizeof(ptrdiff_t) == sizeof(int32_t))
qualifier = PrintfQualifierInt;
else
qualifier = PrintfQualifierLongLong;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
break;
case 'h':
qualifier = PrintfQualifierShort;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 'h') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
qualifier = PrintfQualifierByte;
}
break;
case 'l':
qualifier = PrintfQualifierLong;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 'l') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
qualifier = PrintfQualifierLongLong;
}
break;
case 'z':
qualifier = PrintfQualifierSize;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
break;
case 'j':
qualifier = PrintfQualifierMax;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
break;
default:
qualifier = PrintfQualifierInt;
}
unsigned int base = 10;
switch (uc) {
case 's':
retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
if (retval < 0) {
counter = -counter;
goto out;
}
counter += retval;
j = nxt;
continue;
case 'c':
retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
if (retval < 0) {
counter = -counter;
goto out;
}
counter += retval;
j = nxt;
continue;
case 'P':
flags |= __PRINTF_FLAG_BIGCHARS;
case 'p':
flags |= __PRINTF_FLAG_PREFIX;
flags |= __PRINTF_FLAG_ZEROPADDED;
base = 16;
qualifier = PrintfQualifierPointer;
break;
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'd':
case 'i':
flags |= __PRINTF_FLAG_SIGNED;
case 'u':
break;
case 'X':
flags |= __PRINTF_FLAG_BIGCHARS;
case 'x':
base = 16;
break;
case '%':
j = i;
continue;
default:
continue;
}
size_t size;
uint64_t number;
switch (qualifier) {
case PrintfQualifierByte:
size = sizeof(unsigned char);
number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
break;
case PrintfQualifierShort:
size = sizeof(unsigned short);
number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
break;
case PrintfQualifierInt:
size = sizeof(unsigned int);
number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
break;
case PrintfQualifierLong:
size = sizeof(unsigned long);
number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
break;
case PrintfQualifierLongLong:
size = sizeof(unsigned long long);
number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
break;
case PrintfQualifierPointer:
size = sizeof(void *);
precision = size << 1;
number = (uint64_t) (uintptr_t) va_arg(ap, void *);
break;
case PrintfQualifierSize:
size = sizeof(size_t);
number = (uint64_t) va_arg(ap, size_t);
break;
case PrintfQualifierMax:
size = sizeof(uintmax_t);
number = (uint64_t) va_arg(ap, uintmax_t);
break;
default:
counter = -counter;
goto out;
}
if ((retval = print_number(number, width, precision,
base, flags, ps)) < 0) {
counter = -counter;
goto out;
}
counter += retval;
j = nxt;
}
}
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
counter = -counter;
goto out;
}
counter += retval;
}
out:
return ((int) counter);
}
HelenOS homepage, sources at GitHub