uprintf/uprintf.c

385 lines
10 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdint.h>
#include <stdarg.h>
#include "uprintf.h"
#ifdef UPRINTF_CUSTOM_DIV_FUNC
#include "idiv.h"
#endif
/*
* ------------------- Простая замена printf ------------------------
*
* Спецификаторы в форматной строке так же как и в printf начинаются
* со знака %. После этого знака может идти спецификатор типа с
* опциональным указанием ширины выравнивания, символа для
* выравнивания и направления выравнивания.
*
* Первым должен идти спецификатор символа выравнивания - знак '
* (одинарная скобка). После него - символ для выравнивания.
*
* По умолчанию числа выравниваются символом '0' по правому краю, а
* строки пробелом по левому краю.
*
* Далее идет указатель ширины - десятичное число со знаком,
* обозначающее минимальную длину выводимой строки. Если строка
* получается меньше этого числа, недостающая длина добирается
* символами выравнивания. Если ширина указана в виде отрицательного
* числа, то выравнивание выполняется по правому краю. Если для числа
* не указан символ выравнивания, то оно всегда выравнивается символом
* '0' по правому краю. Вместо числа может стоять символ '*',
* говоряший о том, что ширину нужно брать из аргумента функции.
*
* Спецификатор типа может иметь префикс 'l', обозначающий длинное целое
* (64 бита) и/или префикс 'u', обозначающий беззнаковое число.
*
* Спецификаторы типа:
* d, i Десятичное целое.
* o Восьмиричное целое.
* b Двоичное целое.
* x Шестнадцатиричное целое (строчными).
* X Шестнадцатиричное целое (прописные).
* c Символ.
* s Строка. Если указана ширина, то выравнивание по левому краю.
*
* Пример:
* p("I:+%'=-6i+\n", 10);
* Вывод: I:+====10+
*
*/
static const char abet[2][16] = {{ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' },
{ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }};
typedef enum {
PS_REGULAR = 0,
PS_JSYM_SPEC,
PS_JSYM,
PS_WIDTH_SIGN,
PS_WIDTH_ARG,
PS_WIDTH,
PS_TYPE,
} pstate;
static int l_strlen(const char *str)
{
int l = 0;
while (*str++) l++;
return l;
}
/* Helper functions for p() */
static void print_string(put_char_func pc, const char *str, int width, char wchr)
{
int sl, w;
if (width < 0)
{
sl = l_strlen(str);
for (w = -width; w > sl; w --)
pc(wchr);
}
for (sl = 0; *str; str ++, sl ++)
pc(*str);
if (width > 0)
{
for (w = width; w > sl; w --)
pc(wchr);
}
}
static void print_decimal(put_char_func pc, uint64_t u, int negative, unsigned int base, int width, int lcase, char wchr)
{
if (base > 16) base = 16;
if (base < 2) base = 2;
if (lcase != 0) lcase = 1;
char s[66];
int si = 64;
s[si--] = 0;
do {
uint64_t l;
/* Speed up of special cases */
switch (base) {
case 2:
l = u & 1;
u >>= 1;
break;
case 4:
l = u & 3;
u >>= 2;
break;
case 8:
l = u & 7;
u >>= 3;
break;
case 16:
l = u & 15;
u >>= 4;
break;
default:
#ifdef UPRINTF_CUSTOM_DIV_FUNC
u = idiv64_uu(u, (uint64_t)base, &l);
#else
l = u % base;
u = u / base;
#endif
}
s[si--] = abet[lcase][l];
} while (u > 0);
if (negative) {
if (wchr == '0') {
pc('-');
if (width > 0) width --;
else if (width < 0) width ++;
}
else
s[si--] = '-';
}
si++;
print_string(pc, s+si, width, wchr);
}
static void print_unsigned(put_char_func pc, uint64_t s, unsigned int base, int width, int lcase, char wchr)
{
print_decimal(pc, s, 0, base, width, lcase, wchr);
}
static void print_signed(put_char_func pc, int64_t s, unsigned int base, int width, int lcase, char wchr)
{
if (s < 0)
print_decimal(pc, (uint64_t)-s, 1, base, width, lcase, wchr);
else
print_decimal(pc, (uint64_t)s, 0, base, width, lcase, wchr);
}
static int l_isdigit(char c)
{
return (c >= '0' && c <= '9') ? 1 : 0;
}
/* Like a vprintf */
void pv(put_char_func pc, const char *fmt, va_list ap)
{
/* Initialization for supress gcc warnings */
int width = 0,
wsign = 1,
lng = 0,
sgn = 0,
ab = 0;
/* Width adjustment character */
char wchr = 0;
unsigned int base = 0;
char c;
int64_t d;
pstate st = PS_REGULAR;
for(;;)
{
c = *fmt;
if (c == 0) break;
switch (st)
{
/* ---------------------------------------------------------- */
case PS_REGULAR:
fmt ++;
if (c == '%') {
st = PS_JSYM_SPEC;
lng = 0;
sgn = 1;
width = 0;
wsign = 1;
base = 10;
ab = 0;
wchr = 0;
}
else
pc(c);
break;
/* ---------------------------------------------------------- */
case PS_JSYM_SPEC:
if (c == '\'') {
fmt ++;
st = PS_JSYM;
}
else
st = PS_WIDTH_SIGN;
break;
/* ---------------------------------------------------------- */
case PS_JSYM:
fmt ++;
wchr = c;
st = PS_WIDTH_SIGN;
break;
/* ---------------------------------------------------------- */
case PS_WIDTH_SIGN:
if (c == '-') {
fmt ++;
wsign = -1;
}
st = PS_WIDTH_ARG;
break;
/* ---------------------------------------------------------- */
case PS_WIDTH_ARG:
if (c == '*') {
fmt ++;
width = va_arg(ap, int);
st = PS_TYPE;
} else
st = PS_WIDTH;
break;
/* ---------------------------------------------------------- */
case PS_WIDTH:
if (l_isdigit(c)) {
fmt ++;
width = width * 10 + (c - '0');
} else
st = PS_TYPE;
break;
/* ---------------------------------------------------------- */
case PS_TYPE:
fmt ++;
switch (c)
{
case 'l':
lng = 1;
continue;
case 'u':
sgn = 0;
continue;
case 'd':
case 'i':
case 'b':
case 'o':
case 'x':
case 'X':
if (( lng) && ( sgn)) d = (int64_t)va_arg(ap, long long);
else if (( lng) && (!sgn)) d = (int64_t)va_arg(ap, unsigned long long);
else if ((!lng) && ( sgn)) d = (int64_t)va_arg(ap, int);
else d = (int64_t)va_arg(ap, unsigned int);
ab = 0;
switch (c)
{
case 'd':
case 'i': base = 10; break;
case 'b': base = 2; break;
case 'o': base = 8; break;
case 'x': ab = 1;
case 'X': base = 16; break;
default: break;
}
if (!wchr) {
wchr = '0';
wsign = -1;
}
if (sgn)
print_signed(pc, d, base, (wsign > 0) ? width : -width, ab, wchr);
else
print_unsigned(pc, (uint64_t)d, base, (wsign > 0) ? width : -width, ab, wchr);
break;
case 'c':
pc((char)va_arg(ap, int));
break;
case 's':
if (!wchr) wchr = ' ';
print_string(pc, va_arg(ap, char*), (wsign > 0) ? width : -width, wchr);
break;
case '%':
pc('%');
break;
default:
pc('%');
pc(c);
}
st = PS_REGULAR;
break;
/* ---------------------------------------------------------- */
default:
st = PS_REGULAR;
}
}
}
/* Universal printf */
void pp(put_char_func pc, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
pv(pc, fmt, ap);
va_end(ap);
}
/* Print to console */
void p(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
pv(put_char, fmt, ap);
va_end(ap);
}
/* Print to string */
int psn(char *str, int size, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = 0;
/* Nested function. GCC specific */
void put_char_str(char c)
{
if (n < size) {
*str++ = c;
n++;
}
}
pv(put_char_str, fmt, ap);
va_end(ap);
return n;
}