323 lines
5.4 KiB
C
323 lines
5.4 KiB
C
/*
|
|
* libc printf and friends
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Library General Public License version 2.
|
|
*/
|
|
|
|
#include "libcflat.h"
|
|
|
|
#define BUFSZ 2000
|
|
|
|
typedef struct pstream {
|
|
char *buffer;
|
|
int remain;
|
|
int added;
|
|
} pstream_t;
|
|
|
|
typedef struct strprops {
|
|
char pad;
|
|
int npad;
|
|
bool alternate;
|
|
} strprops_t;
|
|
|
|
static void addchar(pstream_t *p, char c)
|
|
{
|
|
if (p->remain) {
|
|
*p->buffer++ = c;
|
|
--p->remain;
|
|
}
|
|
++p->added;
|
|
}
|
|
|
|
static void print_str(pstream_t *p, const char *s, strprops_t props)
|
|
{
|
|
const char *s_orig = s;
|
|
int npad = props.npad;
|
|
|
|
if (npad > 0) {
|
|
npad -= strlen(s_orig);
|
|
while (npad > 0) {
|
|
addchar(p, props.pad);
|
|
--npad;
|
|
}
|
|
}
|
|
|
|
while (*s)
|
|
addchar(p, *s++);
|
|
|
|
if (npad < 0) {
|
|
props.pad = ' '; /* ignore '0' flag with '-' flag */
|
|
npad += strlen(s_orig);
|
|
while (npad < 0) {
|
|
addchar(p, props.pad);
|
|
++npad;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char digits[16] = "0123456789abcdef";
|
|
|
|
static void print_int(pstream_t *ps, long long n, int base, strprops_t props)
|
|
{
|
|
char buf[sizeof(long) * 3 + 2], *p = buf;
|
|
int s = 0, i;
|
|
|
|
if (n < 0) {
|
|
n = -n;
|
|
s = 1;
|
|
}
|
|
|
|
while (n) {
|
|
*p++ = digits[n % base];
|
|
n /= base;
|
|
}
|
|
|
|
if (s)
|
|
*p++ = '-';
|
|
|
|
if (p == buf)
|
|
*p++ = '0';
|
|
|
|
for (i = 0; i < (p - buf) / 2; ++i) {
|
|
char tmp;
|
|
|
|
tmp = buf[i];
|
|
buf[i] = p[-1-i];
|
|
p[-1-i] = tmp;
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
print_str(ps, buf, props);
|
|
}
|
|
|
|
static void print_unsigned(pstream_t *ps, unsigned long long n, int base,
|
|
strprops_t props)
|
|
{
|
|
char buf[sizeof(long) * 3 + 3], *p = buf;
|
|
int i;
|
|
|
|
while (n) {
|
|
*p++ = digits[n % base];
|
|
n /= base;
|
|
}
|
|
|
|
if (p == buf)
|
|
*p++ = '0';
|
|
else if (props.alternate && base == 16) {
|
|
if (props.pad == '0') {
|
|
addchar(ps, '0');
|
|
addchar(ps, 'x');
|
|
|
|
if (props.npad > 0)
|
|
props.npad = MAX(props.npad - 2, 0);
|
|
} else {
|
|
*p++ = 'x';
|
|
*p++ = '0';
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < (p - buf) / 2; ++i) {
|
|
char tmp;
|
|
|
|
tmp = buf[i];
|
|
buf[i] = p[-1-i];
|
|
p[-1-i] = tmp;
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
print_str(ps, buf, props);
|
|
}
|
|
|
|
static int fmtnum(const char **fmt)
|
|
{
|
|
const char *f = *fmt;
|
|
int len = 0, num;
|
|
|
|
if (*f == '-')
|
|
++f, ++len;
|
|
|
|
while (*f >= '0' && *f <= '9')
|
|
++f, ++len;
|
|
|
|
num = atol(*fmt);
|
|
*fmt += len;
|
|
return num;
|
|
}
|
|
|
|
int vsnprintf(char *buf, int size, const char *fmt, va_list va)
|
|
{
|
|
pstream_t s;
|
|
|
|
s.buffer = buf;
|
|
s.remain = size - 1;
|
|
s.added = 0;
|
|
while (*fmt) {
|
|
char f = *fmt++;
|
|
int nlong = 0;
|
|
strprops_t props;
|
|
memset(&props, 0, sizeof(props));
|
|
props.pad = ' ';
|
|
|
|
if (f != '%') {
|
|
addchar(&s, f);
|
|
continue;
|
|
}
|
|
morefmt:
|
|
f = *fmt++;
|
|
switch (f) {
|
|
case '%':
|
|
addchar(&s, '%');
|
|
break;
|
|
case 'c':
|
|
addchar(&s, va_arg(va, int));
|
|
break;
|
|
case '\0':
|
|
--fmt;
|
|
break;
|
|
case '#':
|
|
props.alternate = true;
|
|
goto morefmt;
|
|
case '0':
|
|
props.pad = '0';
|
|
++fmt;
|
|
/* fall through */
|
|
case '1'...'9':
|
|
case '-':
|
|
--fmt;
|
|
props.npad = fmtnum(&fmt);
|
|
goto morefmt;
|
|
case 'l':
|
|
++nlong;
|
|
goto morefmt;
|
|
case 't':
|
|
case 'z':
|
|
/* Here we only care that sizeof(size_t) == sizeof(long).
|
|
* On a 32-bit platform it doesn't matter that size_t is
|
|
* typedef'ed to int or long; va_arg will work either way.
|
|
* Same for ptrdiff_t (%td).
|
|
*/
|
|
nlong = 1;
|
|
goto morefmt;
|
|
case 'd':
|
|
switch (nlong) {
|
|
case 0:
|
|
print_int(&s, va_arg(va, int), 10, props);
|
|
break;
|
|
case 1:
|
|
print_int(&s, va_arg(va, long), 10, props);
|
|
break;
|
|
default:
|
|
print_int(&s, va_arg(va, long long), 10, props);
|
|
break;
|
|
}
|
|
break;
|
|
case 'u':
|
|
switch (nlong) {
|
|
case 0:
|
|
print_unsigned(&s, va_arg(va, unsigned), 10, props);
|
|
break;
|
|
case 1:
|
|
print_unsigned(&s, va_arg(va, unsigned long), 10, props);
|
|
break;
|
|
default:
|
|
print_unsigned(&s, va_arg(va, unsigned long long), 10, props);
|
|
break;
|
|
}
|
|
break;
|
|
case 'x':
|
|
switch (nlong) {
|
|
case 0:
|
|
print_unsigned(&s, va_arg(va, unsigned), 16, props);
|
|
break;
|
|
case 1:
|
|
print_unsigned(&s, va_arg(va, unsigned long), 16, props);
|
|
break;
|
|
default:
|
|
print_unsigned(&s, va_arg(va, unsigned long long), 16, props);
|
|
break;
|
|
}
|
|
break;
|
|
case 'p':
|
|
props.alternate = true;
|
|
print_unsigned(&s, (unsigned long)va_arg(va, void *), 16, props);
|
|
break;
|
|
case 's':
|
|
print_str(&s, va_arg(va, const char *), props);
|
|
break;
|
|
default:
|
|
addchar(&s, f);
|
|
break;
|
|
}
|
|
}
|
|
*s.buffer = 0;
|
|
return s.added;
|
|
}
|
|
|
|
|
|
int snprintf(char *buf, int size, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
int r;
|
|
|
|
va_start(va, fmt);
|
|
r = vsnprintf(buf, size, fmt, va);
|
|
va_end(va);
|
|
return r;
|
|
}
|
|
|
|
int vprintf(const char *fmt, va_list va)
|
|
{
|
|
char buf[BUFSZ];
|
|
int r;
|
|
|
|
r = vsnprintf(buf, sizeof(buf), fmt, va);
|
|
puts(buf);
|
|
return r;
|
|
}
|
|
|
|
int printf(const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
char buf[BUFSZ];
|
|
int r;
|
|
|
|
va_start(va, fmt);
|
|
r = vsnprintf(buf, sizeof buf, fmt, va);
|
|
va_end(va);
|
|
puts(buf);
|
|
return r;
|
|
}
|
|
|
|
void binstr(unsigned long x, char out[BINSTR_SZ])
|
|
{
|
|
int i;
|
|
char *c;
|
|
int n;
|
|
|
|
n = sizeof(unsigned long) * 8;
|
|
i = 0;
|
|
c = &out[0];
|
|
for (;;) {
|
|
*c++ = (x & (1ul << (n - i - 1))) ? '1' : '0';
|
|
i++;
|
|
|
|
if (i == n) {
|
|
*c = '\0';
|
|
break;
|
|
}
|
|
if (i % 4 == 0)
|
|
*c++ = '\'';
|
|
}
|
|
assert(c + 1 - &out[0] == BINSTR_SZ);
|
|
}
|
|
|
|
void print_binstr(unsigned long x)
|
|
{
|
|
char out[BINSTR_SZ];
|
|
binstr(x, out);
|
|
printf("%s", out);
|
|
}
|