Initial commit
This commit is contained in:
commit
6bcbbfd725
7
Makefile
Normal file
7
Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
all: test
|
||||||
|
|
||||||
|
test: test.c uprintf.c uprintf.h
|
||||||
|
gcc -Os -m32 -o test test.c uprintf.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf test
|
||||||
83
README.md
Normal file
83
README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
## Simple printf replacement
|
||||||
|
|
||||||
|
The conversion specificator in the format string as in printf begins
|
||||||
|
with a % character. This character can be followed by a type specifier
|
||||||
|
with an optional of the padding width, padding character and direction
|
||||||
|
of the padding.
|
||||||
|
|
||||||
|
By default numbers are right-justified with '0' and strings are
|
||||||
|
left-justified with a space. To specify an alignment character precede
|
||||||
|
the width indicator with a single quote (') and an alignment
|
||||||
|
character.
|
||||||
|
|
||||||
|
Next comes the width indicator - a signed decimal number indicating
|
||||||
|
the minimum length of the output string. If the string is less than
|
||||||
|
this number, the missing length is taken up by alignment
|
||||||
|
characters. If the width is specified as a negative number, the
|
||||||
|
alignment is right-justified. If no alignment character is specified
|
||||||
|
for a number, it is always right-justified with '0'. Instead of a
|
||||||
|
number for width you can be use a '*' symbol, which means that the
|
||||||
|
width must be taken from the function argument.
|
||||||
|
|
||||||
|
The type specifier can be prefixed with 'l' for a long integer (64
|
||||||
|
bits) and / or prefix 'u' for an unsigned number.
|
||||||
|
|
||||||
|
Type specifiers:
|
||||||
|
- d, i Decimal integer.
|
||||||
|
- o Octal whole.
|
||||||
|
- b Binary integer.
|
||||||
|
- x Hexadecimal integer (lowercase).
|
||||||
|
- X Hexadecimal integer (uppercase).
|
||||||
|
- c Symbol.
|
||||||
|
- s String. If width is specified, then left alignment.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
p ("I: +% '= - 6i + \ n", 10);
|
||||||
|
|
||||||
|
Output:
|
||||||
|
I: + ==== 10+
|
||||||
|
```
|
||||||
|
|
||||||
|
## Простая замена printf
|
||||||
|
|
||||||
|
Спецификаторы в форматной строке так же как и в printf начинаются со
|
||||||
|
знака %. После этого знака может идти спецификатор типа с опциональным
|
||||||
|
указанием ширины выравнивания, символа для выравнивания и направления
|
||||||
|
выравнивания.
|
||||||
|
|
||||||
|
По умолчанию числа выравниваются символом '0' по правому краю, а
|
||||||
|
строки пробелом по левому краю. Чтобы задать символ выравниявания,
|
||||||
|
перед указателем ширины нужно поставить одинарную кавычку (') и символ
|
||||||
|
выравнивания.
|
||||||
|
|
||||||
|
Далее идет указатель ширины - десятичное число со знаком, обозначающее
|
||||||
|
минимальную длину выводимой строки. Если строка получается меньше
|
||||||
|
этого числа, недостающая длина добирается символами выравнивания. Если
|
||||||
|
ширина указана в виде отрицательного числа, то выравнивание
|
||||||
|
выполняется по правому краю. Если для числа не указан символ
|
||||||
|
выравнивания, то оно всегда выравнивается символом '0' по правому
|
||||||
|
краю. Вместо числа может стоять символ '*', говоряший о том, что
|
||||||
|
ширину нужно брать из аргумента функции.
|
||||||
|
|
||||||
|
Спецификатор типа может иметь префикс 'l', обозначающий длинное целое
|
||||||
|
(64 бита) и/или префикс 'u', обозначающий беззнаковое число.
|
||||||
|
|
||||||
|
Спецификаторы типа:
|
||||||
|
- d, i Десятичное целое.
|
||||||
|
- o Восьмиричное целое.
|
||||||
|
- b Двоичное целое.
|
||||||
|
- x Шестнадцатиричное целое (строчными).
|
||||||
|
- X Шестнадцатиричное целое (прописные).
|
||||||
|
- c Символ.
|
||||||
|
- s Строка. Если указана ширина, то выравнивание по левому краю.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```
|
||||||
|
p("I:+%'=-6i+\n", 10);
|
||||||
|
|
||||||
|
Вывод:
|
||||||
|
I:+====10+
|
||||||
|
```
|
||||||
26
test.c
Normal file
26
test.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "uprintf.h"
|
||||||
|
|
||||||
|
void put_char(char c)
|
||||||
|
{
|
||||||
|
fputc(c, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
p("Hello %s!\n", "ALL");
|
||||||
|
p("1: |%4i:%4i:%4i|\n", 1, -2, 3);
|
||||||
|
p("2: |%-4i:%-4i:%-4i|\n", 1, -2, 3);
|
||||||
|
p("3: |%' 4i:%'.4i:%''4i|\n", 1, -2, 3);
|
||||||
|
p("4: |%' -4i:%'.-4i:%''-4i|\n", 1, -2, 3);
|
||||||
|
|
||||||
|
p("5: |%4s:%4s:%4s|\n", "a", "b", "c");
|
||||||
|
p("6: |%-4s:%-4s:%-4s|\n", "a", "b", "c");
|
||||||
|
|
||||||
|
p("7: |%*s|\n", 5, "s");
|
||||||
|
p("8: |%'-*s|\n", -5, "s");
|
||||||
|
|
||||||
|
/* TODO more tests */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
349
uprintf.c
Normal file
349
uprintf.c
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include "uprintf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ------------------- Простая замена 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;
|
||||||
|
|
||||||
|
if (width < 0)
|
||||||
|
{
|
||||||
|
sl = l_strlen(str);
|
||||||
|
for (int w = -width; w > sl; w --)
|
||||||
|
pc(wchr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (sl = 0; *str; str ++, sl ++)
|
||||||
|
pc(*str);
|
||||||
|
|
||||||
|
if (width > 0)
|
||||||
|
{
|
||||||
|
for (int 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 {
|
||||||
|
int l = (int)(u % base);
|
||||||
|
u = u / base;
|
||||||
|
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;
|
||||||
|
}
|
||||||
15
uprintf.h
Normal file
15
uprintf.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _UPRINTF
|
||||||
|
#define _UPRINTF
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef void (*put_char_func)(char c);
|
||||||
|
extern void put_char(char c);
|
||||||
|
|
||||||
|
void pv(put_char_func pc, const char *fmt, va_list ap);
|
||||||
|
void pp(put_char_func pc, const char *fmt, ...);
|
||||||
|
|
||||||
|
void p(const char *fmt, ...);
|
||||||
|
int psn(char *str, int size, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif // _UPRINTF
|
||||||
Loading…
x
Reference in New Issue
Block a user