mirror of https://github.com/l4ka/hazelnut.git
544 lines
10 KiB
C
544 lines
10 KiB
C
/*********************************************************************
|
|
*
|
|
* Copyright (C) 2001, Karlsruhe University
|
|
*
|
|
* File path: lib/io/print.c
|
|
* Description: fully featured printf
|
|
*
|
|
* @LICENSE@
|
|
*
|
|
* $Id: print.c,v 1.11 2001/11/30 14:24:22 ud3 Exp $
|
|
*
|
|
********************************************************************/
|
|
#include <l4/l4.h>
|
|
#include <stdarg.h>
|
|
#include <l4io.h>
|
|
|
|
/*
|
|
* Function strlen (s)
|
|
*
|
|
* Return the length of string `s', not including the terminating
|
|
* `\0' character.
|
|
*
|
|
*/
|
|
static int strlen(const char *s)
|
|
{
|
|
int n;
|
|
|
|
for ( n = 0; *s++ != '\0'; n++ )
|
|
;
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function print_string (s)
|
|
*
|
|
* Print string `s' to terminal (or some kind of output).
|
|
*
|
|
*/
|
|
static void print_string(char *s)
|
|
{
|
|
while ( *s )
|
|
putc(*s++);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function vsnprintf (str, size, fmt, ap)
|
|
*
|
|
* Formated output conversion according to format string `fmt' and
|
|
* argument list `ap'. The formated string is stored in `out'. If
|
|
* the formated string is longer tha `outsize' characters, the
|
|
* string is truncated.
|
|
*
|
|
* The format conversion works as in printf(3), except for the
|
|
* conversions; e, E, g, and G which are not supported.
|
|
*
|
|
*/
|
|
int vsnprintf(char *str, int size, const char *fmt, va_list ap)
|
|
{
|
|
char convert[64];
|
|
int f_left, f_sign, f_space, f_zero, f_alternate;
|
|
int f_long, f_short, f_ldouble;
|
|
int width, precision;
|
|
int i, c, base, sign, signch, numdigits, numpr = 0, someflag;
|
|
unsigned int uval, uval2;
|
|
double fval;
|
|
char *digits, *string, *p = str;
|
|
l4_threadid_t tid;
|
|
|
|
#define PUTCH(ch) do { \
|
|
if ( size-- <= 0 ) \
|
|
goto Done; \
|
|
*p++ = (ch); \
|
|
} while (0)
|
|
|
|
#define PUTSTR(st) do { \
|
|
char *s = (st); \
|
|
while ( *s != '\0' ) { \
|
|
if ( size-- <= 0 ) \
|
|
goto Done; \
|
|
*p++ = *s++; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define PUTDEC(num) do { \
|
|
uval = num; \
|
|
numdigits = 0; \
|
|
do { \
|
|
convert[ numdigits++ ] = digits[ uval % 10 ]; \
|
|
uval /= 10; \
|
|
} while ( uval > 0 ); \
|
|
while ( numdigits > 0 ) \
|
|
PUTCH( convert[--numdigits] ); \
|
|
} while (0)
|
|
|
|
#define LEFTPAD(num) do { \
|
|
int cnt = (num); \
|
|
if ( ! f_left && cnt > 0 ) \
|
|
while ( cnt-- > 0 ) \
|
|
PUTCH( f_zero ? '0' : ' ' ); \
|
|
} while (0)
|
|
|
|
#define RIGHTPAD(num) do { \
|
|
int cnt = (num); \
|
|
if ( f_left && cnt > 0 ) \
|
|
while ( cnt-- > 0 ) \
|
|
PUTCH( ' ' ); \
|
|
} while (0)
|
|
|
|
/*
|
|
* Make room for null-termination.
|
|
*/
|
|
size--;
|
|
|
|
/*
|
|
* Sanity check on fmt string.
|
|
*/
|
|
if ( fmt == NULL ) {
|
|
PUTSTR( "(null fmt string)" );
|
|
goto Done;
|
|
}
|
|
|
|
while ( size > 0 ) {
|
|
/*
|
|
* Copy characters until we encounter '%'.
|
|
*/
|
|
while ( (c = *fmt++) != '%' ) {
|
|
if ( size-- <= 0 || c == '\0' )
|
|
goto Done;
|
|
*p++ = c;
|
|
}
|
|
|
|
f_left = f_sign = f_space = f_zero = f_alternate = 0;
|
|
someflag = 1;
|
|
|
|
/*
|
|
* Parse flags.
|
|
*/
|
|
while ( someflag ) {
|
|
switch ( *fmt ) {
|
|
case '-': f_left = 1; fmt++; break;
|
|
case '+': f_sign = 1; fmt++; break;
|
|
case ' ': f_space = 1; fmt++; break;
|
|
case '0': f_zero = 1; fmt++; break;
|
|
case '#': f_alternate = 1; fmt++; break;
|
|
default: someflag = 0; break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse field width.
|
|
*/
|
|
if ( (c = *fmt) == '*' ) {
|
|
width = va_arg( ap, int );
|
|
fmt++;
|
|
} else if ( c >= '0' && c <= '9' ) {
|
|
width = 0;
|
|
while ( (c = *fmt++) >= '0' && c <= '9' ) {
|
|
width *= 10;
|
|
width += c - '0';
|
|
}
|
|
fmt--;
|
|
} else {
|
|
width = -1;
|
|
}
|
|
|
|
/*
|
|
* Parse precision.
|
|
*/
|
|
if ( *fmt == '.' ) {
|
|
if ( (c = *++fmt) == '*' ) {
|
|
precision = va_arg( ap, int );
|
|
fmt++;
|
|
} else if ( c >= '0' && c <= '9' ) {
|
|
precision = 0;
|
|
while ( (c = *fmt++) >= '0' && c <= '9' ) {
|
|
precision *= 10;
|
|
precision += c - '0';
|
|
}
|
|
fmt--;
|
|
} else {
|
|
precision = -1;
|
|
}
|
|
} else {
|
|
precision = -1;
|
|
}
|
|
|
|
f_long = f_short = f_ldouble = 0;
|
|
|
|
/*
|
|
* Parse length modifier.
|
|
*/
|
|
switch ( *fmt ) {
|
|
case 'h': f_short = 1; fmt++; break;
|
|
case 'l': f_long = 1; fmt++; break;
|
|
case 'L': f_ldouble = 1; fmt++; break;
|
|
}
|
|
|
|
sign = 1;
|
|
|
|
/*
|
|
* Parse format conversion.
|
|
*/
|
|
switch ( c = *fmt++ ) {
|
|
|
|
case 'b':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 2;
|
|
digits = "01";
|
|
goto Print_unsigned;
|
|
|
|
case 'o':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 8;
|
|
digits = "012345678";
|
|
goto Print_unsigned;
|
|
|
|
case 'p':
|
|
precision = width = 8;
|
|
f_alternate = 1;
|
|
case 'x':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 16;
|
|
digits = "0123456789abcdef";
|
|
goto Print_unsigned;
|
|
|
|
case 'X':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 16;
|
|
digits = "0123456789ABCDEF";
|
|
goto Print_unsigned;
|
|
|
|
case 'd':
|
|
case 'i':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 10;
|
|
digits = "0123456789";
|
|
goto Print_signed;
|
|
|
|
case 'u':
|
|
uval = f_long ? va_arg( ap, long ) : va_arg( ap, int );
|
|
base = 10;
|
|
digits = "0123456789";
|
|
|
|
Print_unsigned:
|
|
sign = 0;
|
|
|
|
Print_signed:
|
|
signch = 0;
|
|
uval2 = uval;
|
|
|
|
/*
|
|
* Check for sign character.
|
|
*/
|
|
if ( sign ) {
|
|
if ( f_sign && (long) uval >= 0 ) {
|
|
signch = '+';
|
|
} else if ( f_space && (long) uval >= 0 ) {
|
|
signch = ' ';
|
|
} else if ( (long) uval < 0 ) {
|
|
signch = '-';
|
|
uval = -( (long) uval );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create reversed number string.
|
|
*/
|
|
numdigits = 0;
|
|
do {
|
|
convert[ numdigits++ ] = digits[ uval % base ];
|
|
uval /= base;
|
|
} while ( uval > 0 );
|
|
|
|
/*
|
|
* Calculate the actual size of the printed number.
|
|
*/
|
|
numpr = numdigits > precision ? numdigits : precision;
|
|
if ( signch )
|
|
numpr++;
|
|
if ( f_alternate && uval2 != 0 ) {
|
|
if ( base == 8 )
|
|
numpr++;
|
|
else if ( base == 16 || base == 2 )
|
|
numpr += 2;
|
|
}
|
|
|
|
/*
|
|
* Insert left padding.
|
|
*/
|
|
if ( ! f_left && width > numpr ) {
|
|
if ( f_zero ) {
|
|
numpr = width;
|
|
} else {
|
|
for ( i = width - numpr; i > 0; i-- )
|
|
PUTCH(' ');
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert sign character.
|
|
*/
|
|
if ( signch ) {
|
|
PUTCH( signch );
|
|
numpr--;
|
|
}
|
|
|
|
/*
|
|
* Insert number prefix.
|
|
*/
|
|
if ( f_alternate && uval2 != 0 ) {
|
|
if ( base == 2 ) {
|
|
numpr--;
|
|
PUTCH('%');
|
|
} else if ( base == 8 ) {
|
|
numpr--;
|
|
PUTCH('0');
|
|
} else if ( base == 16 ) {
|
|
numpr -= 2;
|
|
PUTSTR("0x");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert zero padding.
|
|
*/
|
|
for ( i = numpr - numdigits; i > 0; i-- )
|
|
PUTCH('0');
|
|
|
|
/*
|
|
* Insert number.
|
|
*/
|
|
while ( numdigits > 0 )
|
|
PUTCH( convert[--numdigits] );
|
|
|
|
RIGHTPAD( width - numpr - (signch ? 1 : 0) );
|
|
break;
|
|
|
|
|
|
case 'f':
|
|
fval = va_arg( ap, double );
|
|
if ( precision == -1 )
|
|
precision = 6;
|
|
|
|
/*
|
|
* Check for sign character.
|
|
*/
|
|
if ( f_sign && fval >= 0.0 ) {
|
|
signch = '+';
|
|
} else if ( f_space && fval >= 0.0 ) {
|
|
signch = ' ';
|
|
} else if ( fval < 0.0 ) {
|
|
signch = '-';
|
|
fval = -fval;
|
|
} else {
|
|
signch = 0;
|
|
}
|
|
|
|
/*
|
|
* Get the integer part of the number. If the floating
|
|
* point value is greater than the maximum value of an
|
|
* unsigned long, the result is undefined.
|
|
*/
|
|
uval = (unsigned long) fval;
|
|
numdigits = 0;
|
|
do {
|
|
convert[ numdigits++ ] = '0' + uval % 10;
|
|
uval /= 10;
|
|
} while ( uval > 0 );
|
|
|
|
/*
|
|
* Calculate the actual size of the printed number.
|
|
*/
|
|
numpr = numdigits + (signch ? 1 : 0);
|
|
if ( precision > 0 )
|
|
numpr += 1 + precision;
|
|
|
|
LEFTPAD( width - numpr );
|
|
|
|
/*
|
|
* Insert sign character.
|
|
*/
|
|
if ( signch )
|
|
PUTCH( signch );
|
|
|
|
/*
|
|
* Insert integer number.
|
|
*/
|
|
while ( numdigits > 0 )
|
|
PUTCH( convert[--numdigits] );
|
|
|
|
/*
|
|
* Insert precision.
|
|
*/
|
|
if ( precision > 0 ) {
|
|
/*
|
|
* Truncate number to fractional part only.
|
|
*/
|
|
while ( fval >= 1.0 )
|
|
fval -= (double) (unsigned long) fval;
|
|
|
|
PUTCH('.');
|
|
|
|
/*
|
|
* Insert precision digits.
|
|
*/
|
|
while ( precision-- > 0 ) {
|
|
fval *= 10.0;
|
|
uval = (unsigned long) fval;
|
|
PUTCH( '0' + uval );
|
|
fval -= (double) (unsigned long) fval;
|
|
}
|
|
}
|
|
|
|
RIGHTPAD( width - numpr );
|
|
break;
|
|
|
|
case 't':
|
|
tid = va_arg( ap, l4_threadid_t );
|
|
if ( l4_is_invalid_id(tid) ) {
|
|
PUTSTR("INVALID_ID");
|
|
break;
|
|
} else if ( l4_is_nil_id(tid) ) {
|
|
PUTSTR("NIL_ID");
|
|
break;
|
|
}
|
|
digits = "0123456789";
|
|
|
|
#if defined(CONFIG_VERSION_X0)
|
|
PUTSTR("task=");
|
|
PUTDEC(tid.id.task);
|
|
PUTCH(',');
|
|
#endif
|
|
PUTSTR("thread=");
|
|
PUTDEC(tid.id.thread);
|
|
PUTSTR(",ver=");
|
|
PUTDEC(tid.id.version);
|
|
break;
|
|
|
|
|
|
case 's':
|
|
string = va_arg( ap, char * );
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if ( string == NULL ) {
|
|
PUTSTR( "(null)" );
|
|
break;
|
|
}
|
|
|
|
if ( width > 0 ) {
|
|
/*
|
|
* Calculate printed size.
|
|
*/
|
|
numpr = strlen( string );
|
|
if ( precision >= 0 && precision < numpr )
|
|
numpr = precision;
|
|
|
|
LEFTPAD( width - numpr );
|
|
}
|
|
|
|
/*
|
|
* Insert string.
|
|
*/
|
|
if ( precision >= 0 ) {
|
|
while ( precision-- > 0 && (c = *string++) != '\0' )
|
|
PUTCH( c );
|
|
} else {
|
|
while ( (c = *string++) != '\0' )
|
|
PUTCH( c );
|
|
}
|
|
|
|
RIGHTPAD( width - numpr );
|
|
break;
|
|
|
|
case 'c':
|
|
PUTCH( va_arg( ap, int ) );
|
|
break;
|
|
|
|
case '%':
|
|
PUTCH('%');
|
|
break;
|
|
|
|
case 'n':
|
|
**((int **) ap) = p - str;
|
|
break;
|
|
|
|
default:
|
|
PUTCH('%');
|
|
PUTCH(c);
|
|
break;
|
|
}
|
|
}
|
|
Done:
|
|
|
|
/*
|
|
* Null terminate string.
|
|
*/
|
|
*p = '\0';
|
|
|
|
/*
|
|
* Return size of printed string.
|
|
*/
|
|
return p - str;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function printf (fmt, ...)
|
|
*
|
|
* Print formated string to terminal like printf(3).
|
|
*
|
|
*/
|
|
int printf(const char *fmt, ...)
|
|
{
|
|
char outbuf[256];
|
|
va_list ap;
|
|
int r;
|
|
|
|
/*
|
|
* Safety check
|
|
*/
|
|
if ( fmt == NULL )
|
|
return 0;
|
|
|
|
/*
|
|
* Print into buffer.
|
|
*/
|
|
va_start(ap, fmt);
|
|
r = vsnprintf(outbuf, sizeof(outbuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
/*
|
|
* Output to terminal.
|
|
*/
|
|
if ( r > 0 )
|
|
print_string(outbuf);
|
|
|
|
return r;
|
|
}
|