metalsvm/libkern/printf.c
Jacek Galowicz 394befa3a8 Color management for procedures using vga_putchar
As seen in some BSD operating systems, you can now push and pop foreground and background colors onto a stack to change the colors you see on the screen whenever kprintf/kputchar is used.

This could become useful if one wants to see kernel space kprintfs in other colors than user space printfs or error messages in red and other debugging purposes.

Beware: This is just a small and dirty hack which protects the colorstack with locks and so on. But on task switching the color will not be switched. That makes different colors for different colors persistent for all the time difficult/impossible. But I considered adding colors to the task structures a bit overdone for a small debugging-help.

[Sorry for those commit-and-pull-back-mails. Forgot that I had this stuff on the master branch while pushing my own branch onto the server.]
2011-05-25 09:26:00 +02:00

548 lines
13 KiB
C

/*-
* Copyright (c) 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
*/
/*
* eduOS's printf implementation is based on a implementation which was
* published at http://www.pagetable.com/?p=298.
* The authors built a full-featured standalone version of printf(). The
* base code has been taken from FreeBSD (sys/kern/subr_prf.c) and is
* therefore BSD-licensed. Unnecessary functions have been removed and
* all typedefs required have been added.
*/
#include <metalsvm/string.h>
#include <metalsvm/spinlock.h>
#define __64BIT__
#ifdef __64BIT__
typedef unsigned long long uintmax_t;
typedef long long intmax_t;
#else
typedef unsigned int uintmax_t;
typedef int intmax_t;
#endif
typedef unsigned char u_char;
typedef unsigned int u_int;
typedef unsigned long u_long;
typedef unsigned short u_short;
typedef unsigned long long u_quad_t;
typedef long long quad_t;
typedef unsigned long uintptr_t;
#define NBBY 8 /* number of bits in a byte */
char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#define hex2ascii(hex) (hex2ascii_data[hex])
#define va_list __builtin_va_list
#define va_start __builtin_va_start
#define va_arg __builtin_va_arg
#define va_end __builtin_va_end
#define toupper(c) ((c) - 0x20 * (((c) >= 'a') && ((c) <= 'z')))
/* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */
#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
#define COLOR_STACK_SIZE 10
static int fgstack[COLOR_STACK_SIZE] = {0xf};
static int bgstack[COLOR_STACK_SIZE] = {0x0};
static unsigned int fgstack_pos = 0;
static unsigned int bgstack_pos = 0;
static spinlock_t fglock = SPINLOCK_INIT;
static spinlock_t bglock = SPINLOCK_INIT;
/*
* Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
* order; return an optional length and a pointer to the last character
* written in the buffer (i.e., the first character of the string).
* The buffer pointed to by `nbuf' must have length >= MAXNBUF.
*/
static char *ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
{
char *p, c;
p = nbuf;
*p = '\0';
do {
c = hex2ascii(num % base);
*++p = upper ? toupper(c) : c;
} while (num /= base);
if (lenp)
*lenp = p - nbuf;
return (p);
}
/*
* Scaled down version of printf(3).
*
* Two additional formats:
*
* The format %b is supported to decode error registers.
* Its usage is:
*
* printf("reg=%b\n", regval, "*");
*
* where the output base is expressed as a control character, e.g.
* \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
* the first of which gives the bit number to be inspected (origin 1), and
* the next characters (up to a control character, i.e. a character <= 32),
* give the name of the register. Thus:
*
* kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
*
* would produce output:
*
* reg=3
*
* XXX: %D -- Hexdump, takes pointer and separator string:
* ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
* ("%*D", len, ptr, " " -> XX XX XX XX ...
*/
int kvprintf(char const *fmt, void (*func) (int, void *), void *arg, int radix,
va_list ap)
{
#define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; }
char nbuf[MAXNBUF];
char *d;
const char *p, *percent, *q;
u_char *up;
int ch, n;
uintmax_t num;
int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
int cflag, hflag, jflag, tflag, zflag;
int dwidth, upper;
char padc;
int stop = 0, retval = 0;
num = 0;
if (!func)
d = (char *)arg;
else
d = NULL;
if (fmt == NULL)
fmt = "(fmt null)\n";
if (radix < 2 || radix > 36)
radix = 10;
for (;;) {
padc = ' ';
width = 0;
while ((ch = (u_char) * fmt++) != '%' || stop) {
if (ch == '\0')
return (retval);
PCHAR(ch);
}
percent = fmt - 1;
qflag = 0;
lflag = 0;
ladjust = 0;
sharpflag = 0;
neg = 0;
sign = 0;
dot = 0;
dwidth = 0;
upper = 0;
cflag = 0;
hflag = 0;
jflag = 0;
tflag = 0;
zflag = 0;
reswitch: switch (ch = (u_char) * fmt++) {
case '.':
dot = 1;
goto reswitch;
case '#':
sharpflag = 1;
goto reswitch;
case '+':
sign = 1;
goto reswitch;
case '-':
ladjust = 1;
goto reswitch;
case '%':
PCHAR(ch);
break;
case '*':
if (!dot) {
width = va_arg(ap, int);
if (width < 0) {
ladjust = !ladjust;
width = -width;
}
} else {
dwidth = va_arg(ap, int);
}
goto reswitch;
case '0':
if (!dot) {
padc = '0';
goto reswitch;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (n = 0;; ++fmt) {
n = n * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
if (dot)
dwidth = n;
else
width = n;
goto reswitch;
case 'b':
num = (u_int) va_arg(ap, int);
p = va_arg(ap, char *);
for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
PCHAR(*q--);
if (num == 0)
break;
for (tmp = 0; *p;) {
n = *p++;
if (num & (1 << (n - 1))) {
PCHAR(tmp ? ',' : '<');
for (; (n = *p) > ' '; ++p)
PCHAR(n);
tmp = 1;
} else
for (; *p > ' '; ++p)
continue;
}
if (tmp)
PCHAR('>');
break;
case 'c':
PCHAR(va_arg(ap, int));
break;
case 'D':
up = va_arg(ap, u_char *);
p = va_arg(ap, char *);
if (!width)
width = 16;
while (width--) {
PCHAR(hex2ascii(*up >> 4));
PCHAR(hex2ascii(*up & 0x0f));
up++;
if (width)
for (q = p; *q; q++)
PCHAR(*q);
}
break;
case 'd':
case 'i':
base = 10;
sign = 1;
goto handle_sign;
case 'h':
if (hflag) {
hflag = 0;
cflag = 1;
} else
hflag = 1;
goto reswitch;
case 'j':
jflag = 1;
goto reswitch;
case 'l':
if (lflag) {
lflag = 0;
qflag = 1;
} else
lflag = 1;
goto reswitch;
case 'n':
if (jflag)
*(va_arg(ap, intmax_t *)) = retval;
else if (qflag)
*(va_arg(ap, quad_t *)) = retval;
else if (lflag)
*(va_arg(ap, long *)) = retval;
else if (zflag)
*(va_arg(ap, size_t *)) = retval;
else if (hflag)
*(va_arg(ap, short *)) = retval;
else if (cflag)
*(va_arg(ap, char *)) = retval;
else
*(va_arg(ap, int *)) = retval;
break;
case 'o':
base = 8;
goto handle_nosign;
case 'p':
base = 16;
sharpflag = (width == 0);
sign = 0;
num = (uintptr_t) va_arg(ap, void *);
goto number;
case 'q':
qflag = 1;
goto reswitch;
case 'r':
base = radix;
if (sign)
goto handle_sign;
goto handle_nosign;
case 's':
p = va_arg(ap, char *);
if (p == NULL)
p = "(null)";
if (!dot)
n = strlen(p);
else
for (n = 0; n < dwidth && p[n]; n++)
continue;
width -= n;
if (!ladjust && width > 0)
while (width--)
PCHAR(padc);
while (n--)
PCHAR(*p++);
if (ladjust && width > 0)
while (width--)
PCHAR(padc);
break;
case 't':
tflag = 1;
goto reswitch;
case 'u':
base = 10;
goto handle_nosign;
case 'X':
upper = 1;
case 'x':
base = 16;
goto handle_nosign;
case 'y':
base = 16;
sign = 1;
goto handle_sign;
case 'z':
zflag = 1;
goto reswitch;
handle_nosign:
sign = 0;
if (jflag)
num = va_arg(ap, uintmax_t);
else if (qflag)
num = va_arg(ap, u_quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, u_long);
else if (zflag)
num = va_arg(ap, size_t);
else if (hflag)
num = (u_short) va_arg(ap, int);
else if (cflag)
num = (u_char) va_arg(ap, int);
else
num = va_arg(ap, u_int);
goto number;
handle_sign:
if (jflag)
num = va_arg(ap, intmax_t);
else if (qflag)
num = va_arg(ap, quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, long);
else if (zflag)
num = va_arg(ap, ssize_t);
else if (hflag)
num = (short)va_arg(ap, int);
else if (cflag)
num = (char)va_arg(ap, int);
else
num = va_arg(ap, int);
number:
if (sign && (intmax_t) num < 0) {
neg = 1;
num = -(intmax_t) num;
}
p = ksprintn(nbuf, num, base, &tmp, upper);
if (sharpflag && num != 0) {
if (base == 8)
tmp++;
else if (base == 16)
tmp += 2;
}
if (neg)
tmp++;
if (!ladjust && padc != '0' && width
&& (width -= tmp) > 0)
while (width--)
PCHAR(padc);
if (neg)
PCHAR('-');
if (sharpflag && num != 0) {
if (base == 8) {
PCHAR('0');
} else if (base == 16) {
PCHAR('0');
PCHAR('x');
}
}
if (!ladjust && width && (width -= tmp) > 0)
while (width--)
PCHAR(padc);
while (*p)
PCHAR(*p--);
if (ladjust && width && (width -= tmp) > 0)
while (width--)
PCHAR(padc);
break;
default:
while (percent < fmt)
PCHAR(*percent++);
/*
* Since we ignore a formatting argument it is no
* longer safe to obey the remaining formatting
* arguments as the arguments will no longer match
* the format specs.
*/
stop = 1;
break;
}
}
#undef PCHAR
}
/*
* Print directly a character on the screen
*/
extern int kputchar(int);
/*
* A wrapper function for kputchar because
* kvprintf needs an output function, which possesses two arguments.
* The first arguments defines the output character, the second could be used to pass
* additional arguments. In the case of kputchar is no additional argument needed.
*/
static void _putchar(int c, void *arg)
{
kputchar(c);
}
int kprintf(const char *fmt, ...)
{
int ret;
/* http://www.pagetable.com/?p=298 */
va_list ap;
va_start(ap, fmt);
ret = kvprintf(fmt,
_putchar, /* output function */
NULL, /* additional argument for the output function */
10, ap);
va_end(ap);
return ret;
}
extern void settextcolor(unsigned char forecolor, unsigned char backcolor);
void pushfg(unsigned char fgcol)
{
spinlock_lock(&fglock);
/* Ignore this request if the stack is full */
if (fgstack_pos == COLOR_STACK_SIZE) goto rel_pushfglock;
++fgstack_pos;
fgstack[fgstack_pos] = fgcol;
settextcolor(fgstack[fgstack_pos], bgstack[bgstack_pos]);
rel_pushfglock:
spinlock_unlock(&fglock);
}
void pushbg(unsigned char bgcol)
{
spinlock_lock(&bglock);
/* Ignore this request if the stack is full */
if (bgstack_pos == COLOR_STACK_SIZE) goto rel_pushbglock;
++bgstack_pos;
bgstack[bgstack_pos] = bgcol;
settextcolor(fgstack[fgstack_pos], bgstack[bgstack_pos]);
rel_pushbglock:
spinlock_unlock(&bglock);
}
void popfg()
{
spinlock_lock(&fglock);
/* Ignore if we're already at the stack's bottom */
if (!fgstack_pos) goto rel_popfglock;
--fgstack_pos;
settextcolor(fgstack[fgstack_pos], bgstack[bgstack_pos]);
rel_popfglock:
spinlock_unlock(&fglock);
}
void popbg()
{
spinlock_lock(&bglock);
/* Ignore if we're already at the stack's bottom */
if (!bgstack_pos) goto rel_popbglock;
--bgstack_pos;
settextcolor(fgstack[fgstack_pos], bgstack[bgstack_pos]);
rel_popbglock:
spinlock_unlock(&bglock);
}