1 |
|
/* Common code for printf. |
2 |
|
Copyright 1999-2002 Johan Rydberg, jrydberg@rtmk.org. |
3 |
|
|
4 |
/* |
This program is free software; you can redistribute it and/or modify |
5 |
* Mach Operating System |
it under the terms of the GNU General Public License as published by |
6 |
* Copyright (c) 1993 Carnegie Mellon University |
the Free Software Foundation; either version 2 of the License, or |
7 |
* All Rights Reserved. |
(at your option) any later version. |
8 |
* |
|
9 |
* Permission to use, copy, modify and distribute this software and its |
This program is distributed in the hope that it will be useful, |
10 |
* documentation is hereby granted, provided that both the copyright |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
* notice and this permission notice appear in all copies of the |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
* software, derivative works or modified versions, and any portions |
GNU General Public License for more details. |
13 |
* thereof, and that both notices appear in supporting documentation. |
|
14 |
* |
You should have received a copy of the GNU General Public License |
15 |
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
along with this program; if not, write to the Free Software |
16 |
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
|
* 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) |
|
|
*/ |
|
17 |
|
|
|
#include <stdbool.h> |
|
18 |
#include <stdarg.h> |
#include <stdarg.h> |
19 |
#include "libkern.h" |
#include "libkern.h" |
20 |
|
|
|
#define isdigit(d) ((d) >= '0' && (d) <= '9') |
|
|
#define Ctod(c) ((c) - '0') |
|
|
|
|
|
#define MAXBUF (sizeof(long int) * 8) /* enough for binary */ |
|
|
|
|
|
static void |
|
|
printnum (register unsigned long u, |
|
|
register int base, void (*putc) (char, void *), void *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); |
|
|
|
|
|
} |
|
|
|
|
|
static bool _doprnt_truncates = false; |
|
|
|
|
|
static void |
|
|
_doprnt (register char *fmt, va_list *argp, |
|
|
/* character output routine */ |
|
|
void (*putc) (char, void *), int radix, /* default radix - for '%r' */ |
|
|
void *putc_arg) |
|
|
{ |
|
|
int length; |
|
|
int prec; |
|
|
bool ladjust; |
|
|
char padc; |
|
|
long n; |
|
|
unsigned long u; |
|
|
int plus_sign; |
|
|
int sign_char; |
|
|
bool altfmt, truncate; |
|
|
int base; |
|
|
register char c; |
|
|
|
|
|
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; |
|
|
padc = '0'; |
|
|
} |
|
|
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; |
|
|
bool 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 'p': |
|
|
truncate = _doprnt_truncates; |
|
|
case 'P': |
|
|
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++; |
|
|
} |
|
|
} |
|
21 |
|
|
22 |
static void (*putc_fn) (char, void *) = 0; |
static void (*putc_fn) (char, void *) = 0; |
23 |
static void *putc_arg = 0; |
static void *putc_arg = 0; |
38 |
if (! putc_fn) |
if (! putc_fn) |
39 |
return 0; |
return 0; |
40 |
|
|
|
#if 1 |
|
41 |
__lprintf (putc_arg, fmt, listp, putc_fn); |
__lprintf (putc_arg, fmt, listp, putc_fn); |
|
#else |
|
|
_doprnt (fmt, &listp, putc_fn, 16, putc_arg); |
|
|
#endif |
|
42 |
return 0; |
return 0; |
43 |
} |
} |
44 |
|
|
54 |
{ |
{ |
55 |
va_list listp; |
va_list listp; |
56 |
va_start (listp, fmt); |
va_start (listp, fmt); |
|
#if 0 |
|
|
_doprnt ((char *) fmt, &listp, putc_fn, 16, putc_arg); |
|
|
#else |
|
57 |
__lprintf (putc_arg, fmt, listp, putc_fn); |
__lprintf (putc_arg, fmt, listp, putc_fn); |
|
#endif |
|
58 |
va_end (listp); |
va_end (listp); |
59 |
return 0; |
return 0; |
60 |
} |
} |
70 |
{ |
{ |
71 |
char *start_buf = buf; |
char *start_buf = buf; |
72 |
|
|
|
#if 0 |
|
|
_doprnt (fmt, &listp, savechar, 16, (void *) &buf); |
|
|
#else |
|
73 |
__lprintf (savechar, fmt, listp, (void *) &buf); |
__lprintf (savechar, fmt, listp, (void *) &buf); |
|
#endif |
|
74 |
*buf = '\0'; |
*buf = '\0'; |
75 |
return (int) (buf - start_buf); |
return (int) (buf - start_buf); |
76 |
} |
} |