123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #include <string.h> /* strlen() */
- #include "do_printf.h"
- /*****************************************************************************
- Revised Jan 28, 2002
- - changes to make characters 0x80-0xFF display properly
- Revised June 10, 2001
- - changes to make vsprintf() terminate string with '\0'
- Revised May 12, 2000
- - math in DO_NUM is now unsigned, as it should be
- - %0 flag (pad left with zeroes) now works
- - actually did some TESTING, maybe fixed some other bugs
- name: do_printf
- action: minimal subfunction for ?printf, calls function
- 'fn' with arg 'ptr' for each character to be output
- returns:total number of characters output
- %[flag][width][.prec][mod][conv]
- flag: - left justify, pad right w/ blanks DONE
- 0 pad left w/ 0 for numerics DONE
- + always print sign, + or - no
- ' ' (blank) no
- # (???) no
- width: (field width) DONE
- prec: (precision) no
- conv: d,i decimal int DONE
- u decimal unsigned DONE
- o octal DONE
- x,X hex DONE
- f,e,g,E,G float no
- c char DONE
- s string DONE
- p ptr DONE
- mod: N near ptr DONE
- F far ptr no
- h short (16-bit) int DONE
- l long (32-bit) int DONE
- L long long (64-bit) int no
- *****************************************************************************/
- /* flags used in processing format string */
- #define PR_LJ 0x01 /* left justify */
- #define PR_CA 0x02 /* use A-F instead of a-f for hex */
- #define PR_SG 0x04 /* signed numeric conversion (%d vs. %u) */
- #define PR_32 0x08 /* long (32-bit) numeric conversion */
- #define PR_16 0x10 /* short (16-bit) numeric conversion */
- #define PR_WS 0x20 /* PR_SG set and num was < 0 */
- #define PR_LZ 0x40 /* pad left with '0' instead of ' ' */
- #define PR_FP 0x80 /* pointers are far */
- /* largest number handled is 2^32-1, lowest radix handled is 8.
- 2^32-1 in base 8 has 11 digits (add 5 for trailing NUL and for slop) */
- #define PR_BUFLEN 16
- int do_printf(const char *fmt, va_list args, fnptr_t fn, void *ptr)
- {
- unsigned state, flags, radix, actual_wd, count, given_wd;
- unsigned char *where, buf[PR_BUFLEN];
- long num;
- state = flags = count = given_wd = 0;
- /* begin scanning format specifier list */
- for(; *fmt; fmt++)
- {
- switch(state)
- {
- /* STATE 0: AWAITING % */
- case 0:
- if(*fmt != '%') /* not %... */
- {
- fn(*fmt, &ptr); /* ...just echo it */
- count++;
- break;
- }
- /* found %, get next char and advance state to check if next char is a flag */
- state++;
- fmt++;
- /* FALL THROUGH */
- /* STATE 1: AWAITING FLAGS (%-0) */
- case 1:
- if(*fmt == '%') /* %% */
- {
- fn(*fmt, &ptr);
- count++;
- state = flags = given_wd = 0;
- break;
- }
- if(*fmt == '-')
- {
- if(flags & PR_LJ)/* %-- is illegal */
- state = flags = given_wd = 0;
- else
- flags |= PR_LJ;
- break;
- }
- /* not a flag char: advance state to check if it's field width */
- state++;
- /* check now for '%0...' */
- if(*fmt == '0')
- {
- flags |= PR_LZ;
- fmt++;
- }
- /* FALL THROUGH */
- /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */
- case 2:
- if(*fmt >= '0' && *fmt <= '9')
- {
- given_wd = 10 * given_wd +
- (*fmt - '0');
- break;
- }
- /* not field width: advance state to check if it's a modifier */
- state++;
- /* FALL THROUGH */
- /* STATE 3: AWAITING MODIFIER CHARS (FNlh) */
- case 3:
- if(*fmt == 'F')
- {
- flags |= PR_FP;
- break;
- }
- if(*fmt == 'N')
- break;
- if(*fmt == 'l')
- {
- flags |= PR_32;
- break;
- }
- if(*fmt == 'h')
- {
- flags |= PR_16;
- break;
- }
- /* not modifier: advance state to check if it's a conversion char */
- state++;
- /* FALL THROUGH */
- /* STATE 4: AWAITING CONVERSION CHARS (Xxpndiuocs) */
- case 4:
- where = buf + PR_BUFLEN - 1;
- *where = '\0';
- switch(*fmt)
- {
- case 'X':
- flags |= PR_CA;
- /* FALL THROUGH */
- /* xxx - far pointers (%Fp, %Fn) not yet supported */
- case 'x':
- case 'p':
- case 'n':
- radix = 16;
- goto DO_NUM;
- case 'd':
- case 'i':
- flags |= PR_SG;
- /* FALL THROUGH */
- case 'u':
- radix = 10;
- goto DO_NUM;
- case 'o':
- radix = 8;
- /* load the value to be printed. l=long=32 bits: */
- DO_NUM: if(flags & PR_32)
- num = va_arg(args, unsigned long);
- /* h=short=16 bits (signed or unsigned) */
- else if(flags & PR_16)
- {
- if(flags & PR_SG)
- num = va_arg(args, unsigned long);
- else
- num = va_arg(args, unsigned long);
- }
- /* no h nor l: sizeof(int) bits (signed or unsigned) */
- else
- {
- if(flags & PR_SG)
- num = va_arg(args, unsigned long);
- else
- num = va_arg(args, unsigned long);
- }
- /* take care of sign */
- if(flags & PR_SG)
- {
- if(num < 0)
- {
- flags |= PR_WS;
- num = -num;
- }
- }
- /* convert binary to octal/decimal/hex ASCII
- OK, I found my mistake. The math here is _always_ unsigned */
- do
- {
- unsigned long temp;
- temp = (unsigned long)num % radix;
- where--;
- if(temp < 10)
- *where = temp + '0';
- else if(flags & PR_CA)
- *where = temp - 10 + 'A';
- else
- *where = temp - 10 + 'a';
- num = (unsigned long)num / radix;
- }
- while(num != 0);
- goto EMIT;
- case 'c':
- /* disallow pad-left-with-zeroes for %c */
- flags &= ~PR_LZ;
- where--;
- *where = (unsigned char)va_arg(args,
- unsigned int);
- actual_wd = 1;
- goto EMIT2;
- case 's':
- /* disallow pad-left-with-zeroes for %s */
- flags &= ~PR_LZ;
- where = (unsigned char *)va_arg(args, unsigned int);
- EMIT:
- actual_wd = strlen((const char *)where);
- if(flags & PR_WS)
- actual_wd++;
- /* if we pad left with ZEROES, do the sign now */
- if((flags & (PR_WS | PR_LZ)) ==
- (PR_WS | PR_LZ))
- {
- fn('-', &ptr);
- count++;
- }
- /* pad on left with spaces or zeroes (for right justify) */
- EMIT2: if((flags & PR_LJ) == 0)
- {
- while(given_wd > actual_wd)
- {
- fn(flags & PR_LZ ? '0' :
- ' ', &ptr);
- count++;
- given_wd--;
- }
- }
- /* if we pad left with SPACES, do the sign now */
- if((flags & (PR_WS | PR_LZ)) == PR_WS)
- {
- fn('-', &ptr);
- count++;
- }
- /* emit string/char/converted number */
- while(*where != '\0')
- {
- fn(*where++, &ptr);
- count++;
- }
- /* pad on right with spaces (for left justify) */
- if(given_wd < actual_wd)
- given_wd = 0;
- else given_wd -= actual_wd;
- for(; given_wd; given_wd--)
- {
- fn(' ', &ptr);
- count++;
- }
- break;
- default:
- break;
- }
- default:
- state = flags = given_wd = 0;
- break;
- }
- }
- return count;
- }
|