Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 2571 → Rev 2572

/trunk/kernel/generic/src/printf/printf_core.c
39,21 → 39,33
#include <putchar.h>
#include <print.h>
#include <arch/arg.h>
#include <macros.h>
#include <func.h>
#include <arch.h>
 
#define __PRINTF_FLAG_PREFIX 0x00000001 /**< show prefixes 0x or 0*/
#define __PRINTF_FLAG_SIGNED 0x00000002 /**< signed / unsigned number */
#define __PRINTF_FLAG_ZEROPADDED 0x00000004 /**< print leading zeroes */
#define __PRINTF_FLAG_LEFTALIGNED 0x00000010 /**< align to left */
#define __PRINTF_FLAG_SHOWPLUS 0x00000020 /**< always show + sign */
#define __PRINTF_FLAG_SPACESIGN 0x00000040 /**< print space instead of plus */
#define __PRINTF_FLAG_BIGCHARS 0x00000080 /**< show big characters */
#define __PRINTF_FLAG_NEGATIVE 0x00000100 /**< number has - sign */
/** show prefixes 0x or 0 */
#define __PRINTF_FLAG_PREFIX 0x00000001
/** signed / unsigned number */
#define __PRINTF_FLAG_SIGNED 0x00000002
/** print leading zeroes */
#define __PRINTF_FLAG_ZEROPADDED 0x00000004
/** align to left */
#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
/** always show + sign */
#define __PRINTF_FLAG_SHOWPLUS 0x00000020
/** print space instead of plus */
#define __PRINTF_FLAG_SPACESIGN 0x00000040
/** show big characters */
#define __PRINTF_FLAG_BIGCHARS 0x00000080
/** number has - sign */
#define __PRINTF_FLAG_NEGATIVE 0x00000100
 
#define PRINT_NUMBER_BUFFER_SIZE (64+5) /**< Buffer big enought for 64 bit number
* printed in base 2, sign, prefix and
* 0 to terminate string.. (last one is only for better testing
* end of buffer by zero-filling subroutine)*/
/**
* Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
* to terminate string... (last one is only for better testing end of buffer by
* zero-filling subroutine)
*/
#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
 
/** Enumeration of possible arguments types.
*/
67,48 → 79,28
PrintfQualifierPointer
} qualifier_t;
 
static char digits_small[] = "0123456789abcdef"; /**< Small hexadecimal characters */
static char digits_big[] = "0123456789ABCDEF"; /**< Big hexadecimal characters */
static char digits_small[] = "0123456789abcdef";
static char digits_big[] = "0123456789ABCDEF";
 
/** Checks c for a digit.
* @param c One character.
* @return nonzero if c is from interval '0 to '9'.
/** Print one or more characters without adding newline.
*
* @param buf Buffer with size at least count bytes. NULL pointer is
* not allowed!
* @param count Number of characters to print.
* @param ps Output method and its data.
* @return Number of characters printed.
*/
static inline int isdigit(int c)
static int printf_putnchars(const char * buf, size_t count,
struct printf_spec *ps)
{
return ((c >= '0' )&&( c <= '9'));
}
 
/** Compute length of given zero terminated string.
* @param str Pointer to valid string.
* @return string length without trailing zero.
*/
static unative_t strlen(const char *str)
{
unative_t counter = 0;
 
while (str[counter] != 0) {
counter++;
}
 
return counter;
}
 
/** Print count chars from buffer without adding newline
* @param buf Buffer with size at least count bytes - NULL pointer NOT allowed!
* @param count
* @param ps output method and its data
* @return number of printed characters
*/
static int printf_putnchars(const char * buf, size_t count, struct printf_spec *ps)
{
return ps->write((void *)buf, count, ps->data);
}
 
/** Print string without added newline
* @param str string to print
* @param ps write function specification and support data
* @return number of printed characters
/** Print a string without adding a newline.
*
* @param str String to print.
* @param ps Write function specification and support data.
* @return Number of characters printed.
*/
static int printf_putstr(const char * str, struct printf_spec *ps)
{
115,7 → 107,8
size_t count;
if (str == NULL) {
return printf_putnchars("(NULL)", 6, ps);
char *nullstr = "(NULL)";
return printf_putnchars(nullstr, strlen(nullstr), ps);
}
 
count = strlen(str);
123,10 → 116,12
return ps->write((void *) str, count, ps->data);
}
 
/** Print one character to output
* @param c one character
* @param ps output method
* @return number of printed characters
/** Print one character.
*
* @param c Character to be printed.
* @param ps Output method.
*
* @return Number of characters printed.
*/
static int printf_putchar(int c, struct printf_spec *ps)
{
135,11 → 130,13
return ps->write((void *) &ch, 1, ps->data);
}
 
/** Print one formatted character
* @param c character to print
* @param width
* @param flags
* @return number of printed characters, negative value on fail
/** Print one formatted character.
*
* @param c Character to print.
* @param width Width modifier.
* @param flags Flags that change the way the character is printed.
*
* @return Number of characters printed, negative value on failure.
*/
static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
{
146,7 → 143,11
int counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (--width > 0) { /* one space is consumed by character itself hence predecrement */
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
++counter;
}
155,7 → 156,11
if (printf_putchar(c, ps) > 0)
counter++;
 
while (--width > 0) { /* one space is consumed by character itself hence predecrement */
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
++counter;
}
163,15 → 168,17
return ++counter;
}
 
/** Print one string
* @param s string
* @param width
* @param precision
* @param flags
* @return number of printed characters or negative value on fail
*/
static int print_string(char *s, int width, int precision, uint64_t flags, struct printf_spec *ps)
/** Print string.
*
* @param s String to be printed.
* @param width Width modifier.
* @param precision Precision modifier.
* @param flags Flags that modify the way the string is printed.
*
* @return Number of characters printed, negative value on failure.
*/
static int print_string(char *s, int width, int precision, uint64_t flags,
struct printf_spec *ps)
{
int counter = 0;
size_t size;
197,8 → 204,7
}
}
 
if ((retval = printf_putnchars(s, size < precision ? size : precision,
ps)) < 0) {
if ((retval = printf_putnchars(s, min(size, precision), ps)) < 0) {
return -counter;
}
counter += retval;
212,27 → 218,27
}
 
 
/** Print number in given base
/** Print a number in a given base.
*
* Print significant digits of a number in given
* base.
* Print significant digits of a number in given base.
*
* @param num Number to print.
* @param width
* @param precision
* @param base Base to print the number in (should
* be in range 2 .. 16).
* @param flags output modifiers
* @return number of written characters or EOF
* @param num Number to print.
* @param widt Width modifier.h
* @param precision Precision modifier.
* @param base Base to print the number in (must be between 2 and 16).
* @param flags Flags that modify the way the number is printed.
*
* @return Number of characters printed.
*
*/
static int print_number(uint64_t num, int width, int precision, int base , uint64_t flags, struct printf_spec *ps)
static int print_number(uint64_t num, int width, int precision, int base,
uint64_t flags, struct printf_spec *ps)
{
char *digits = digits_small;
char d[PRINT_NUMBER_BUFFER_SIZE]; /* this is good enough even for base == 2, prefix and sign */
char d[PRINT_NUMBER_BUFFER_SIZE];
char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
int size = 0; /* size of number with all prefixes and signs */
int number_size; /* size of plain number */
int size = 0; /* size of number with all prefixes and signs */
int number_size; /* size of plain number */
char sgn;
int retval;
int counter = 0;
254,7 → 260,10
number_size = size;
 
/* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
/*
* Collect the sum of all prefixes/signs/... to calculate padding and
* leading zeroes.
*/
if (flags & __PRINTF_FLAG_PREFIX) {
switch(base) {
case 2: /* Binary formating is not standard, but usefull */
275,12 → 284,12
sgn = '-';
size++;
} else if (flags & __PRINTF_FLAG_SHOWPLUS) {
sgn = '+';
size++;
} else if (flags & __PRINTF_FLAG_SPACESIGN) {
sgn = ' ';
size++;
}
sgn = '+';
size++;
} else if (flags & __PRINTF_FLAG_SPACESIGN) {
sgn = ' ';
size++;
}
}
 
if (flags & __PRINTF_FLAG_LEFTALIGNED) {
287,7 → 296,10
flags &= ~__PRINTF_FLAG_ZEROPADDED;
}
 
/* if number is leftaligned or precision is specified then zeropadding is ignored */
/*
* If the number is leftaligned or precision is specified then
* zeropadding is ignored.
*/
if (flags & __PRINTF_FLAG_ZEROPADDED) {
if ((precision == 0) && (width > size)) {
precision = width - size + number_size;
295,8 → 307,10
}
 
/* print leading spaces */
if (number_size > precision) /* We must print whole number not only a part */
if (number_size > precision) {
/* print the whole number not only a part */
precision = number_size;
}
 
width -= precision + size - number_size;
374,42 → 388,43
 
/** Print formatted string.
*
* Print string formatted according to the fmt parameter
* and variadic arguments. Each formatting directive
* must have the following form:
* Print string formatted according to the fmt parameter and variadic arguments.
* Each formatting directive must have the following form:
*
* \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
*
* FLAGS:@n
* - "#" Force to print prefix.
* For conversion \%o the prefix is 0, for \%x and \%X prefixes are 0x and 0X
* and for conversion \%b the prefix is 0b.
* - "#" Force to print prefix.For \%o conversion, the prefix is 0, for
* \%x and \%X prefixes are 0x and 0X and for conversion \%b the
* prefix is 0b.
*
* - "-" Align to left.
*
* - "+" Print positive sign just as negative.
*
* - " " If the printed number is positive and "+" flag is not set, print space in
* place of sign.
* - " " If the printed number is positive and "+" flag is not set,
* print space in place of sign.
*
* - "0" Print 0 as padding instead of spaces. Zeroes are placed between sign and the
* rest of the number. This flag is ignored if "-" flag is specified.
* - "0" Print 0 as padding instead of spaces. Zeroes are placed between
* sign and the rest of the number. This flag is ignored if "-"
* flag is specified.
*
* WIDTH:@n
* - Specify minimal width of printed argument. If it is bigger, width is ignored.
* If width is specified with a "*" character instead of number, width is taken from
* parameter list. And integer parameter is expected before parameter for processed
* conversion specification. If this value is negative its absolute value is taken
* and the "-" flag is set.
* - Specify the minimal width of a printed argument. If it is bigger,
* width is ignored. If width is specified with a "*" character instead of
* number, width is taken from parameter list. And integer parameter is
* expected before parameter for processed conversion specification. If
* this value is negative its absolute value is taken and the "-" flag is
* set.
*
* PRECISION:@n
* - Value precision. For numbers it specifies minimum valid numbers.
* Smaller numbers are printed with leading zeroes. Bigger numbers are not affected.
* Strings with more than precision characters are cut off.
* Just as with width, an "*" can be used used instead of a number.
* An integer value is then expected in parameters. When both width and precision
* are specified using "*", the first parameter is used for width and the second one
* for precision.
* Smaller numbers are printed with leading zeroes. Bigger numbers are not
* affected. Strings with more than precision characters are cut off. Just
* as with width, an "*" can be used used instead of a number. An integer
* value is then expected in parameters. When both width and precision are
* specified using "*", the first parameter is used for width and the
* second one for precision.
*
* TYPE:@n
* - "hh" Signed or unsigned char.@n
425,38 → 440,45
*
* - c Print single character.
*
* - s Print zero terminated string. If a NULL value is passed as value, "(NULL)" is printed instead.
* - s Print zero terminated string. If a NULL value is passed as
* value, "(NULL)" is printed instead.
*
* - P, p Print value of a pointer. Void * value is expected and it is printed in hexadecimal notation with prefix
* (as with \%#X / \%#x for 32bit or \%#X / \%#x for 64bit long pointers).
* - P, p Print value of a pointer. Void * value is expected and it is
* printed in hexadecimal notation with prefix (as with \%#X / \%#x
* for 32-bit or \%#X / \%#x for 64-bit long pointers).
*
* - b Print value as unsigned binary number. Prefix is not printed by default. (Nonstandard extension.)
* - b Print value as unsigned binary number. Prefix is not printed by
* default. (Nonstandard extension.)
*
* - o Print value as unsigned octal number. Prefix is not printed by default.
* - o Print value as unsigned octal number. Prefix is not printed by
* default.
*
* - d,i Print signed decimal number. There is no difference between d and i conversion.
* - d, i Print signed decimal number. There is no difference between d
* and i conversion.
*
* - u Print unsigned decimal number.
*
* - X, x Print hexadecimal number with upper- or lower-case. Prefix is not printed by default.
* - X, x Print hexadecimal number with upper- or lower-case. Prefix isi
* not printed by default.
*
* All other characters from fmt except the formatting directives
* are printed in verbatim.
* All other characters from fmt except the formatting directives are printed in
* verbatim.
*
* @param fmt Formatting NULL terminated string.
* @return Number of printed characters or negative value on failure.
* @param fmt Formatting NULL terminated string.
* @return Number of characters printed, negative value on failure.
*/
int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
{
int i = 0, j = 0; /**< i is index of currently processed char from fmt, j is index to the first not printed nonformating character */
int i = 0; /* index of the currently processed char from fmt */
int j = 0; /* index to the first not printed nonformating character */
int end;
int counter; /**< counter of printed characters */
int retval; /**< used to store return values from called functions */
int counter; /* counter of printed characters */
int retval; /* used to store return values from called functions */
char c;
qualifier_t qualifier; /* type of argument */
int base; /**< base in which will be parameter (numbers only) printed */
uint64_t number; /**< argument value */
size_t size; /**< byte size of integer parameter */
qualifier_t qualifier; /* type of argument */
int base; /* base in which a numeric parameter will be printed */
uint64_t number; /* argument value */
size_t size; /* byte size of integer parameter */
int width, precision;
uint64_t flags;
467,7 → 489,8
if (c == '%' ) {
/* print common characters if any processed */
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
if ((retval = printf_putnchars(&fmt[j],
(size_t)(i - j), ps)) < 0) { /* error */
counter = -counter;
goto out;
}
482,12 → 505,23
do {
++i;
switch (c = fmt[i]) {
case '#': flags |= __PRINTF_FLAG_PREFIX; break;
case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break;
case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break;
case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break;
case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break;
default: end = 1;
case '#':
flags |= __PRINTF_FLAG_PREFIX;
break;
case '-':
flags |= __PRINTF_FLAG_LEFTALIGNED;
break;
case '+':
flags |= __PRINTF_FLAG_SHOWPLUS;
break;
case ' ':
flags |= __PRINTF_FLAG_SPACESIGN;
break;
case '0':
flags |= __PRINTF_FLAG_ZEROPADDED;
break;
default:
end = 1;
};
} while (end == 0);
500,11 → 534,11
width += fmt[i++] - '0';
}
} else if (fmt[i] == '*') {
/* get width value from argument list*/
/* get width value from argument list */
i++;
width = (int)va_arg(ap, int);
if (width < 0) {
/* negative width means to set '-' flag */
/* negative width sets '-' flag */
width *= -1;
flags |= __PRINTF_FLAG_LEFTALIGNED;
}
520,11 → 554,14
precision += fmt[i++] - '0';
}
} else if (fmt[i] == '*') {
/* get precision value from argument list*/
/*
* Get precision value from the argument
* list.
*/
i++;
precision = (int)va_arg(ap, int);
if (precision < 0) {
/* negative precision means to ignore it */
/* ignore negative precision */
precision = 0;
}
}
531,7 → 568,7
}
 
switch (fmt[i++]) {
/** TODO: unimplemented qualifiers:
/** @todo unimplemented qualifiers:
* t ptrdiff_t - ISO C 99
*/
case 'h': /* char or short */
552,7 → 589,8
qualifier = PrintfQualifierNative;
break;
default:
qualifier = PrintfQualifierInt; /* default type */
/* default type */
qualifier = PrintfQualifierInt;
--i;
}
564,7 → 602,8
* String and character conversions.
*/
case 's':
if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) {
if ((retval = print_string(va_arg(ap, char *),
width, precision, flags, ps)) < 0) {
counter = -counter;
goto out;
};
574,7 → 613,8
goto next_char;
case 'c':
c = va_arg(ap, unsigned int);
if ((retval = print_char(c, width, flags, ps)) < 0) {
retval = print_char(c, width, flags, ps);
if (retval < 0) {
counter = -counter;
goto out;
};
617,9 → 657,9
* Bad formatting.
*/
default:
/* Unknown format
* now, j is index of '%' so we will
* print whole bad format sequence
/*
* Unknown format. Now, j is the index of '%'
* so we will print whole bad format sequence.
*/
goto next_char;
}
646,11 → 686,13
break;
case PrintfQualifierLongLong:
size = sizeof(unsigned long long);
number = (uint64_t)va_arg(ap, unsigned long long);
number = (uint64_t)va_arg(ap,
unsigned long long);
break;
case PrintfQualifierPointer:
size = sizeof(void *);
number = (uint64_t)(unsigned long)va_arg(ap, void *);
number = (uint64_t)(unsigned long)va_arg(ap,
void *);
break;
case PrintfQualifierNative:
size = sizeof(unative_t);
662,7 → 704,7
}
if (flags & __PRINTF_FLAG_SIGNED) {
if (number & (0x1 << (size*8 - 1))) {
if (number & (0x1 << (size * 8 - 1))) {
flags |= __PRINTF_FLAG_NEGATIVE;
if (size == sizeof(uint64_t)) {
669,16 → 711,19
number = -((int64_t)number);
} else {
number = ~number;
number &= (~((0xFFFFFFFFFFFFFFFFll) << (size * 8)));
number &=
~(0xFFFFFFFFFFFFFFFFll <<
(size * 8));
number++;
}
}
}
 
if ((retval = print_number(number, width, precision, base, flags, ps)) < 0) {
if ((retval = print_number(number, width, precision,
base, flags, ps)) < 0) {
counter = -counter;
goto out;
};
}
 
counter += retval;
j = i + 1;
689,7 → 734,8
}
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], (unative_t)(i - j), ps)) < 0) { /* error */
if ((retval = printf_putnchars(&fmt[j], (unative_t)(i - j),
ps)) < 0) { /* error */
counter = -counter;
goto out;