123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- #include <stdarg.h>
- #include "if_ether.h" /* for ETH_ALEN */
- #include "limits.h" /* for CHAR_BIT */
- #include "console.h"
- #include "errno.h"
- #include "vsprintf.h"
-
- #define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
- #define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
- #define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
- #define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
-
- /** @file
- *
- * printf and friends.
- *
- * Etherboot's printf() functions understand the following format
- * specifiers:
- *
- * - Hexadecimal integers
- * - @c %[#]x - 4 bytes int (8 hex digits, lower case)
- * - @c %[#]X - 4 bytes int (8 hex digits, upper case)
- * - @c %[#]lx - 8 bytes long (16 hex digits, lower case)
- * - @c %[#]lX - 8 bytes long (16 hex digits, upper case)
- * - @c %[#]hx - 2 bytes int (4 hex digits, lower case)
- * - @c %[#]hX - 2 bytes int (4 hex digits, upper case)
- * - @c %[#]hhx - 1 byte int (2 hex digits, lower case)
- * - @c %[#]hhX - 1 byte int (2 hex digits, upper case)
- * .
- * If the optional # prefix is specified, the output will
- * be prefixed with 0x (or 0X).
- *
- * - Other integers
- * - @c %d - decimal int
- * .
- * Note that any width specification (e.g. the @c 02 in @c %02x)
- * will be accepted but ignored.
- *
- * - Strings and characters
- * - @c %c - char
- * - @c %s - string
- * - @c %m - error message text (i.e. strerror(errno))
- *
- * - Etherboot-specific specifiers
- * - @c %@ - IP in ddd.ddd.ddd.ddd notation
- * - @c %! - MAC address in xx:xx:xx:xx:xx:xx notation
- *
- */
-
- /**
- * Write a formatted string to a buffer.
- *
- * @v buf Buffer into which to write the string, or NULL
- * @v fmt Format string
- * @v args Arguments corresponding to the format string
- * @ret len Length of string written to buffer (if buf != NULL)
- * @ret 0 (if buf == NULL)
- * @err None
- *
- * If @c buf==NULL, then the string will be written to the console
- * directly using putchar().
- *
- */
- static int vsprintf(char *buf, const char *fmt, va_list args)
- {
- const char *p;
- char *s;
- s = buf;
- for ( ; *fmt != '\0'; ++fmt) {
- if (*fmt != '%') {
- buf ? *s++ = *fmt : putchar(*fmt);
- continue;
- }
- /* skip width specs */
- fmt++;
- while (*fmt >= '0' && *fmt <= '9')
- fmt++;
- if (*fmt == '.')
- fmt++;
- while (*fmt >= '0' && *fmt <= '9')
- fmt++;
- if (*fmt == 's') {
- for(p = va_arg(args, char *); *p != '\0'; p++)
- buf ? *s++ = *p : putchar(*p);
- } else if (*fmt == 'm') {
- for(p = strerror(errno); *p != '\0'; p++)
- buf ? *s++ = *p : putchar(*p);
- } else { /* Length of item is bounded */
- char tmp[40], *q = tmp;
- int alt = 0;
- int shift = INT_SHIFT;
- if (*fmt == '#') {
- alt = 1;
- fmt++;
- }
- if (*fmt == 'l') {
- shift = LONG_SHIFT;
- fmt++;
- }
- else if (*fmt == 'h') {
- shift = SHRT_SHIFT;
- fmt++;
- if (*fmt == 'h') {
- shift = CHAR_SHIFT;
- fmt++;
- }
- }
-
- /*
- * Before each format q points to tmp buffer
- * After each format q points past end of item
- */
- if ((*fmt | 0x20) == 'x') {
- /* With x86 gcc, sizeof(long) == sizeof(int) */
- unsigned long h;
- int ncase;
- if (shift > INT_SHIFT) {
- h = va_arg(args, unsigned long);
- } else {
- h = va_arg(args, unsigned int);
- }
- ncase = (*fmt & 0x20);
- if (alt) {
- *q++ = '0';
- *q++ = 'X' | ncase;
- }
- for ( ; shift >= 0; shift -= 4)
- *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
- }
- else if (*fmt == 'd') {
- char *r, *t;
- long i;
- if (shift > INT_SHIFT) {
- i = va_arg(args, long);
- } else {
- i = va_arg(args, int);
- }
- if (i < 0) {
- *q++ = '-';
- i = -i;
- }
- t = q; /* save beginning of digits */
- do {
- *q++ = '0' + (i % 10);
- i /= 10;
- } while (i);
- /* reverse digits, stop in middle */
- r = q; /* don't alter q */
- while (--r > t) {
- i = *r;
- *r = *t;
- *t++ = i;
- }
- }
- else if (*fmt == '@') {
- unsigned char *r;
- union {
- uint32_t l;
- unsigned char c[4];
- } u;
- u.l = va_arg(args, uint32_t);
- for (r = &u.c[0]; r < &u.c[4]; ++r)
- q += sprintf(q, "%d.", *r);
- --q;
- }
- else if (*fmt == '!') {
- const char *r;
- p = va_arg(args, char *);
- for (r = p + ETH_ALEN; p < r; ++p)
- q += sprintf(q, "%hhX:", *p);
- --q;
- }
- else if (*fmt == 'c')
- *q++ = va_arg(args, int);
- else
- *q++ = *fmt;
- /* now output the saved string */
- for (p = tmp; p < q; ++p)
- buf ? *s++ = *p : putchar(*p);
- }
- }
- if (buf)
- *s = '\0';
- return (s - buf);
- }
-
- /**
- * Write a formatted string to a buffer.
- *
- * @v buf Buffer into which to write the string, or NULL
- * @v fmt Format string
- * @v ... Arguments corresponding to the format string
- * @ret len Length of string written to buffer (if buf != NULL)
- * @ret 0 (if buf == NULL)
- * @err None
- *
- * If @c buf==NULL, then the string will be written to the console
- * directly using putchar().
- *
- */
- int sprintf(char *buf, const char *fmt, ...)
- {
- va_list args;
- int i;
- va_start(args, fmt);
- i=vsprintf(buf, fmt, args);
- va_end(args);
- return i;
- }
-
- /**
- * Write a formatted string to the console.
- *
- * @v fmt Format string
- * @v ... Arguments corresponding to the format string
- * @ret None
- * @err None
- *
- */
- void printf(const char *fmt, ...)
- {
- va_list args;
- int i;
- va_start(args, fmt);
- i=vsprintf(0, fmt, args);
- va_end(args);
- }
|