blob: 58f785612ddce45d09ee77928f560677bb089c07 [file] [log] [blame]
// Stripped-down primitive printf-style formatting routines,
// used in common by printf, sprintf, fprintf, etc.
// This code is also used by both the kernel and user programs.
#ifdef __SHARC__
#pragma nosharc
#endif
#include <ros/common.h>
#include <error.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ip.h>
#include <kthread.h>
/* to avoid all this inclusion of the universe. */
extern uint8_t v4prefix[IPaddrlen];
enum {
Isprefix = 16,
};
uint8_t prefixvals[256] = {
[0x00] 0 | Isprefix,
[0x80] 1 | Isprefix,
[0xC0] 2 | Isprefix,
[0xE0] 3 | Isprefix,
[0xF0] 4 | Isprefix,
[0xF8] 5 | Isprefix,
[0xFC] 6 | Isprefix,
[0xFE] 7 | Isprefix,
[0xFF] 8 | Isprefix,
};
/* Print a number (base <= 16) in reverse order,
* using specified putch function and associated pointer putdat. */
void printnum(void (*putch) (int, void **), void **putdat,
unsigned long long num, unsigned base, int width, int padc)
{
unsigned long long temp = num;
int nr_digits = 1;
/* Determine how many leading zeros we need.
* For every digit/nibble beyond base, we do one less width padding */
while ((temp /= base)) {
nr_digits++;
width--;
}
/* And another one less, since we'll always print the last digit */
while (--width > 0)
putch(padc, putdat);
for (int i = nr_digits; i > 0; i--) {
temp = num;
/* To get digit i, we only div (i-1) times */
for (int j = 0; j < i - 1; j++) {
temp /= base;
}
putch("0123456789abcdef"[temp % base], putdat);
}
}
// Main function to format and print a string.
#ifdef __DEPUTY__
void printfmt(void (*putch) (int, TV(t)), TV(t) putdat, const char *fmt, ...);
#else
void printfmt(void (*putch) (int, void **), void **putdat, const char *fmt,
...);
#endif
#ifdef __DEPUTY__
void vprintfmt(void (*putch) (int, TV(t)), TV(t) putdat, const char *fmt,
va_list ap)
#else
void vprintfmt(void (*putch) (int, void **), void **putdat, const char *fmt,
va_list ap)
#endif
{
register const char *NTS p;
uint8_t *up;
uint32_t *lp;
uint16_t s;
const char *NTS last_fmt;
register int ch, err;
unsigned long long num;
int base, lflag, width, precision, altflag;
char padc;
int i, j, n, eln, eli;
while (1) {
while ((ch = *(unsigned char *)fmt) != '%') {
if (ch == '\0')
return;
fmt++;
putch(ch, putdat);
}
fmt++;
// Process a %-escape sequence
last_fmt = fmt;
padc = ' ';
width = -1;
precision = -1;
lflag = 0;
altflag = 0;
reswitch:
switch (ch = *(unsigned char *)fmt++) {
// flag to pad on the right
case '-':
padc = '-';
goto reswitch;
// flag to pad with 0's instead of spaces
case '0':
padc = '0';
goto reswitch;
// width field
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (precision = 0;; ++fmt) {
precision = precision * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
goto process_precision;
case '*':
precision = va_arg(ap, int);
goto process_precision;
case '.':
if (width < 0)
width = 0;
goto reswitch;
case '#':
altflag = 1;
goto reswitch;
process_precision:
if (width < 0)
width = precision, precision = -1;
goto reswitch;
// long flag (doubled for long long)
case 'l':
lflag++;
goto reswitch;
// character
case 'c':
putch(va_arg(ap, int), putdat);
break;
// error message
case 'e':
err = va_arg(ap, int);
if (err < 0)
err = -err;
if (err >= NUMERRORS)
printfmt(putch, putdat, "error %d", err);
else
printfmt(putch, putdat, "%s", error_string[err]);
break;
// string
case 's':
if ((p = va_arg(ap, char *)) == NULL)
p = "(null)";
if (width > 0 && padc != '-')
for (width -= strnlen(p, precision); width > 0; width--)
putch(padc, putdat);
for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0);
width--) {
if (altflag && (ch < ' ' || ch > '~'))
putch('?', putdat);
else
putch(ch, putdat);
// zra: make sure *p isn't '\0' before inc'ing
p++;
}
for (; width > 0; width--)
putch(' ', putdat);
break;
case 'd': /* (signed) decimal */
if (lflag >= 2)
num = va_arg(ap, long long);
else if (lflag)
num = va_arg(ap, long);
else
num = va_arg(ap, int);
if ((long long)num < 0) {
putch('-', putdat);
num = -(long long)num;
}
base = 10;
goto number;
case 'u': /* unsigned decimal */
case 'o': /* (unsigned) octal */
case 'x': /* (unsigned) hexadecimal */
if (lflag >= 2)
num = va_arg(ap, unsigned long long);
else if (lflag)
num = va_arg(ap, unsigned long);
else
num = va_arg(ap, unsigned int);
if (ch == 'u')
base = 10;
else if (ch == 'o')
base = 8;
else /* x */
base = 16;
goto number;
// pointer
case 'p':
putch('0', putdat);
putch('x', putdat);
/* automatically zero-pad pointers, out to the length of a ptr */
padc = '0';
width = sizeof(void *) * 2; /* 8 bits per byte / 4 bits per char */
num = (unsigned long long)
(uintptr_t) va_arg(ap, void *);
base = 16;
goto number;
number:
printnum(putch, putdat, num, base, width, padc);
break;
// escaped '%' character
case '%':
putch(ch, putdat);
break;
case 'E': /* Ethernet address */
if ((up = va_arg(ap, uint8_t *)) == NULL)
up = (uint8_t *) "xxxxxx";
for (i = 0; i < 6; i++)
printnum(putch, putdat, up[i], 16, 2, '0');
break;
case 'I': /* Ip address */
up = va_arg(ap, uint8_t *);
common:
if (memcmp(up, v4prefix, 12) == 0) {
printnum(putch, putdat, up[12], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[13], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[14], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[15], 10, 0, ' ');
break;
}
/* find longest elision */
eln = eli = -1;
for (i = 0; i < 16; i += 2) {
for (j = i; j < 16; j += 2)
if (up[j] != 0 || up[j + 1] != 0)
break;
if (j > i && j - i > eln) {
eli = i;
eln = j - i;
}
}
/* print with possible elision */
n = 0;
for (i = 0; i < 16; i += 2) {
if (i == eli) {
putch(':', putdat);
putch(':', putdat);
n += 2;
i += eln;
if (i >= 16)
break;
} else if (i != 0)
putch(':', putdat);
n++;
s = (up[i] << 8) + up[i + 1];
printnum(putch, putdat, s, 16, 4, 0);
n += 4;
}
break;
case 'i': /* v6 address as 4 longs */
lp = va_arg(ap, uint32_t *);
for (i = 0; i < 4; i++)
printnum(putch, putdat, *lp++, 16, 8, 0);
up = (uint8_t *) lp;
goto common;
case 'V': /* v4 ip address */
up = va_arg(ap, uint8_t *);
printnum(putch, putdat, up[12], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[13], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[14], 10, 0, ' ');
putch('.', putdat);
printnum(putch, putdat, up[15], 10, 0, ' ');
break;
case 'M': /* ip mask */
up = va_arg(ap, uint8_t *);
/* look for a prefix mask */
for (i = 0; i < 16; i++)
if (up[i] != 0xff)
break;
if (i < 16) {
if ((prefixvals[up[i]] & Isprefix) == 0)
goto common;
for (j = i + 1; j < 16; j++)
if (up[j] != 0)
goto common;
n = 8 * i + (prefixvals[up[i]] & ~Isprefix);
} else
n = 8 * 16;
/* got one, use /xx format */
putch('/', putdat);
printnum(putch, putdat, n, 10, 0, 0);
break;
// unrecognized escape sequence - just print it literally
default:
putch('%', putdat);
fmt = last_fmt;
//for (fmt--; fmt[-1] != '%'; fmt--)
/* do nothing */ ;
break;
}
}
}
#ifdef __DEPUTY__
void printfmt(void (*putch) (int, TV(t)), TV(t) putdat, const char *fmt, ...)
#else
void printfmt(void (*putch) (int, void **), void **putdat, const char *fmt, ...)
#endif
{
va_list ap;
va_start(ap, fmt);
vprintfmt(putch, putdat, fmt, ap);
va_end(ap);
check_poison("printfmt");
}
typedef struct sprintbuf {
char *BND(__this, ebuf) buf;
char *SNT ebuf;
int cnt;
} sprintbuf_t;
static void sprintputch(int ch, sprintbuf_t * NONNULL * NONNULL b)
{
(*b)->cnt++;
if ((*b)->buf < (*b)->ebuf)
*((*b)->buf++) = ch;
}
int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
{
sprintbuf_t b; // = {buf, buf+n-1, 0};
sprintbuf_t *COUNT(1) NONNULL bp = &b;
if (buf == NULL || n < 1)
return -EINVAL;
b.buf = NULL; // zra : help out the Deputy optimizer a bit
b.ebuf = buf + n - 1;
b.cnt = 0;
b.buf = buf;
// print the string to the buffer
#ifdef __DEPUTY__
vprintfmt((void *)sprintputch, (sprintbuf_t * NONNULL * NONNULL) & bp, fmt,
ap);
#else
vprintfmt((void *)sprintputch, (void *)&bp, fmt, ap);
#endif
// null terminate the buffer
*b.buf = '\0';
return b.cnt;
}
int snprintf(char *buf, int n, const char *fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vsnprintf(buf, n, fmt, ap);
va_end(ap);
check_poison("snprintf");
return rc;
}
/* convenience function: do a print, return the pointer to the end. */
char *seprintf(char *buf, char *end, const char *fmt, ...)
{
va_list ap;
int rc;
int n = end - buf;
if (n <= 0)
return buf;
va_start(ap, fmt);
rc = vsnprintf(buf, n, fmt, ap);
va_end(ap);
check_poison("seprintf");
if (rc >= 0)
return buf + rc;
else
return buf;
}