// Stripped-down printf. Unlike kernel printf, formats to buffer // and then sends buffer in one go with sys_cputs. #include "lib.h" /* * Put a number(base <= 16) in a buffer in reverse order; return an * optional length and a pointer to the NULL terminated(preceded?) * buffer. */ static char * printnum(u_int uq, int base, char *buf, int *lenp) { /* A quad in binary, plus NULL. */ register char *p; p = buf; *buf = 0; do { *++p = "0123456789abcdef"[uq % base]; } while (uq /= base); if (lenp) *lenp = p - buf; return(p); } /* * Scaled down version of printf(3). * * Three additional formats: * * The format %b is supported to decode error registers. * Its usage is: * * printf("reg=%b\n", regval, "*"); * * where is the output base 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: * * kprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); * * would produce output: * * reg=3 * * The format %r passes an additional format string and argument list * recursively. Its usage is: * * fn(char *fmt, ...) * { * va_list ap; * va_start(ap, fmt); * printf("prefix: %r: suffix\n", fmt, ap); * va_end(ap); * } * * Space or zero padding and a field width are supported for the numeric * formats only. * * The format %e takes an integer error code and prints a string describing the error. * The integer may be positive or negative, so that -E_NO_MEM and E_NO_MEM are * equivalent. */ static char *error_string[MAXERROR+1] = { NULL, "unspecified error", "bad environment", "invalid parameter", "out of memory", "out of environments", "env is not recving", }; static u_int getint(va_list *ap, int lflag, int qflag) { if (lflag) return va_arg(*ap, u_long); else /*if (qflag) return va_arg(*ap, u_quad_t); else*/ return va_arg(*ap, u_int); } static void buf_putc(char **pbuf, char *ebuf, int ch) { char *buf; buf = *pbuf; if(buf >= ebuf){ *pbuf = ebuf-1; ebuf[-1] = 0; return; } *buf++ = ch; *pbuf = buf; } int vsnprintf(char *buf, int m, const char *fmt, va_list ap) { char *obuf, *ebuf; register char *p, *q; register int ch, n; u_int uq; int base, lflag, qflag, tmp, width; char padc; char numbuf[sizeof(u_quad_t) * 8 + 1]; obuf = buf; ebuf = buf+m; for (;;) { padc = ' '; width = 0; while ((ch = *(u_char *) fmt++) != '%') { buf_putc(&buf, ebuf, ch); if (ch == '\0') return buf-1-obuf; } lflag = 0; qflag = 0; reswitch: switch (ch = *(u_char *) fmt++) { case '0': padc = '0'; goto reswitch; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (width = 0;; ++fmt) { width = width * 10 + ch - '0'; ch = *fmt; if (ch < '0' || ch > '9') break; } goto reswitch; case 'l': lflag = 1; qflag = 0; /* this should be done better */ goto reswitch; case 'q': qflag = 1; lflag = 0; /* this should be done better */ goto reswitch; case 'b': uq = va_arg(ap, int); p = va_arg(ap, char *); for (q = printnum(uq, *p++, numbuf, NULL); (ch = *q--) != '\0';) buf_putc(&buf, ebuf, ch); if (!uq) break; for (tmp = 0; (n = *p++) != '\0'; ) { if (uq & (1 << (n - 1))) { buf_putc(&buf, ebuf, tmp ? ',' : '<'); for (; (n = *p) > ' '; ++p) buf_putc(&buf, ebuf, n); tmp = 1; } else for (; *p > ' '; ++p) continue; } if (tmp) buf_putc(&buf, ebuf, '>'); break; case 'c': buf_putc(&buf, ebuf, va_arg(ap, int)); break; case 'e': n = va_arg(ap, int); if(n < 0) n = -n; if(n > MAXERROR || (p = error_string[n]) == NULL) buf += snprintf(buf, ebuf-buf, "error %d", n); else buf += snprintf(buf, ebuf-buf, "%s", p); break; case 'r': p = va_arg(ap, char *); buf += vsnprintf(buf, ebuf-buf, p, va_arg(ap, va_list)); break; case 's': if ((p = va_arg(ap, char *)) == NULL) p = "(null)"; while ((ch = *p++) != '\0') buf_putc(&buf, ebuf, ch); break; case 'd': uq = getint(&ap, lflag, qflag); /*if (qflag && (quad_t) uq < 0) { buf_putc(&buf, ebuf, '-'); uq = -(quad_t) uq; } else*/ if ((long)uq < 0) { buf_putc(&buf, ebuf, '-'); uq = -(long) uq; } base = 10; goto number; case 'u': uq = getint(&ap, lflag, qflag); base = 10; goto number; case 'o': uq = getint(&ap, lflag, qflag); base = 8; goto number; case 'p': buf_putc(&buf, ebuf, '0'); buf_putc(&buf, ebuf, 'x'); uq = (u_long) va_arg(ap, void *); base = 16; goto number; case 'x': uq = getint(&ap, lflag, qflag); base = 16; number: p = printnum(uq, base, numbuf, &tmp); if (width && (width -= tmp) > 0) while (width--) buf_putc(&buf, ebuf, padc); while ((ch = *p--) != '\0') buf_putc(&buf, ebuf, ch); break; default: buf_putc(&buf, ebuf, '%'); if (lflag) buf_putc(&buf, ebuf, 'l'); /* FALLTHROUGH */ case '%': buf_putc(&buf, ebuf, ch); } } } int snprintf(char *buf, int n, const char *fmt, ...) { int m; va_list ap; va_start(ap, fmt); m = vsnprintf(buf, n, fmt, ap); va_end(ap); return m; } /* * Panic is called on unresolvable fatal errors. * It prints "panic: mesg", and then enters an infinite loop. * If executing on Bochs, drop into the debugger rather than chew CPU. */ void _panic(const char *file, int line, const char *fmt,...) { va_list ap; char buf[256]; int n; va_start(ap, fmt); n = snprintf(buf, sizeof buf, "user panic at %s:%d: ", file, line); n += vsnprintf(buf+n, sizeof buf-n, fmt, ap); n += snprintf(buf+n, sizeof buf-n, "\n"); va_end(ap); sys_cputs(buf); for(;;); } /* like panic, but don't */ void warn(const char *fmt, ...) { va_list ap; char buf[256]; int n; va_start(ap, fmt); n = snprintf(buf, sizeof buf, "warning: "); n += vsnprintf(buf+n, sizeof buf-n, fmt, ap); n += snprintf(buf+n, sizeof buf-n, "\n"); va_end(ap); sys_cputs(buf); } void printf(const char *fmt, ...) { char buf[256]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); sys_cputs(buf); }