/*++ Copyright (c) 1995 Microsoft Corporation Module Name: rprintf.cxx Abstract: Contains my own version of printf(), sprintf() and vprintf(). Useful since adding new printf escape sequences becomes easy Contents: rprintf limited re-entrant version of printf rsprintf limited re-entrant version of sprintf _sprintf routine which does the work Author: Richard L Firth (rfirth) 20-Jun-1995 Revision History: 29-Aug-1989 rfirth Created --*/ #include #include "rprintf.h" // // defines for flags word // #define F_SPACES 0x00000001 // prefix field with spaces #define F_ZEROES 0x00000002 // prefix field with zeroes #define F_MINUS 0x00000004 // field is left justified #define F_HASH 0x00000008 // hex field is prefixed with 0x/0X #define F_XUPPER 0x00000010 // hex field has upper case letters #define F_LONG 0x00000020 // long int/hex/oct prefix #define F_PLUS 0x00000040 // prefix +'ve signed number with + #define F_DOT 0x00000080 // separator for field and precision #define F_NEAR 0x00000100 // far pointer has near prefix #define F_FAR 0x00000200 // near pointer has far prefix #define F_SREPLICATE 0x00000400 // this field replicated #define F_EREPLICATE 0x00000800 // end of replications #define F_UNICODE 0x00001000 // string is wide character (%ws/%wq) #define F_QUOTING 0x00002000 // strings enclosed in double quotes #define F_ELLIPSE 0x00004000 // a sized, quoted string ends in "..." #define BUFFER_SIZE 1024 // // minimum field widths for various ASCII representations of numbers // #define MIN_BIN_WIDTH 16 // minimum field width in btoa #define MIN_HEX_WIDTH 8 // minimum field width in xtoa #define MIN_INT_WIDTH 10 // minimum field width in itoa #define MIN_LHEX_WIDTH 8 // minimum field width in long xtoa #define MIN_LINT_WIDTH 10 // minimum field width in long itoa #define MIN_LOCT_WIDTH 11 // minimum field width in long otoa #define MIN_OCT_WIDTH 11 // minimum field width in otoa #define MIN_UINT_WIDTH 10 // minimum field width in utoa // // character defines // #define EOSTR '\0' #define CR '\x0d' #define LF '\x0a' #if !defined(min) #define min(a, b) ((a)<(b)) ? (a) : (b) #endif PRIVATE int _atoi(char**); PRIVATE void convert(char**, ULONG_PTR, int, int, unsigned, char(*)(ULONG_PTR*)); PRIVATE char btoa(ULONG_PTR *); PRIVATE char otoa(ULONG_PTR *); PRIVATE char utoa(ULONG_PTR *); PRIVATE char xtoa(ULONG_PTR *); PRIVATE char Xasc(ULONG_PTR *); /*** rprintf - a re-entrant cut-down version of printf. Understands usual * printf format characters introduced by '%' plus one or * two additions * * ENTRY format - pointer to buffer containing format string defining * the output. As per usual printf the arguments to * fill in the blanks in the format string are on the * the stack after the format string * * - arguments on stack, size and type determined from * the format string * * EXIT format string used to convert arguments (if any) and print * the results to stdout. * The number of character copied is the value returned */ #ifdef UNUSED int cdecl rprintf(char* format, ...) { int charsPrinted = 0; char buffer[BUFFER_SIZE]; DWORD nwritten; va_list args; /* print the output into buffer then print the formatted buffer to the * screen */ va_start(args, format); charsPrinted = _sprintf(buffer, format, args); va_end(args); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), buffer, charsPrinted, &nwritten, 0 ); return nwritten; } #endif /*** rsprintf - a re-entrant cut-down version of sprintf. See rprintf for * details * * ENTRY buffer - pointer to the buffer which will receive the * formatted output * * format - pointer to buffer which defines the formatted * output. Consists of normal printing characters * and printf-style format characters (see rprintf) * * EXIT characters from format string and arguments converted to * character format based on format string are copied into the * buffer * The number of character copied is the value returned */ int cdecl rsprintf(char* buffer, char* format, ...) { va_list args; int n; va_start(args, format); n = _sprintf(buffer, format, args); va_end(args); return n; } /*** _sprintf - performs the sprintf function. Receives an extra parameter * on the stack which is the pointer to the variable argument * list of rprintf and rsprintf * * ENTRY buffer - pointer to buffer which will receive the output * * format - pointer to the format string * * args - variable argument list which will be used to fill in * the escape sequences in the format string * * EXIT The characters in the format string are used to convert the * arguments and copy them to the buffer. * The number of character copied is the value returned */ int cdecl _sprintf(char* buffer, char* format, va_list args) { char* original = buffer; int FieldWidth; int FieldPrecision; int FieldLen; BOOL SubDone; int StrLen; int i; DWORD flags; int replications; while (*format) { switch ((unsigned)*format) { case '\n': // // convert line-feed to carriage-return, line-feed. But only if the // format string doesn't already contain a carriage-return directly // before the line-feed! This way we can make multiple calls into // this function, with the same buffer, and only once expand the // line-feed // if (*(buffer - 1) != CR) { *buffer++ = CR; } *buffer++ = LF; break; case '%': SubDone = FALSE; flags = 0; FieldWidth = 0; FieldPrecision = 0; replications = 1; /* default replication is 1 */ while (!SubDone) { switch ((unsigned)*++format) { case '%': *buffer++ = '%'; SubDone = TRUE; break; case ' ': flags |= F_SPACES; break; case '#': flags |= F_HASH; break; case '-': flags |= F_MINUS; break; case '+': flags |= F_PLUS; break; case '.': flags |= F_DOT; break; case '*': if (flags & F_DOT) { FieldPrecision = va_arg(args, int); } else { FieldWidth = va_arg(args, int); } break; case '@': replications = _atoi(&format); break; case '[': flags |= F_SREPLICATE; break; case ']': flags |= F_EREPLICATE; break; case '0': /* if this is leading zero then caller wants * zero prefixed number of given width (%04x) */ if (!(flags & F_ZEROES)) { flags |= F_ZEROES; break; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (flags & F_DOT) { FieldPrecision = _atoi(&format); } else { FieldWidth = _atoi(&format); } break; case 'b': // // Binary representation // while (replications--) { convert(&buffer, va_arg(args, unsigned int), (FieldWidth) ? FieldWidth : MIN_BIN_WIDTH, MIN_BIN_WIDTH, flags, btoa ); } SubDone = TRUE; break; case 'B': // // Boolean representation // if (va_arg(args, BOOL)) { *buffer++ = 'T'; *buffer++ = 'R'; *buffer++ = 'U'; *buffer++ = 'E'; } else { *buffer++ = 'F'; *buffer++ = 'A'; *buffer++ = 'L'; *buffer++ = 'S'; *buffer++ = 'E'; } SubDone = TRUE; break; case 'c': // // assume that a character is the size of the // width of the stack which in turn has the same // width as an integer // --FieldWidth; while (replications--) { for (i = 0; i < FieldWidth; i++) { *buffer++ = ' '; } *buffer++ = (char) va_arg(args, int); } SubDone = TRUE; break; case 'd': case 'i': while (replications--) { long l; l = (flags & F_LONG) ? va_arg(args, long) : (long)va_arg(args, int); if (l < 0) { *buffer++ = '-'; if (flags & F_LONG) { l = -(long)l; } else { l = -(int)l; } } else if (flags & F_PLUS) { *buffer++ = '+'; } convert(&buffer, l, FieldWidth, (flags & F_LONG) ? MIN_LINT_WIDTH : MIN_INT_WIDTH, flags, utoa ); } SubDone = TRUE; break; case 'e': /* not currently handled */ break; case 'f': /* not currently handled */ break; case 'F': flags |= F_FAR; break; case 'g': case 'G': /* not currently handled */ break; case 'h': /* not currently handled */ break; case 'l': flags |= F_LONG; break; case 'L': /* not currently handled */ break; case 'n': *(va_arg(args, int*)) = (int)(buffer - original); SubDone = TRUE; break; case 'N': flags |= F_NEAR; break; case 'o': while (replications--) { convert(&buffer, (flags & F_LONG) ? va_arg(args, unsigned long) : (unsigned long)va_arg(args, unsigned), FieldWidth, (flags & F_LONG) ? MIN_LOCT_WIDTH : MIN_OCT_WIDTH, flags, otoa ); } SubDone = TRUE; break; case 'p': while (replications--) { void* p; if (!(flags & F_NEAR)) { convert(&buffer, (ULONG_PTR) va_arg(args, char near *), MIN_HEX_WIDTH, MIN_HEX_WIDTH, flags | F_XUPPER, Xasc ); *buffer++ = ':'; } convert(&buffer, (ULONG_PTR)va_arg(args, unsigned), MIN_HEX_WIDTH, MIN_HEX_WIDTH, flags | F_XUPPER, Xasc ); } SubDone = TRUE; break; case 'Q': // quoted unicode string flags |= F_UNICODE; // *** FALL THROUGH *** case 'q': *buffer++ = '"'; flags |= F_QUOTING; // // *** FALL THROUGH *** // case 's': while (replications--) { char* s; s = va_arg(args, char*); if (s != NULL) { // darrenmi 2/24/00 Note that if the string has a field precision, // it's not always null terminated!! Don't depend on it being psz // and stop when we hit our max length. StrLen = 0; if (flags & F_UNICODE) { WCHAR *pWork = (LPWSTR)s; while((!FieldPrecision || StrLen < FieldPrecision) && *pWork) { pWork++; StrLen++; } } else { CHAR *pWork = s; while((!FieldPrecision || StrLen < FieldPrecision) && *pWork) { pWork++; StrLen++; } } FieldLen = (FieldPrecision) ? min(StrLen, FieldPrecision) : StrLen ; if ((flags & F_QUOTING) && (FieldPrecision > 3) && (FieldLen == FieldPrecision)) { FieldLen -= 3; flags |= F_ELLIPSE; } for (i = 0; i < (FieldWidth - FieldLen); i++) { *buffer++ = ' '; } if (flags & F_UNICODE) { char wbuf[4096]; int wi; WideCharToMultiByte(CP_ACP, 0, (LPWSTR)s, -1, wbuf, 4096, NULL, NULL); for (wi = 0; wbuf[wi] && FieldLen; ++wi) { *buffer = wbuf[wi]; // // if this is a quoted string, and it contains // \r and/or \n, then we reverse-convert these // characters, since we don't want then to // break the string. Do the same for \t // if (flags & F_QUOTING) { char ch; ch = *buffer; if ((ch == '\r') || (ch == '\n') || (ch == '\t')) { *buffer++ = '\\'; *buffer = (ch == '\r') ? 'r' : (ch == '\n') ? 'n' : 't' ; } } ++buffer; --FieldLen; } } else { while (*s && FieldLen) { *buffer = *s++; // // if this is a quoted string, and it contains // \r and/or \n, then we reverse-convert these // characters, since we don't want then to // break the string. Do the same for \t // if (flags & F_QUOTING) { char ch; ch = *buffer; if ((ch == '\r') || (ch == '\n') || (ch == '\t')) { *buffer++ = '\\'; *buffer = (ch == '\r') ? 'r' : (ch == '\n') ? 'n' : 't' ; } } ++buffer; --FieldLen; } } if (flags & F_ELLIPSE) { *buffer++ = '.'; *buffer++ = '.'; *buffer++ = '.'; } } else if (!(flags & F_QUOTING)) { *buffer++ = '('; *buffer++ = 'n'; *buffer++ = 'u'; *buffer++ = 'l'; *buffer++ = 'l'; *buffer++ = ')'; } } if (flags & F_QUOTING) { *buffer++ = '"'; } SubDone = TRUE; break; case 'S': break; case 'u': while (replications--) { convert(&buffer, va_arg(args, unsigned), FieldWidth, MIN_UINT_WIDTH, flags, utoa ); } SubDone = TRUE; break; case 'w': flags |= F_UNICODE; break; case 'X': flags |= F_XUPPER; // // *** FALL THROUGH *** // case 'x': while (replications--) { if (flags & F_HASH) { *buffer++ = '0'; *buffer++ = (flags & F_XUPPER) ? (char)'X' : (char)'x'; } convert(&buffer, (flags & F_LONG) ? va_arg(args, unsigned long) : va_arg(args, unsigned), FieldWidth, (flags & F_LONG) ? MIN_LHEX_WIDTH : MIN_HEX_WIDTH, flags, (flags & F_XUPPER) ? Xasc : xtoa ); } SubDone = TRUE; break; } /* switch <%-specifier> */ } break; default: *buffer++ = *format; } /* switch */ ++format; } /* while */ *buffer = EOSTR; return (int)(buffer - original); } /*** _atoi - ascii to integer conversion used to get the field width out * of the format string * * ENTRY p - pointer to pointer to format string * * EXIT returns the number found in the prefix string as a (16-bit) * int format string pointer is updated past the field width */ PRIVATE int _atoi(char** p) { int n = 0; int i = 5; while ((**p >= '0' && **p <= '9') && i--) { n = n*10+((int)(*(*p)++)-(int)'0'); } /* put the format pointer back one since the major loop tests *++format */ --*p; return n; } /*** convert - convert number to representation defined by procedure * * ENTRY buffer - pointer to buffer to receive conversion * n - number to convert * width - user defined field width * mwidth - minimum width for representation * flags - flags controlling conversion * proc - pointer to conversion routine * * EXIT buffer is updated to point past the number representation * just put into it */ PRIVATE void convert( char** buffer, ULONG_PTR n, int width, int mwidth, unsigned flags, char (*proc)(ULONG_PTR*) ) { char numarray[33]; int MinWidth; int i; MinWidth = (width < mwidth) ? mwidth : width; i = MinWidth; do { numarray[--i] = (*proc)(&n); } while (n); while (width > MinWidth-i) { numarray[--i] = (char)((flags & F_SPACES) ? ' ' : '0'); } while (i < MinWidth) { *(*buffer)++ = numarray[i++]; } } /*** btoa - return next (least significant) char in a binary to ASCII * conversion * * ENTRY pn - pointer to number to convert * * EXIT returns next (LS) character, updates original number */ PRIVATE char btoa(ULONG_PTR *pn) { char rch; rch = (char)(*pn&1)+'0'; *pn >>= 1; return rch; } /*** otoa - return next (least significant) char in an octal to ASCII * conversion * * ENTRY pn - pointer to number to convert * * EXIT returns next (LS) character, updates original number */ PRIVATE char otoa(ULONG_PTR *pn) { char rch; rch = (char)'0'+(char)(*pn&7); *pn >>= 3; return rch; } /*** utoa - return next (least significant) char in an unsigned int to * ASCII conversion * * ENTRY pn - pointer to number to convert * * EXIT returns next (LS) character, updates original number */ PRIVATE char utoa(ULONG_PTR *pn) { char rch; rch = (char)'0'+(char)(*pn%10); *pn /= 10; return rch; } /*** xtoa - return next (least significant) char in a hexadecimal to * ASCII conversion. Returns lower case hex characters * * ENTRY pn - pointer to number to convert * * EXIT returns next (LS) character, updates original number */ PRIVATE char xtoa(ULONG_PTR *pn) { ULONG_PTR n = *pn & 0x000f; char rch = (n <= 9) ? (char)n+'0' : (char)n+'0'+('a'-'9'-1); *pn >>= 4; return rch; } /*** Xasc - return next (least significant) char in a hexadecimal to * ASCII conversion. Returns upper case hex characters * * ENTRY pn - pointer to number to convert * * EXIT returns next (LS) character, updates original number */ PRIVATE char Xasc(ULONG_PTR *pn) { ULONG_PTR n = *pn & 0x000f; char rch = (n <= 9) ? (char)n+'0' : (char)n+'0'+('A'-'9'-1); *pn >>= 4; return rch; }