Vhex-kernel/src/lib/libc/stdio/internal/printf_actions.c

197 lines
4.6 KiB
C

#include <stdio.h>
// Define all actions
static void action_str(struct printf_opt *op, char n);
static void action_ptr(struct printf_opt *op, char n);
static void action_int(struct printf_opt *op, char n);
static void action_uint(struct printf_opt *op, char n);
static void action_char(struct printf_opt *op, char n);
// Define all actions which can be used
void (*action[26])(struct printf_opt *opt, char n) = {
NULL, NULL, action_char, action_int,
NULL, NULL, NULL, NULL,
action_int, NULL, NULL, NULL,
NULL, NULL, action_uint, action_ptr,
NULL, NULL, action_str, NULL,
action_uint, NULL, NULL, action_uint,
NULL, NULL,
};
//---
// Disp part
//---
static void base_to_str(struct printf_opt *opt, uint64_t num, int base, int digits)
{
char *hexa = (opt->uppercase == 1) ? "0123456789ABCDEF" : "0123456789abcdef";
opt->digits = 0;
while (num != 0 || opt->digits < digits)
{
opt->format[opt->digits++] = hexa[num % base];
num = num / base;
}
}
static void disp_format(struct printf_opt *opt)
{
int total;
// display pre symbols
if (opt->sign != '\0')
(*opt->disp_char)(opt, opt->sign);
if (opt->base[0] != '\0')
(*opt->disp_char)(opt, opt->base[0]);
if (opt->base[1] != '\0')
(*opt->disp_char)(opt, opt->base[1]);
// padding (left)
if ((opt->flags.minus == 1 || opt->flags.zero == 1) && opt->width > opt->digits)
{
total = opt->digits + (opt->sign != '\0') + (opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - (total + opt->precision);
while (--total >= 0)
(*opt->disp_char)(opt, (opt->flags.minus == 1) ? ' ' : '0');
}
// Precision
if (opt->precision > opt->digits)
{
total = opt->precision - opt->digits;
while (--total >= 0)
(*opt->disp_char)(opt, '0');
}
// Display number
int saved_digits = opt->digits;
while (--opt->digits >= 0)
(*opt->disp_char)(opt, opt->format[opt->digits]);
// padding (right)
if (opt->flags.minus == 0 && opt->flags.zero == 0 && opt->width > saved_digits)
{
int total = saved_digits + (opt->sign != '\0') +
(opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - total;
while (--total >= 0)
(*opt->disp_char)(opt, ' ');
}
}
//---
// Args part
//---
static uint64_t get_arg_i(struct printf_opt *opt)
{
switch (opt->lenght)
{
case 0: return ((signed char)va_arg(opt->ap, int));
case 1: return ((short int)va_arg(opt->ap, int));
case 2: return (va_arg(opt->ap, long int));
case 3: return (va_arg(opt->ap, long long int));
case 4: return (va_arg(opt->ap, intmax_t));
case 5: return (va_arg(opt->ap, size_t));
case 6: return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, int));
}
static uint64_t get_arg_u(struct printf_opt *opt)
{
switch (opt->lenght)
{
case 0: return ((unsigned char)va_arg(opt->ap, int));
case 1: return ((unsigned short int)va_arg(opt->ap, int));
case 2: return (va_arg(opt->ap, unsigned long int));
case 3: return (va_arg(opt->ap, unsigned long long int));
case 4: return (va_arg(opt->ap, intmax_t));
case 5: return (va_arg(opt->ap, size_t));
case 6: return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, unsigned int));
}
//---
// Actions part.
//---
static void action_str(struct printf_opt *opt, char n)
{
const char *str;
(void)n;
str = va_arg(opt->ap, const char *);
while (*str != '\0')
(*opt->disp_char)(opt, *(str++));
}
static void action_char(struct printf_opt *opt, char n)
{
n = (char)va_arg(opt->ap, int);
(*opt->disp_char)(opt, n);
}
static void action_ptr(struct printf_opt *opt, char n)
{
(void)n;
opt->sign = '@';
opt->base[0] = '0';
opt->base[1] = 'x';
base_to_str(opt, (uint32_t)va_arg(opt->ap, void*), 16, 8);
disp_format(opt);
}
static void action_int(struct printf_opt *opt, char n)
{
int64_t num;
// Get data and check negative value
// FIXME: max negative value can not be reversed
(void)n;
num = get_arg_i(opt);
if (num < 0) {
opt->sign = '-';
num = -num;
} else if (opt->flags.space == 1 || opt->flags.plus == 1) {
opt->sign = (opt->flags.plus == 1) ? '+' : ' ';
}
// Generate / display number
base_to_str(opt, num, 10, 1);
disp_format(opt);
}
static void action_uint(struct printf_opt *opt, char n)
{
uint64_t num;
int base;
// Get appropriate base
switch (n)
{
case 'o': base = 8; break;
case 'x': base = 16; break;
default: base = 10; break;
}
// Display extra symbols if needed
if (opt->flags.diez == 1)
{
if (n == 'o') {
opt->base[0] = '0';
} else if (n == 'x') {
opt->base[0] = '0';
opt->base[1] = (opt->uppercase == 1) ? 'X' : 'x';
}
}
// Get number
num = get_arg_u(opt);
// Generate / display number
base_to_str(opt, num, base, 1);
disp_format(opt);
}