summaryrefslogtreecommitdiff
path: root/kern/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'kern/printf.c')
-rw-r--r--kern/printf.c637
1 files changed, 637 insertions, 0 deletions
diff --git a/kern/printf.c b/kern/printf.c
new file mode 100644
index 0000000..693c660
--- /dev/null
+++ b/kern/printf.c
@@ -0,0 +1,637 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Common code for printf et al.
+ *
+ * The calling routine typically takes a variable number of arguments,
+ * and passes the address of the first one. This implementation
+ * assumes a straightforward, stack implementation, aligned to the
+ * machine's wordsize. Increasing addresses are assumed to point to
+ * successive arguments (left-to-right), as is the case for a machine
+ * with a downward-growing stack with arguments pushed right-to-left.
+ *
+ * To write, for example, fprintf() using this routine, the code
+ *
+ * fprintf(fd, format, args)
+ * FILE *fd;
+ * char *format;
+ * {
+ * _doprnt(format, &args, fd);
+ * }
+ *
+ * would suffice. (This example does not handle the fprintf's "return
+ * value" correctly, but who looks at the return value of fprintf
+ * anyway?)
+ *
+ * This version implements the following printf features:
+ *
+ * %d decimal conversion
+ * %u unsigned conversion
+ * %x hexadecimal conversion
+ * %X hexadecimal conversion with capital letters
+ * %o octal conversion
+ * %c character
+ * %s string
+ * %m.n field width, precision
+ * %-m.n left adjustment
+ * %0m.n zero-padding
+ * %*.* width and precision taken from arguments
+ *
+ * This version does not implement %f, %e, or %g. It accepts, but
+ * ignores, an `l' as in %ld, %lo, %lx, and %lu, and therefore will not
+ * work correctly on machines for which sizeof(long) != sizeof(int).
+ * It does not even parse %D, %O, or %U; you should be using %ld, %o and
+ * %lu if you mean long conversion.
+ *
+ * As mentioned, this version does not return any reasonable value.
+ *
+ * Permission is granted to use, modify, or propagate this code as
+ * long as this notice is incorporated.
+ *
+ * Steve Summit 3/25/87
+ */
+
+/*
+ * Added formats for decoding device registers:
+ *
+ * printf("reg = %b", regval, "<base><arg>*")
+ *
+ * where <base> is the output base expressed as a control character:
+ * i.e. '\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 rest (up to a control character (<= 32)) give the
+ * name of the register. Thus
+ * printf("reg = %b\n", 3, "\10\2BITTWO\1BITONE")
+ * would produce
+ * reg = 3<BITTWO,BITONE>
+ *
+ * If the second character in <arg> is also a control character, it
+ * indicates the last bit of a bit field. In this case, printf will extract
+ * bits <1> to <2> and print it. Characters following the second control
+ * character are printed before the bit field.
+ * printf("reg = %b\n", 0xb, "\10\4\3FIELD1=\2BITTWO\1BITONE")
+ * would produce
+ * reg = b<FIELD1=2,BITONE>
+ */
+/*
+ * Added for general use:
+ * # prefix for alternate format:
+ * 0x (0X) for hex
+ * leading 0 for octal
+ * + print '+' if positive
+ * blank print ' ' if positive
+ *
+ * z signed hexadecimal
+ * r signed, 'radix'
+ * n unsigned, 'radix'
+ *
+ * D,U,O,Z same as corresponding lower-case versions
+ * (compatibility)
+ */
+
+#include <mach/boolean.h>
+#include <kern/lock.h>
+#include <kern/strings.h>
+#include <sys/varargs.h>
+
+#define isdigit(d) ((d) >= '0' && (d) <= '9')
+#define Ctod(c) ((c) - '0')
+
+#define MAXBUF (sizeof(long int) * 8) /* enough for binary */
+
+
+void printnum(
+ register unsigned long u,
+ register int base,
+ void (*putc)( char, vm_offset_t ),
+ vm_offset_t putc_arg)
+{
+ char buf[MAXBUF]; /* build number here */
+ register char * p = &buf[MAXBUF-1];
+ static char digs[] = "0123456789abcdef";
+
+ do {
+ *p-- = digs[u % base];
+ u /= base;
+ } while (u != 0);
+
+ while (++p != &buf[MAXBUF])
+ (*putc)(*p, putc_arg);
+
+}
+
+boolean_t _doprnt_truncates = FALSE;
+
+/* printf could be called at _any_ point during system initialization,
+ including before printf_init() gets called from the "normal" place
+ in kern/startup.c. */
+boolean_t _doprnt_lock_initialized = FALSE;
+decl_simple_lock_data(,_doprnt_lock)
+
+void printf_init()
+{
+ if (!_doprnt_lock_initialized)
+ {
+ _doprnt_lock_initialized = TRUE;
+ simple_lock_init(&_doprnt_lock);
+ }
+}
+
+void _doprnt(
+ register char *fmt,
+ va_list *argp,
+ /* character output routine */
+ void (*putc)( char, vm_offset_t),
+ int radix, /* default radix - for '%r' */
+ vm_offset_t putc_arg)
+{
+ int length;
+ int prec;
+ boolean_t ladjust;
+ char padc;
+ long n;
+ unsigned long u;
+ int plus_sign;
+ int sign_char;
+ boolean_t altfmt, truncate;
+ int base;
+ register char c;
+
+ printf_init();
+
+#if 0
+ /* Make sure that we get *some* printout, no matter what */
+ simple_lock(&_doprnt_lock);
+#else
+ {
+ register int i = 0;
+ while (i < 1*1024*1024) {
+ if (simple_lock_try(&_doprnt_lock))
+ break;
+ i++;
+ }
+ }
+#endif
+
+ while ((c = *fmt) != '\0') {
+ if (c != '%') {
+ (*putc)(c, putc_arg);
+ fmt++;
+ continue;
+ }
+
+ fmt++;
+
+ length = 0;
+ prec = -1;
+ ladjust = FALSE;
+ padc = ' ';
+ plus_sign = 0;
+ sign_char = 0;
+ altfmt = FALSE;
+
+ while (TRUE) {
+ c = *fmt;
+ if (c == '#') {
+ altfmt = TRUE;
+ }
+ else if (c == '-') {
+ ladjust = TRUE;
+ }
+ else if (c == '+') {
+ plus_sign = '+';
+ }
+ else if (c == ' ') {
+ if (plus_sign == 0)
+ plus_sign = ' ';
+ }
+ else
+ break;
+ fmt++;
+ }
+
+ if (c == '0') {
+ padc = '0';
+ c = *++fmt;
+ }
+
+ if (isdigit(c)) {
+ while(isdigit(c)) {
+ length = 10 * length + Ctod(c);
+ c = *++fmt;
+ }
+ }
+ else if (c == '*') {
+ length = va_arg(*argp, int);
+ c = *++fmt;
+ if (length < 0) {
+ ladjust = !ladjust;
+ length = -length;
+ }
+ }
+
+ if (c == '.') {
+ c = *++fmt;
+ if (isdigit(c)) {
+ prec = 0;
+ while(isdigit(c)) {
+ prec = 10 * prec + Ctod(c);
+ c = *++fmt;
+ }
+ }
+ else if (c == '*') {
+ prec = va_arg(*argp, int);
+ c = *++fmt;
+ }
+ }
+
+ if (c == 'l')
+ c = *++fmt; /* need it if sizeof(int) < sizeof(long) */
+
+ truncate = FALSE;
+
+ switch(c) {
+ case 'b':
+ case 'B':
+ {
+ register char *p;
+ boolean_t any;
+ register int i;
+
+ u = va_arg(*argp, unsigned long);
+ p = va_arg(*argp, char *);
+ base = *p++;
+ printnum(u, base, putc, putc_arg);
+
+ if (u == 0)
+ break;
+
+ any = FALSE;
+ while (i = *p++) {
+ /* NOTE: The '32' here is because ascii space */
+ if (*p <= 32) {
+ /*
+ * Bit field
+ */
+ register int j;
+ if (any)
+ (*putc)(',', putc_arg);
+ else {
+ (*putc)('<', putc_arg);
+ any = TRUE;
+ }
+ j = *p++;
+ for (; (c = *p) > 32; p++)
+ (*putc)(c, putc_arg);
+ printnum((unsigned)( (u>>(j-1)) & ((2<<(i-j))-1)),
+ base, putc, putc_arg);
+ }
+ else if (u & (1<<(i-1))) {
+ if (any)
+ (*putc)(',', putc_arg);
+ else {
+ (*putc)('<', putc_arg);
+ any = TRUE;
+ }
+ for (; (c = *p) > 32; p++)
+ (*putc)(c, putc_arg);
+ }
+ else {
+ for (; *p > 32; p++)
+ continue;
+ }
+ }
+ if (any)
+ (*putc)('>', putc_arg);
+ break;
+ }
+
+ case 'c':
+ c = va_arg(*argp, int);
+ (*putc)(c, putc_arg);
+ break;
+
+ case 's':
+ {
+ register char *p;
+ register char *p2;
+
+ if (prec == -1)
+ prec = 0x7fffffff; /* MAXINT */
+
+ p = va_arg(*argp, char *);
+
+ if (p == (char *)0)
+ p = "";
+
+ if (length > 0 && !ladjust) {
+ n = 0;
+ p2 = p;
+
+ for (; *p != '\0' && n < prec; p++)
+ n++;
+
+ p = p2;
+
+ while (n < length) {
+ (*putc)(' ', putc_arg);
+ n++;
+ }
+ }
+
+ n = 0;
+
+ while (*p != '\0') {
+ if (++n > prec)
+ break;
+
+ (*putc)(*p++, putc_arg);
+ }
+
+ if (n < length && ladjust) {
+ while (n < length) {
+ (*putc)(' ', putc_arg);
+ n++;
+ }
+ }
+
+ break;
+ }
+
+ case 'o':
+ truncate = _doprnt_truncates;
+ case 'O':
+ base = 8;
+ goto print_unsigned;
+
+ case 'd':
+ truncate = _doprnt_truncates;
+ case 'D':
+ base = 10;
+ goto print_signed;
+
+ case 'u':
+ truncate = _doprnt_truncates;
+ case 'U':
+ base = 10;
+ goto print_unsigned;
+
+ case 'x':
+ truncate = _doprnt_truncates;
+ case 'X':
+ base = 16;
+ goto print_unsigned;
+
+ case 'z':
+ truncate = _doprnt_truncates;
+ case 'Z':
+ base = 16;
+ goto print_signed;
+
+ case 'r':
+ truncate = _doprnt_truncates;
+ case 'R':
+ base = radix;
+ goto print_signed;
+
+ case 'n':
+ truncate = _doprnt_truncates;
+ case 'N':
+ base = radix;
+ goto print_unsigned;
+
+ print_signed:
+ n = va_arg(*argp, long);
+ if (n >= 0) {
+ u = n;
+ sign_char = plus_sign;
+ }
+ else {
+ u = -n;
+ sign_char = '-';
+ }
+ goto print_num;
+
+ print_unsigned:
+ u = va_arg(*argp, unsigned long);
+ goto print_num;
+
+ print_num:
+ {
+ char buf[MAXBUF]; /* build number here */
+ register char * p = &buf[MAXBUF-1];
+ static char digits[] = "0123456789abcdef";
+ char *prefix = 0;
+
+ if (truncate) u = (long)((int)(u));
+
+ if (u != 0 && altfmt) {
+ if (base == 8)
+ prefix = "0";
+ else if (base == 16)
+ prefix = "0x";
+ }
+
+ do {
+ *p-- = digits[u % base];
+ u /= base;
+ } while (u != 0);
+
+ length -= (&buf[MAXBUF-1] - p);
+ if (sign_char)
+ length--;
+ if (prefix)
+ length -= strlen(prefix);
+
+ if (padc == ' ' && !ladjust) {
+ /* blank padding goes before prefix */
+ while (--length >= 0)
+ (*putc)(' ', putc_arg);
+ }
+ if (sign_char)
+ (*putc)(sign_char, putc_arg);
+ if (prefix)
+ while (*prefix)
+ (*putc)(*prefix++, putc_arg);
+ if (padc == '0') {
+ /* zero padding goes after sign and prefix */
+ while (--length >= 0)
+ (*putc)('0', putc_arg);
+ }
+ while (++p != &buf[MAXBUF])
+ (*putc)(*p, putc_arg);
+
+ if (ladjust) {
+ while (--length >= 0)
+ (*putc)(' ', putc_arg);
+ }
+ break;
+ }
+
+ case '\0':
+ fmt--;
+ break;
+
+ default:
+ (*putc)(c, putc_arg);
+ }
+ fmt++;
+ }
+
+ simple_unlock(&_doprnt_lock);
+}
+
+/*
+ * Printing (to console)
+ */
+extern void cnputc( char, /*not really*/vm_offset_t);
+
+void vprintf(fmt, listp)
+ char * fmt;
+ va_list listp;
+{
+ _doprnt(fmt, &listp, cnputc, 16, 0);
+}
+
+/*VARARGS1*/
+void printf(fmt, va_alist)
+ char * fmt;
+ va_dcl
+{
+ va_list listp;
+ va_start(listp);
+ vprintf(fmt, listp);
+ va_end(listp);
+}
+
+int indent = 0;
+
+/*
+ * Printing (to console) with indentation.
+ */
+/*VARARGS1*/
+void iprintf(fmt, va_alist)
+ char * fmt;
+ va_dcl
+{
+ va_list listp;
+ register int i;
+
+ for (i = indent; i > 0; ){
+ if (i >= 8) {
+ printf("\t");
+ i -= 8;
+ }
+ else {
+ printf(" ");
+ i--;
+ }
+ }
+ va_start(listp);
+ _doprnt(fmt, &listp, cnputc, 16, 0);
+ va_end(listp);
+}
+
+/*
+ * Printing to generic buffer
+ * Returns #bytes printed.
+ * Strings are zero-terminated.
+ */
+static void
+sputc(
+ char c,
+ vm_offset_t arg)
+{
+ register char **bufp = (char **) arg;
+ register char *p = *bufp;
+ *p++ = c;
+ *bufp = p;
+}
+
+int
+sprintf( buf, fmt, va_alist)
+ char *buf;
+ char *fmt;
+ va_dcl
+{
+ va_list listp;
+ char *start = buf;
+
+ va_start(listp);
+ _doprnt(fmt, &listp, sputc, 16, (vm_offset_t)&buf);
+ va_end(listp);
+
+ *buf = 0;
+ return (buf - start);
+}
+
+
+void safe_gets(str, maxlen)
+ char *str;
+ int maxlen;
+{
+ register char *lp;
+ register int c;
+ char *strmax = str + maxlen - 1; /* allow space for trailing 0 */
+
+ lp = str;
+ for (;;) {
+ c = cngetc();
+ switch (c) {
+ case '\n':
+ case '\r':
+ printf("\n");
+ *lp++ = 0;
+ return;
+
+ case '\b':
+ case '#':
+ case '\177':
+ if (lp > str) {
+ printf("\b \b");
+ lp--;
+ }
+ continue;
+
+ case '@':
+ case 'u'&037:
+ lp = str;
+ printf("\n\r");
+ continue;
+
+ default:
+ if (c >= ' ' && c < '\177') {
+ if (lp < strmax) {
+ *lp++ = c;
+ printf("%c", c);
+ }
+ else {
+ printf("%c", '\007'); /* beep */
+ }
+ }
+ }
+ }
+}