Initial commit

This commit is contained in:
Николай Пузанов 2020-12-13 09:51:25 +03:00
commit 6bcbbfd725
5 changed files with 480 additions and 0 deletions

7
Makefile Normal file
View 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
View 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
View 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
View 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
View 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