/* * Mach Operating System * Copyright (c) 1991,1990,1989 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. */ #include #include #include #include "doprnt.h" /* * 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, "*") * * where is the output base expressed as a control character: * i.e. '\10' gives octal, '\20' gives hex. Each 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 * * If the second character in 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 */ /* * 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) */ /* * Added ANSI %p for pointers. Output looks like 0x%08x. */ /* * * Added special for L4-use: %t format * * prints L4-threadids. standard format is "task.thread". Field-width * may be specified with width-modifier. Padding begins with threadid, * up to 2 chars, task-part follows. * * modifiers: * # surrounds output with square brackets [] * l prints the high and low part of a threadid * fixed length for the dwords: 8 chars * 0 as usual, padding after optional '[' * - as usual * * Jork Loeser 9/20/99 */ #define isdigit(d) ((d) >= '0' && (d) <= '9') #define Ctod(c) ((c) - '0') #define MAXBUF (sizeof(long int) * 8) /* enough for binary */ static char digs[] = "0123456789abcdef"; static void printnum(u, base, putc, putc_arg) register unsigned long u; /* number to print */ register int base; void (*putc)(); char *putc_arg; { char buf[MAXBUF]; /* build number here */ register char * p = &buf[MAXBUF-1]; do { *p-- = digs[u % base]; u /= base; } while (u != 0); while (++p != &buf[MAXBUF]) (*putc)(putc_arg, *p); } static void printnum_16(u, putc, putc_arg) register unsigned long u; /* number to print */ void (*putc)(); char *putc_arg; { char buf[8]; /* build number here */ register char * p = &buf[7]; int i; for(i=0; i<8;i++){ *p-- = digs[u & 0x0f]; u >>= 4; }; for(i=0;i<8;i++){ (*putc)(putc_arg, buf[i]); } } boolean_t _doprnt_truncates = FALSE; void _doprnt(fmt, args, radix, putc, putc_arg) register const char *fmt; va_list args; int radix; /* default radix - for '%r' */ void (*putc)(); /* character output */ char *putc_arg; /* argument for putc */ { int length; int prec; boolean_t ladjust; char padc; long long n, m; unsigned long long u; int plus_sign; int sign_char; boolean_t altfmt, truncate, upcase; int base; char c; int longopt; while (*fmt != '\0') { if (*fmt != '%') { (*putc)(putc_arg, *fmt++); continue; } fmt++; length = 0; prec = -1; ladjust = FALSE; padc = ' '; plus_sign = 0; sign_char = 0; altfmt = FALSE; longopt = 0; while (TRUE) { if (*fmt == '#') { altfmt = TRUE; fmt++; } else if (*fmt == '-') { ladjust = TRUE; fmt++; } else if (*fmt == '+') { plus_sign = '+'; fmt++; } else if (*fmt == ' ') { if (plus_sign == 0) plus_sign = ' '; fmt++; } else break; } if (*fmt == '0') { padc = '0'; fmt++; } if (isdigit(*fmt)) { while(isdigit(*fmt)) length = 10 * length + Ctod(*fmt++); } else if (*fmt == '*') { length = va_arg(args, int); fmt++; if (length < 0) { ladjust = !ladjust; length = -length; } } if (*fmt == '.') { fmt++; if (isdigit(*fmt)) { prec = 0; while(isdigit(*fmt)) prec = 10 * prec + Ctod(*fmt++); } else if (*fmt == '*') { prec = va_arg(args, int); fmt++; } } while (*fmt == 'l'){ longopt++; fmt++; } truncate = FALSE; upcase = FALSE; switch(*fmt) { case 'b': case 'B': { register char *p; boolean_t any; register int i; u = va_arg(args, unsigned long); p = va_arg(args, char *); base = *p++; printnum(u, base, putc, putc_arg); if (u == 0) break; any = FALSE; while ((i = *p++) != 0) { /* 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)(putc_arg, c); 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)(putc_arg, c); } else { for (; *p > 32; p++) continue; } } if (any) (*putc)(putc_arg, '>'); break; } case 'c': c = va_arg(args, int); (*putc)(putc_arg, c); break; case 't': { typedef struct { unsigned version_low:10; unsigned lthread:7; unsigned task:11; unsigned version_high:4; unsigned site:17; unsigned chief:11; unsigned nest:4; } tid_t; typedef struct { unsigned high; unsigned low; } lh_t; union tid_t { tid_t id; lh_t lh; } tid; tid = va_arg(args, union tid_t); if(longopt){ if(altfmt){ n = 19; } else { n = 17; } if (length > 0 && !ladjust) { while(n < length){ putc(putc_arg, ' '); n++; } } if(altfmt) putc(putc_arg, '['); printnum_16( tid.lh.high, putc, putc_arg); putc(putc_arg, ':'); printnum_16( tid.lh.low, putc, putc_arg); if(altfmt) putc(putc_arg, ']'); if(length > 0 && ladjust) { while(n < length){ putc(putc_arg, ' '); n++; } } } else { if(altfmt){ n = 4; } else { n = 2; } m = 1; m += tid.id.lthread >= 0x10; n += tid.id.task >= 0x10; n += tid.id.task >= 0x100; if (length > 0 && !ladjust && padc == ' ') { while (n + 2 < length) { (*putc)(putc_arg, ' '); n++; } } if(altfmt) (*putc)(putc_arg, '['); if( length > 0 && !ladjust && padc == '0') { while (n + 2 < length) { putc(putc_arg, '0'); n++; } } printnum(tid.id.task, 16, putc, putc_arg); putc(putc_arg, '.'); if(length > 0 && !ladjust) { while (n+m < length){ putc(putc_arg, padc); n++; } } printnum(tid.id.lthread, 16, putc, putc_arg); if(altfmt) putc(putc_arg, ']'); if (n + m < length && ladjust) { while (n + m < length) { (*putc)(putc_arg, ' '); n++; } } } break; } case 's': { register char *p; register char *p2; if (prec == -1) prec = 0x7fffffff; /* MAXINT */ p = va_arg(args, 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)(putc_arg, *p++); } 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 'p': padc = '0'; length = 8; /* * We do this instead of just setting altfmt to TRUE * because we want 0 to have a 0x in front, and we want * eight digits after the 0x -- not just 6. */ (*putc)(putc_arg, '0'); (*putc)(putc_arg, 'x'); case 'x': truncate = _doprnt_truncates; base = 16; goto print_unsigned; case 'X': base = 16; upcase = TRUE; goto print_unsigned; case 'z': truncate = _doprnt_truncates; base = 16; goto print_signed; case 'Z': base = 16; upcase = TRUE; 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: if (longopt>1) n = va_arg(args, long long); else n = va_arg(args, long); if (n >= 0) { u = n; sign_char = plus_sign; } else { u = -n; sign_char = '-'; } goto print_num; print_unsigned: if (longopt>1) u = va_arg(args, unsigned long long); else u = va_arg(args, unsigned long); goto print_num; print_num: { char buf[MAXBUF]; /* build number here */ register char * p = &buf[MAXBUF-1]; static char ldigits[] = "0123456789abcdef"; static char udigits[] = "0123456789ABCDEF"; char *digits = ldigits; char *prefix = 0; if (truncate) u = (long)((int)(u)); if (upcase) digits = udigits; 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)(putc_arg, sign_char); if (prefix) while (*prefix) (*putc)(putc_arg, *prefix++); if (padc == '0') { /* zero padding goes after sign and prefix */ while (--length >= 0) (*putc)(putc_arg, '0'); } while (++p != &buf[MAXBUF]) (*putc)(putc_arg, *p); if (ladjust) { while (--length >= 0) (*putc)(putc_arg, ' '); } break; } case '\0': fmt--; break; default: (*putc)(putc_arg, *fmt); } fmt++; } }