home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
crt
/
src
/
output.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-17
|
87KB
|
2,568 lines
/***
*output.c - printf style output to a FILE
*
* Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* This file contains the code that does all the work for the
* printf family of functions. It should not be called directly, only
* by the *printf functions. We don't make any assumtions about the
* sizes of ints, longs, shorts, or long doubles, but if types do overlap,
* we also try to be efficient. We do assume that pointers are the same
* size as either ints or longs.
* If CPRFLAG is defined, defines _cprintf instead.
* **** DOESN'T CURRENTLY DO MTHREAD LOCKING ****
*
*******************************************************************************/
#ifdef _WIN32
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
#define DOUBLE double
#endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
/* temporary work-around for compiler without 64-bit support */
#ifndef _INTEGRAL_MAX_BITS
#define _INTEGRAL_MAX_BITS 64
#endif /* _INTEGRAL_MAX_BITS */
#include <cruntime.h>
#include <limits.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
#include <cvt.h>
#include <conio.h>
#include <internal.h>
#include <fltintrn.h>
#include <stdlib.h>
#include <ctype.h>
#include <dbgint.h>
/* inline keyword is non-ANSI C7 extension */
#if !defined (_MSC_VER) || defined (__STDC__)
#define __inline static
#else /* !defined (_MSC_VER) || defined (__STDC__) */
/* UNDONE: compiler is broken */
#define __inline static
#endif /* !defined (_MSC_VER) || defined (__STDC__) */
#ifdef _MBCS
#undef _MBCS
#endif /* _MBCS */
#include <tchar.h>
/* this macro defines a function which is private and as fast as possible: */
/* for example, in C 6.0, it might be static _fastcall <type> near. */
#define LOCAL(x) static x __cdecl
/* int/long/short/pointer sizes */
/* the following should be set depending on the sizes of various types */
#define LONG_IS_INT 1 /* 1 means long is same size as int */
#define SHORT_IS_INT 0 /* 1 means short is same size as int */
#define LONGDOUBLE_IS_DOUBLE 1 /* 1 means long double is same as double */
#define PTR_IS_INT 1 /* 1 means ptr is same size as int */
#define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
#if LONG_IS_INT
#define get_long_arg(x) (long)get_int_arg(x)
#endif /* LONG_IS_INT */
#ifndef _UNICODE
#if SHORT_IS_INT
#define get_short_arg(x) (short)get_int_arg(x)
#endif /* SHORT_IS_INT */
#endif /* _UNICODE */
#if PTR_IS_INT
#define get_ptr_arg(x) (void *)get_int_arg(x)
#elif PTR_IS_LONG
#define get_ptr_arg(x) (void *)get_long_arg(x)
#else /* PTR_IS_LONG */
#error Size of pointer must be same as size of int or long
#endif /* PTR_IS_LONG */
/* CONSTANTS */
/* size of conversion buffer (ANSI-specified minimum is 509) */
#define BUFFERSIZE 512
#if BUFFERSIZE < CVTBUFSIZE
#error Conversion buffer too small for max double.
#endif /* BUFFERSIZE < CVTBUFSIZE */
/* flag definitions */
#define FL_SIGN 0x00001 /* put plus or minus in front */
#define FL_SIGNSP 0x00002 /* put space or minus in front */
#define FL_LEFT 0x00004 /* left justify */
#define FL_LEADZERO 0x00008 /* pad with leading zeros */
#define FL_LONG 0x00010 /* long value given */
#define FL_SHORT 0x00020 /* short value given */
#define FL_SIGNED 0x00040 /* signed data given */
#define FL_ALTERNATE 0x00080 /* alternate form requested */
#define FL_NEGATIVE 0x00100 /* value is negative */
#define FL_FORCEOCTAL 0x00200 /* force leading '0' for octals */
#define FL_LONGDOUBLE 0x00400 /* long double value given */
#define FL_WIDECHAR 0x00800 /* wide characters */
#define FL_I64 0x08000 /* __int64 value given */
/* state definitions */
enum STATE {
ST_NORMAL, /* normal state; outputting literal chars */
ST_PERCENT, /* just read '%' */
ST_FLAG, /* just read flag character */
ST_WIDTH, /* just read width specifier */
ST_DOT, /* just read '.' */
ST_PRECIS, /* just read precision specifier */
ST_SIZE, /* just read size specifier */
ST_TYPE /* just read type specifier */
};
#define NUMSTATES (ST_TYPE + 1)
/* character type values */
enum CHARTYPE {
CH_OTHER, /* character with no special meaning */
CH_PERCENT, /* '%' */
CH_DOT, /* '.' */
CH_STAR, /* '*' */
CH_ZERO, /* '0' */
CH_DIGIT, /* '1'..'9' */
CH_FLAG, /* ' ', '+', '-', '#' */
CH_SIZE, /* 'h', 'l', 'L', 'N', 'F', 'w' */
CH_TYPE /* type specifying character */
};
/* static data (read only, since we are re-entrant) */
#if defined (_UNICODE) || defined (CPRFLAG)
extern char *__nullstring; /* string to print on null ptr */
extern wchar_t *__wnullstring; /* string to print on null ptr */
#else /* defined (_UNICODE) || defined (CPRFLAG) */
char *__nullstring = "(null)"; /* string to print on null ptr */
wchar_t *__wnullstring = L"(null)";/* string to print on null ptr */
#endif /* defined (_UNICODE) || defined (CPRFLAG) */
/* The state table. This table is actually two tables combined into one. */
/* The lower nybble of each byte gives the character class of any */
/* character; while the uper nybble of the byte gives the next state */
/* to enter. See the macros below the table for details. */
/* */
/* The table is generated by maketabc.c -- use this program to make */
/* changes. */
#if defined (_UNICODE) || defined (CPRFLAG)
extern const char __lookuptable[];
#else /* defined (_UNICODE) || defined (CPRFLAG) */
/* Table generated by maketabc.c built with -D_WIN32_. Defines additional */
/* format code %Z for counted string. */
const char __lookuptable[] = {
0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00,
0x10, 0x00, 0x03, 0x06, 0x00, 0x06, 0x02, 0x10,
0x04, 0x45, 0x45, 0x45, 0x05, 0x05, 0x05, 0x05,
0x05, 0x35, 0x30, 0x00, 0x50, 0x00, 0x00, 0x00,
0x00, 0x20, 0x28, 0x38, 0x50, 0x58, 0x07, 0x08,
0x00, 0x37, 0x30, 0x30, 0x57, 0x50, 0x07, 0x00,
0x00, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00,
0x08, 0x60,
0x68, /* 'Z' (extension for NT development) */
0x60, 0x60, 0x60, 0x60, 0x00,
0x00, 0x70, 0x70, 0x78, 0x78, 0x78, 0x78, 0x08,
0x07, 0x08, 0x00, 0x00, 0x07, 0x00, 0x08, 0x08,
0x08, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
0x07, /* 'w' (extension for NT development) */
0x08
};
#endif /* defined (_UNICODE) || defined (CPRFLAG) */
#define find_char_class(c) \
((c) < _T(' ') || (c) > _T('x') ? \
CH_OTHER \
: \
__lookuptable[(c)-_T(' ')] & 0xF)
#define find_next_state(class, state) \
(__lookuptable[(class) * NUMSTATES + (state)] >> 4)
/*
* Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
*/
/* prototypes */
#ifdef CPRFLAG
#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, pnw)
LOCAL(void) write_char(int ch, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, int *numwritten);
#elif defined (_UNICODE)
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
LOCAL(void) write_char(wchar_t ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(wchar_t *string, int len, FILE *f, int *numwritten);
#else /* defined (_UNICODE) */
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, stream, pnw)
LOCAL(void) write_char(int ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, FILE *f, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, FILE *f, int *numwritten);
#endif /* defined (_UNICODE) */
__inline int __cdecl get_int_arg(va_list *pargptr);
#ifndef _UNICODE
#if !SHORT_IS_INT
__inline short __cdecl get_short_arg(va_list *pargptr);
#endif /* !SHORT_IS_INT */
#endif /* _UNICODE */
#if !LONG_IS_INT
__inline long __cdecl get_long_arg(va_list *pargptr);
#endif /* !LONG_IS_INT */
#if _INTEGRAL_MAX_BITS >= 64
__inline __int64 __cdecl get_int64_arg(va_list *pargptr);
#endif /* _INTEGRAL_MAX_BITS >= 64 */
#ifdef CPRFLAG
LOCAL(int) output(const char *, va_list);
/***
*int _cprintf(format, arglist) - write formatted output directly to console
*
*Purpose:
* Writes formatted data like printf, but uses console I/O functions.
*
*Entry:
* char *format - format string to determine data formats
* arglist - list of POINTERS to where to put data
*
*Exit:
* returns number of characters written
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _cprintf (
const char * format,
...
)
{
va_list arglist;
va_start(arglist, format);
return output(format, arglist);
}
#endif /* CPRFLAG */
/***
*int _output(stream, format, argptr), static int output(format, argptr)
*
*Purpose:
* Output performs printf style output onto a stream. It is called by
* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
* work. In multi-thread situations, _output assumes that the given
* stream is already locked.
*
* Algorithm:
* The format string is parsed by using a finite state automaton
* based on the current state and the current character read from
* the format string. Thus, looping is on a per-character basis,
* not a per conversion specifier basis. Once the format specififying
* character is read, output is performed.
*
*Entry:
* FILE *stream - stream for output
* char *format - printf style format string
* va_list argptr - pointer to list of subsidiary arguments
*
*Exit:
* Returns the number of characters written, or -1 if an output error
* occurs.
*ifdef _UNICODE
* The wide-character flavour returns the number of wide-characters written.
*endif
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(int) output (
#elif defined (_UNICODE)
int __cdecl _woutput (
FILE *stream,
#else /* defined (_UNICODE) */
int __cdecl _output (
FILE *stream,
#endif /* defined (_UNICODE) */
const TCHAR *format,
va_list argptr
)
{
int hexadd; /* offset to add to number to get 'a'..'f' */
TCHAR ch; /* character just read */
int flags; /* flag word -- see #defines above for flag values */
enum STATE state; /* current state */
enum CHARTYPE chclass; /* class of current character */
int radix; /* current conversion radix */
int charsout; /* characters currently written so far, -1 = IO error */
int fldwidth; /* selected field width -- 0 means default */
int precision; /* selected precision -- -1 means default */
TCHAR prefix[2]; /* numeric prefix -- up to two characters */
int prefixlen; /* length of prefix -- 0 means no prefix */
int capexp; /* non-zero = 'E' exponent signifient, zero = 'e' */
int no_output; /* non-zero = prodcue no output for this specifier */
union {
char *sz; /* pointer text to be printed, not zero terminated */
wchar_t *wz;
} text;
int textlen; /* length of the text in bytes/wchars to be printed.
textlen is in multibyte or wide chars if _UNICODE */
union {
char sz[BUFFERSIZE];
#ifdef _UNICODE
wchar_t wz[BUFFERSIZE];
#endif /* _UNICODE */
} buffer;
wchar_t wchar; /* temp wchar_t */
int bufferiswide; /* non-zero = buffer contains wide chars already */
textlen = 0; /* no text yet */
charsout = 0; /* no characters written yet */
state = ST_NORMAL; /* starting state */
/* main loop -- loop while format character exist and no I/O errors */
while ((ch = *format++) != _T('\0') && charsout >= 0) {
chclass = find_char_class(ch); /* find character class */
state = find_next_state(chclass, state); /* find next state */
/* execute code for each state */
switch (state) {
case ST_NORMAL:
NORMAL_STATE:
/* normal state -- just write character */
#ifdef _UNICODE
bufferiswide = 1;
#else /* _UNICODE */
bufferiswide = 0;
if (isleadbyte((int)(unsigned char)ch)) {
WRITE_CHAR(ch, &charsout);
ch = *format++;
_ASSERTE (ch != _T('\0')); /* UNDONE: don't fall off format string */
}
#endif /* _UNICODE */
WRITE_CHAR(ch, &charsout);
break;
case ST_PERCENT:
/* set default value of conversion parameters */
prefixlen = fldwidth = no_output = capexp = 0;
flags = 0;
precision = -1;
bufferiswide = 0; /* default */
break;
case ST_FLAG:
/* set flag based on which flag character */
switch (ch) {
case _T('-'):
flags |= FL_LEFT; /* '-' => left justify */
break;
case _T('+'):
flags |= FL_SIGN; /* '+' => force sign indicator */
break;
case _T(' '):
flags |= FL_SIGNSP; /* ' ' => force sign or space */
break;
case _T('#'):
flags |= FL_ALTERNATE; /* '#' => alternate form */
break;
case _T('0'):
flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
break;
}
break;
case ST_WIDTH:
/* update width value */
if (ch == _T('*')) {
/* get width from arg list */
fldwidth = get_int_arg(&argptr);
if (fldwidth < 0) {
/* ANSI says neg fld width means '-' flag and pos width */
flags |= FL_LEFT;
fldwidth = -fldwidth;
}
}
else {
/* add digit to current field width */
fldwidth = fldwidth * 10 + (ch - _T('0'));
}
break;
case ST_DOT:
/* zero the precision, since dot with no number means 0
not default, according to ANSI */
precision = 0;
break;
case ST_PRECIS:
/* update precison value */
if (ch == _T('*')) {
/* get precision from arg list */
precision = get_int_arg(&argptr);
if (precision < 0)
precision = -1; /* neg precision means default */
}
else {
/* add digit to current precision */
precision = precision * 10 + (ch - _T('0'));
}
break;
case ST_SIZE:
/* just read a size specifier, set the flags based on it */
switch (ch) {
#if !LONG_IS_INT || !defined (_UNICODE)
case _T('l'):
flags |= FL_LONG; /* 'l' => long int or wchar_t */
break;
#endif /* !LONG_IS_INT || !defined (_UNICODE) */
#if !LONGDOUBLE_IS_DOUBLE || defined (_M_ALPHA)
/*
* Alpha has native 64-bit integer registers and operations.
* The int and long types are 32 bits and an Alpha specific
* __int64 type is 64 bits. We also use the 'L' flag for
* integer arguments to indicate 64-bit conversions (%Lx).
*/
case _T('L'):
flags |= FL_I64; /* 'L' => __int64 */
break;
#endif /* !LONGDOUBLE_IS_DOUBLE || defined (_M_ALPHA) */
case _T('I'):
/*
* In order to handle the I64 size modifier, we depart from
* the simple deterministic state machine. The code below
* scans
*/
if ( (*format == _T('6')) && (*(format + 1) == _T('4')) ) {
format += 2;
flags |= FL_I64; /* I64 => __int64 */
}
else {
state = ST_NORMAL;
goto NORMAL_STATE;
}
break;
#if !SHORT_IS_INT || defined (_UNICODE)
case _T('h'):
flags |= FL_SHORT; /* 'h' => short int or char */
break;
#endif /* !SHORT_IS_INT || defined (_UNICODE) */
/* UNDONE: support %wc and %ws for now only for compatibility */
case _T('w'):
flags |= FL_WIDECHAR; /* 'w' => wide character */
break;
}
break;
case ST_TYPE:
/* we have finally read the actual type character, so we */
/* now format and "print" the output. We use a big switch */
/* statement that sets 'text' to point to the text that should */
/* be printed, and 'textlen' to the length of this text. */
/* Common code later on takes care of justifying it and */
/* other miscellaneous chores. Note that cases share code, */
/* in particular, all integer formatting is done in one place. */
/* Look at those funky goto statements! */
switch (ch) {
case _T('C'): /* ISO wide character */
if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
#ifdef _UNICODE
flags |= FL_SHORT;
#else /* _UNICODE */
flags |= FL_WIDECHAR; /* ISO std. */
#endif /* _UNICODE */
/* fall into 'c' case */
case _T('c'): {
/* print a single character specified by int argument */
#ifdef _UNICODE
bufferiswide = 1;
wchar = (wchar_t) get_int_arg(&argptr);
if (flags & FL_SHORT) {
/* format multibyte character */
/* this is an extension of ANSI */
char tempchar[2];
{
tempchar[0] = (char)(wchar & 0x00ff);
tempchar[1] = '\0';
}
if (mbtowc(buffer.wz,tempchar,MB_CUR_MAX) < 0) {
/* ignore if conversion was unsuccessful */
no_output = 1;
}
} else {
buffer.wz[0] = wchar;
}
text.wz = buffer.wz;
textlen = 1; /* print just a single character */
#else /* _UNICODE */
if (flags & (FL_LONG|FL_WIDECHAR)) {
wchar = (wchar_t) get_short_arg(&argptr);
/* convert to multibyte character */
textlen = wctomb(buffer.sz, wchar);
/* check that conversion was successful */
if (textlen < 0)
no_output = 1;
} else {
/* format multibyte character */
/* this is an extension of ANSI */
unsigned short temp;
temp = (unsigned short) get_int_arg(&argptr);
{
buffer.sz[0] = (char) temp;
textlen = 1;
}
}
text.sz = buffer.sz;
#endif /* _UNICODE */
}
break;
case _T('Z'): {
/* print a Counted String
int i;
char *p; /* temps */
struct _count_string {
short Length;
short MaximumLength;
char *Buffer;
} *pstr;
pstr = get_ptr_arg(&argptr);
if (pstr == NULL || pstr->Buffer == NULL) {
/* null ptr passed, use special string */
text.sz = __nullstring;
textlen = strlen(text.sz);
} else {
if (flags & FL_WIDECHAR) {
text.wz = (wchar_t *)pstr->Buffer;
textlen = pstr->Length / sizeof(wchar_t);
bufferiswide = 1;
} else {
bufferiswide = 0;
text.sz = pstr->Buffer;
textlen = pstr->Length;
}
}
}
break;
case _T('S'): /* ISO wide character string */
#ifndef _UNICODE
if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
flags |= FL_WIDECHAR;
#else /* _UNICODE */
if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
flags |= FL_SHORT;
#endif /* _UNICODE */
case _T('s'): {
/* print a string -- */
/* ANSI rules on how much of string to print: */
/* all if precision is default, */
/* min(precision, length) if precision given. */
/* prints '(null)' if a null string is passed */
int i;
char *p; /* temps */
wchar_t *pwch;
/* At this point it is tempting to use strlen(), but */
/* if a precision is specified, we're not allowed to */
/* scan past there, because there might be no null */
/* at all. Thus, we must do our own scan. */
i = (precision == -1) ? INT_MAX : precision;
text.sz = get_ptr_arg(&argptr);
/* UNDONE: handle '#' case properly */
/* scan for null upto i characters */
#ifdef _UNICODE
if (flags & FL_SHORT) {
if (text.sz == NULL) /* NULL passed, use special string */
text.sz = __nullstring;
p = text.sz;
for (textlen=0; textlen<i && *p; textlen++) {
if (isleadbyte((int)*p))
++p;
++p;
}
/* textlen now contains length in multibyte chars */
} else {
if (text.wz == NULL) /* NULL passed, use special string */
text.wz = __wnullstring;
bufferiswide = 1;
pwch = text.wz;
while (i-- && *pwch)
++pwch;
textlen = pwch - text.wz; /* in wchar_ts */
/* textlen now contains length in wide chars */
}
#else /* _UNICODE */
if (flags & (FL_LONG|FL_WIDECHAR)) {
if (text.wz == NULL) /* NULL passed, use special string */
text.wz = __wnullstring;
bufferiswide = 1;
pwch = text.wz;
while ( i-- && *pwch )
++pwch;
textlen = pwch - text.wz;
/* textlen now contains length in wide chars */
} else {
if (text.sz == NULL) /* NULL passed, use special string */
text.sz = __nullstring;
p = text.sz;
while (i-- && *p)
++p;
textlen = p - text.sz; /* length of the string */
}
#endif /* _UNICODE */
}
break;
case _T('n'): {
/* write count of characters seen so far into */
/* short/int/long thru ptr read from args */
void *p; /* temp */
p = get_ptr_arg(&argptr);
/* store chars out into short/long/int depending on flags */
#if !LONG_IS_INT
if (flags & FL_LONG)
*(long *)p = charsout;
else
#endif /* !LONG_IS_INT */
#if !SHORT_IS_INT
if (flags & FL_SHORT)
*(short *)p = (short) charsout;
else
#endif /* !SHORT_IS_INT */
*(int *)p = charsout;
no_output = 1; /* force no output */
}
break;
case _T('E'):
case _T('G'):
capexp = 1; /* capitalize exponent */
ch += _T('a') - _T('A'); /* convert format char to lower */
/* DROP THROUGH */
case _T('e'):
case _T('f'):
case _T('g'): {
/* floating point conversion -- we call cfltcvt routines */
/* to do the work for us. */
flags |= FL_SIGNED; /* floating point is signed conversion */
text.sz = buffer.sz; /* put result in buffer */
/* compute the precision value */
if (precision < 0)
precision = 6; /* default precision: 6 */
else if (precision == 0 && ch == _T('g'))
precision = 1; /* ANSI specified */
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags & FL_LONGDOUBLE) {
LONGDOUBLE tmp;
tmp=va_arg(argptr, LONGDOUBLE);
/* Note: assumes ch is in ASCII range */
_cldcvt(&tmp, text.sz, (char)ch, precision, capexp);
} else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
DOUBLE tmp;
tmp=va_arg(argptr, DOUBLE);
/* Note: assumes ch is in ASCII range */
_cfltcvt(&tmp,text.sz, (char)ch, precision, capexp);
}
/* '#' and precision == 0 means force a decimal point */
if ((flags & FL_ALTERNATE) && precision == 0)
_forcdecpt(text.sz);
/* 'g' format means crop zero unless '#' given */
if (ch == _T('g') && !(flags & FL_ALTERNATE))
_cropzeros(text.sz);
/* check if result was negative, save '-' for later */
/* and point to positive part (this is for '0' padding) */
if (*text.sz == '-') {
flags |= FL_NEGATIVE;
++text.sz;
}
textlen = strlen(text.sz); /* compute length of text */
}
break;
case _T('d'):
case _T('i'):
/* signed decimal output */
flags |= FL_SIGNED;
radix = 10;
goto COMMON_INT;
case _T('u'):
radix = 10;
goto COMMON_INT;
case _T('p'):
/* write a pointer -- this is like an integer or long */
/* except we force precision to pad with zeros and */
/* output in big hex. */
precision = 2 * sizeof(void *); /* number of hex digits needed */
#if !PTR_IS_INT
flags |= FL_LONG; /* assume we're converting a long */
#endif /* !PTR_IS_INT */
/* DROP THROUGH to hex formatting */
case _T('X'):
/* unsigned upper hex output */
hexadd = _T('A') - _T('9') - 1; /* set hexadd for uppercase hex */
goto COMMON_HEX;
case _T('x'):
/* unsigned lower hex output */
hexadd = _T('a') - _T('9') - 1; /* set hexadd for lowercase hex */
/* DROP THROUGH TO COMMON_HEX */
COMMON_HEX:
radix = 16;
if (flags & FL_ALTERNATE) {
/* alternate form means '0x' prefix */
prefix[0] = _T('0');
prefix[1] = (TCHAR)(_T('x') - _T('a') + _T('9') + 1 + hexadd); /* 'x' or 'X' */
prefixlen = 2;
}
goto COMMON_INT;
case _T('o'):
/* unsigned octal output */
radix = 8;
if (flags & FL_ALTERNATE) {
/* alternate form means force a leading 0 */
flags |= FL_FORCEOCTAL;
}
/* DROP THROUGH to COMMON_INT */
COMMON_INT: {
/* This is the general integer formatting routine. */
/* Basically, we get an argument, make it positive */
/* if necessary, and convert it according to the */
/* correct radix, setting text and textlen */
/* appropriately. */
#if _INTEGRAL_MAX_BITS >= 64
unsigned __int64 number; /* number to convert */
int digit; /* ascii value of digit */
__int64 l; /* temp long value */
#else /* _INTEGRAL_MAX_BITS >= 64 */
unsigned long number; /* number to convert */
int digit; /* ascii value of digit */
long l; /* temp long value */
#endif /* _INTEGRAL_MAX_BITS >= 64 */
/* 1. read argument into l, sign extend as needed */
#if _INTEGRAL_MAX_BITS >= 64
if (flags & FL_I64)
l = get_int64_arg(&argptr);
else
#endif /* _INTEGRAL_MAX_BITS >= 64 */
#if !LONG_IS_INT
if (flags & FL_LONG)
l = get_long_arg(&argptr);
else
#endif /* !LONG_IS_INT */
#if !SHORT_IS_INT
if (flags & FL_SHORT) {
if (flags & FL_SIGNED)
l = (short) get_int_arg(&argptr); /* sign extend */
else
l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
} else
#endif /* !SHORT_IS_INT */
{
if (flags & FL_SIGNED)
l = get_int_arg(&argptr); /* sign extend */
else
l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
}
/* 2. check for negative; copy into number */
if ( (flags & FL_SIGNED) && l < 0) {
number = -l;
flags |= FL_NEGATIVE; /* remember negative sign */
} else {
number = l;
}
#if _INTEGRAL_MAX_BITS >= 64
if ( (flags & FL_I64) == 0 ) {
/*
* Unless printing a full 64-bit value, insure values
* here are not in cananical longword format to prevent
* the sign extended upper 32-bits from being printed.
*/
number &= 0xffffffff;
}
#endif /* _INTEGRAL_MAX_BITS >= 64 */
/* 3. check precision value for default; non-default */
/* turns off 0 flag, according to ANSI. */
if (precision < 0)
precision = 1; /* default precision */
else
flags &= ~FL_LEADZERO;
/* 4. Check if data is 0; if so, turn off hex prefix */
if (number == 0)
prefixlen = 0;
/* 5. Convert data to ASCII -- note if precision is zero */
/* and number is zero, we get no digits at all. */
text.sz = &buffer.sz[BUFFERSIZE-1]; /* last digit at end of buffer */
while (precision-- > 0 || number != 0) {
digit = (int)(number % radix) + '0';
number /= radix; /* reduce number */
if (digit > '9') {
/* a hex digit, make it a letter */
digit += hexadd;
}
*text.sz-- = (char)digit; /* store the digit */
}
textlen = (char *)&buffer.sz[BUFFERSIZE-1] - text.sz; /* compute length of number */
++text.sz; /* text points to first digit now */
/* 6. Force a leading zero if FORCEOCTAL flag set */
if ((flags & FL_FORCEOCTAL) && (text.sz[0] != '0' || textlen == 0)) {
*--text.sz = '0';
++textlen; /* add a zero */
}
}
break;
}
/* At this point, we have done the specific conversion, and */
/* 'text' points to text to print; 'textlen' is length. Now we */
/* justify it, put on prefixes, leading zeros, and then */
/* print it. */
if (!no_output) {
int padding; /* amount of padding, negative means zero */
if (flags & FL_SIGNED) {
if (flags & FL_NEGATIVE) {
/* prefix is a '-' */
prefix[0] = _T('-');
prefixlen = 1;
}
else if (flags & FL_SIGN) {
/* prefix is '+' */
prefix[0] = _T('+');
prefixlen = 1;
}
else if (flags & FL_SIGNSP) {
/* prefix is ' ' */
prefix[0] = _T(' ');
prefixlen = 1;
}
}
/* calculate amount of padding -- might be negative, */
/* but this will just mean zero */
padding = fldwidth - textlen - prefixlen;
/* put out the padding, prefix, and text, in the correct order */
if (!(flags & (FL_LEFT | FL_LEADZERO))) {
/* pad on left with blanks */
WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
}
/* write prefix */
WRITE_STRING(prefix, prefixlen, &charsout);
if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
/* write leading zeros */
WRITE_MULTI_CHAR(_T('0'), padding, &charsout);
}
/* write text */
#ifndef _UNICODE
if (bufferiswide && (textlen > 0)) {
wchar_t *p;
int retval, count;
char buffer[MB_LEN_MAX+1];
p = text.wz;
count = textlen;
while (count--) {
retval = wctomb(buffer, *p++);
if (retval <= 0)
break;
WRITE_STRING(buffer, retval, &charsout);
}
} else {
WRITE_STRING(text.sz, textlen, &charsout);
}
#else /* _UNICODE */
if (!bufferiswide && textlen > 0) {
char *p;
int retval, count;
p = text.sz;
count = textlen;
while (count-- > 0) {
retval = mbtowc(&wchar, p, MB_CUR_MAX);
if (retval <= 0)
break;
WRITE_CHAR(wchar, &charsout);
p += retval;
}
} else {
WRITE_STRING(text.wz, textlen, &charsout);
}
#endif /* _UNICODE */
if (flags & FL_LEFT) {
/* pad on right with blanks */
WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
}
/* we're done! */
}
break;
}
}
return charsout; /* return value = number of characters written */
}
/*
* Future Optimizations for swprintf:
* - Don't free the memory used for converting the buffer to wide chars.
* Use realloc if the memory is not sufficient. Free it at the end.
*/
/***
*void write_char(int ch, int *pnumwritten)
*ifdef _UNICODE
*void write_char(wchar_t ch, FILE *f, int *pnumwritten)
*endif
*void write_char(int ch, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes a single character to the given file/console. If no error occurs,
* then *pnumwritten is incremented; otherwise, *pnumwritten is set
* to -1.
*
*Entry:
* int ch - character to write
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_char (
int ch,
int *pnumwritten
)
{
if (_putch_lk(ch) == EOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#elif defined (_UNICODE)
LOCAL(void) write_char (
wchar_t ch,
FILE *f,
int *pnumwritten
)
{
if (_putwc_lk(ch, f) == WEOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#else /* defined (_UNICODE) */
LOCAL(void) write_char (
int ch,
FILE *f,
int *pnumwritten
)
{
if (_putc_lk(ch, f) == EOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#endif /* defined (_UNICODE) */
/***
*void write_multi_char(int ch, int num, int *pnumwritten)
*ifdef _UNICODE
*void write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten)
*endif
*void write_multi_char(int ch, int num, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes num copies of a character to the given file/console. If no error occurs,
* then *pnumwritten is incremented by num; otherwise, *pnumwritten is set
* to -1. If num is negative, it is treated as zero.
*
*Entry:
* int ch - character to write
* int num - number of times to write the characters
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_multi_char (
int ch,
int num,
int *pnumwritten
)
{
while (num-- > 0) {
write_char(ch, pnumwritten);
if (*pnumwritten == -1)
break;
}
}
#else /* CPRFLAG */
#ifdef _UNICODE
LOCAL(void) write_multi_char (
wchar_t ch,
int num,
FILE *f,
int *pnumwritten
)
#else /* _UNICODE */
LOCAL(void) write_multi_char (
int ch,
int num,
FILE *f,
int *pnumwritten
)
#endif /* _UNICODE */
{
while (num-- > 0) {
write_char(ch, f, pnumwritten);
if (*pnumwritten == -1)
break;
}
}
#endif /* CPRFLAG */
/***
*void write_string(char *string, int len, int *pnumwritten)
*void write_string(char *string, int len, FILE *f, int *pnumwritten)
*ifdef _UNICODE
*void write_string(wchar_t *string, int len, FILE *f, int *pnumwritten)
*endif
*void write_wstring(wchar_t *string, int len, int *pnumwritten)
*void write_wstring(wchar_t *string, int len, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes a string of the given length to the given file. If no error occurs,
* then *pnumwritten is incremented by len; otherwise, *pnumwritten is set
* to -1. If len is negative, it is treated as zero.
*
*Entry:
* char *string - string to write (NOT null-terminated)
* int len - length of string
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_string (
char *string,
int len,
int *pnumwritten
)
{
while (len-- > 0) {
write_char(*string++, pnumwritten);
if (*pnumwritten == -1)
break;
}
}
#else /* CPRFLAG */
#if _UNICODE
LOCAL(void) write_string (
wchar_t *string,
int len,
FILE *f,
int *pnumwritten
)
#else /* _UNICODE */
LOCAL(void) write_string (
char *string,
int len,
FILE *f,
int *pnumwritten
)
#endif /* _UNICODE */
{
while (len-- > 0) {
write_char(*string++, f, pnumwritten);
if (*pnumwritten == -1)
break;
}
}
#endif /* CPRFLAG */
/***
*int get_int_arg(va_list *pargptr)
*
*Purpose:
* Gets an int argument off the given argument list and updates *pargptr.
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the integer argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
__inline int __cdecl get_int_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, int);
}
/***
*long get_long_arg(va_list *pargptr)
*
*Purpose:
* Gets an long argument off the given argument list and updates *pargptr.
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the long argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
#if !LONG_IS_INT
__inline long __cdecl get_long_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, long);
}
#endif /* !LONG_IS_INT */
#if _INTEGRAL_MAX_BITS >= 64
__inline __int64 __cdecl get_int64_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, __int64);
}
#endif /* _INTEGRAL_MAX_BITS >= 64 */
#ifndef _UNICODE
/***
*short get_short_arg(va_list *pargptr)
*
*Purpose:
* Gets a short argument off the given argument list and updates *pargptr.
* *** CURRENTLY ONLY USED TO GET A WCHAR_T, IFDEF _INTL ***
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the short argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
#if !SHORT_IS_INT
__inline short __cdecl get_short_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, short);
}
#endif /* !SHORT_IS_INT */
#endif /* _UNICODE */
#else /* _WIN32 */
#if defined (_M_MPPC) || defined (_M_M68K)
#include <cruntime.h>
#include <limits.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
#include <cvt.h>
#include <conio.h>
#include <internal.h>
#include <fltintrn.h>
#include <tchar.h>
#include <stdlib.h>
#include <ctype.h>
#include <dbgint.h>
/*
* Code under if defined(_WIN32_) && !defined(_DOSX32_) && !defined(_INTL)
* is partial international support written by NT developers. This code
* should be removed when international sources are merged with orville.
*/
/* this macro defines a function which is private and as fast as possible: */
/* for example, in C 6.0, it might be static _fastcall <type> near. */
#define LOCAL(x) static x __cdecl
/* int/long/short/pointer sizes */
/* the following should be set depending on the sizes of various types */
#define LONG_IS_INT 1 /* 1 means long is same size as int */
#define SHORT_IS_INT 0 /* 1 means short is same size as int */
#define PTR_IS_INT 1 /* 1 means ptr is same size as int */
#define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
#ifdef _M_MPPC
#define LONGDOUBLE_IS_DOUBLE 1 /* 1 means long double is same as double */
#else /* _M_MPPC */
#define LONGDOUBLE_IS_DOUBLE 0 /* 1 means long double is same as double */
#endif /* _M_MPPC */
#ifndef _INTEGRAL_MAX_BITS
#define _INTEGRAL_MAX_BITS 64
#endif /* _INTEGRAL_MAX_BITS */
#if LONG_IS_INT
#define get_long_arg(x) (long)get_int_arg(x)
#endif /* LONG_IS_INT */
#if PTR_IS_INT
#define get_ptr_arg(x) (void *)get_int_arg(x)
#elif PTR_IS_LONG
#define get_ptr_arg(x) (void *)get_long_arg(x)
#else /* PTR_IS_LONG */
#error Size of pointer must be same as size of int or long
#endif /* PTR_IS_LONG */
/* CONSTANTS */
/* size of conversion buffer (ANSI-specified minimum is 509) */
#define BUFFERSIZE 512
#if BUFFERSIZE < CVTBUFSIZE
#error Conversion buffer too small for max double.
#endif /* BUFFERSIZE < CVTBUFSIZE */
/* flag definitions */
#define FL_SIGN 0x0001 /* put plus or minus in front */
#define FL_SIGNSP 0x0002 /* put space or minus in front */
#define FL_LEFT 0x0004 /* left justify */
#define FL_LEADZERO 0x0008 /* pad with leading zeros */
#define FL_LONG 0x0010 /* long value given */
#define FL_SHORT 0x0020 /* short value given */
#define FL_SIGNED 0x0040 /* signed data given */
#define FL_ALTERNATE 0x0080 /* alternate form requested */
#define FL_NEGATIVE 0x0100 /* value is negative */
#define FL_FORCEOCTAL 0x0200 /* force leading '0' for octals */
#define FL_LONGDOUBLE 0x0400 /* long double value given */
#define FL_WIDECHAR 0x0800 /* wide characters */
#define FL_I64 0x08000 /* __int64 value given */
/* state definitions */
enum STATE {
ST_NORMAL, /* normal state; outputting literal chars */
ST_PERCENT, /* just read '%' */
ST_FLAG, /* just read flag character */
ST_WIDTH, /* just read width specifier */
ST_DOT, /* just read '.' */
ST_PRECIS, /* just read precision specifier */
ST_SIZE, /* just read size specifier */
ST_TYPE /* just read type specifier */
};
#define NUMSTATES (ST_TYPE + 1)
/* character type values */
enum CHARTYPE {
CH_OTHER, /* character with no special meaning */
CH_PERCENT, /* '%' */
CH_DOT, /* '.' */
CH_STAR, /* '*' */
CH_ZERO, /* '0' */
CH_DIGIT, /* '1'..'9' */
CH_FLAG, /* ' ', '+', '-', '#' */
CH_SIZE, /* 'h', 'l', 'L', 'N', 'F', 'w' */
CH_TYPE /* type specifying character */
};
/* static data (read only, since we are re-entrant) */
static char *nullstring = "(null)"; /* string to print on null ptr */
#ifdef _UNICODE
static wchar_t *wnullstring = L"(null)";/* string to print on null ptr */
#endif /* _UNICODE */
/* The state table. This table is actually two tables combined into one. */
/* The lower nybble of each byte gives the character class of any */
/* character; while the uper nybble of the byte gives the next state */
/* to enter. See the macros below the table for details. */
/* */
/* The table is generated by maketabc.c -- use this program to make */
/* changes. */
static char lookuptable[] = {
0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00,
0x10, 0x00, 0x03, 0x06, 0x00, 0x06, 0x02, 0x10,
0x04, 0x45, 0x45, 0x45, 0x05, 0x05, 0x05, 0x05,
0x05, 0x35, 0x30, 0x00, 0x50, 0x00, 0x00, 0x00,
0x00, 0x20, 0x28, 0x38, 0x50, 0x58, 0x07, 0x08,
0x00, 0x37, 0x30, 0x30, 0x57, 0x50, 0x07, 0x00,
0x00, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00,
0x08, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
0x00, 0x70, 0x70, 0x78, 0x78, 0x78, 0x78, 0x08,
0x07, 0x08, 0x00, 0x00, 0x07, 0x00, 0x08, 0x08,
0x08, 0x00, 0x00, 0x08, 0x08, 0x08, 0x00, 0x08,
0x08
};
#define find_char_class(c) \
((c) < ' ' || (c) > 'x' ? \
CH_OTHER \
: \
lookuptable[(c)-' '] & 0xF)
#define find_next_state(class, state) \
(lookuptable[(class) * NUMSTATES + (state)] >> 4)
/*
* Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
*/
/* prototypes */
#ifdef CPRFLAG
#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, pnw)
LOCAL(void) write_char(int ch, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, int *numwritten);
#elif defined (_UNICODE)
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
LOCAL(void) write_char(wchar_t ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(wchar_t *string, int len, FILE *f, int *numwritten);
#else /* defined (_UNICODE) */
#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
#define WRITE_WSTRING(s, len, pnw) write_wstring(s, len, stream, pnw)
LOCAL(void) write_char(int ch, FILE *f, int *pnumwritten);
LOCAL(void) write_multi_char(int ch, int num, FILE *f, int *pnumwritten);
LOCAL(void) write_string(char *string, int len, FILE *f, int *numwritten);
LOCAL(void) write_wstring(wchar_t *string, int len, FILE *f, int *numwritten);
#endif /* defined (_UNICODE) */
LOCAL(int) get_int_arg(va_list *pargptr);
LOCAL(short) get_short_arg(va_list *pargptr);
#if !LONG_IS_INT
LOCAL(long) get_long_arg(va_list *pargptr);
#endif /* !LONG_IS_INT */
LOCAL(__int64) get_int64_arg(va_list *pargptr);
#ifdef _UNICODE
LOCAL(int) __cdecl _woutput(FILE *, const char *, va_list);
#endif /* _UNICODE */
#ifdef CPRFLAG
LOCAL(int) output(const char *, va_list);
/***
*int _cprintf(format, arglist) - write formatted output directly to console
*
*Purpose:
* Writes formatted data like printf, but uses console I/O functions.
*
*Entry:
* char *format - format string to determine data formats
* arglist - list of POINTERS to where to put data
*
*Exit:
* returns number of characters written
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _cprintf (
const char * format,
...
)
{
va_list arglist;
va_start(arglist, format);
return output(format, arglist);
}
#endif /* CPRFLAG */
/***
*int _output(stream, format, argptr), static int output(format, argptr)
*
*Purpose:
* Output performs printf style output onto a stream. It is called by
* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
* work. In multi-thread situations, _output assumes that the given
* stream is already locked.
*
* Algorithm:
* The format string is parsed by using a finite state automaton
* based on the current state and the current character read from
* the format string. Thus, looping is on a per-character basis,
* not a per conversion specifier basis. Once the format specififying
* character is read, output is performed.
*
*Entry:
* FILE *stream - stream for output
* char *format - printf style format string
* va_list argptr - pointer to list of subsidiary arguments
*
*Exit:
* Returns the number of characters written, or -1 if an output error
* occurs.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(int) output (
#elif defined (_UNICODE)
LOCAL(int) _woutput (
FILE *stream,
#else /* defined (_UNICODE) */
int __cdecl _output (
FILE *stream,
#endif /* defined (_UNICODE) */
const char *format,
va_list argptr
)
{
int hexadd; /* offset to add to number to get 'a'..'f' */
char ch; /* character just read */
int flags; /* flag word -- see #defines above for flag values */
enum STATE state; /* current state */
enum CHARTYPE chclass; /* class of current character */
int radix; /* current conversion radix */
int charsout; /* characters currently written so far, -1 = IO error */
int fldwidth; /* selected field with -- 0 means default */
int precision; /* selected precision -- -1 means default */
char prefix[2]; /* numeric prefix -- up to two characters */
int prefixlen; /* length of prefix -- 0 means no prefix */
int capexp; /* non-zero = 'E' exponent signifiet, zero = 'e' */
int no_output; /* non-zero = prodcue no output for this specifier */
char *text; /* pointer text to be printed, not zero terminated */
int textlen; /* length of the text in bytes to be printed */
char buffer[BUFFERSIZE]; /* buffer for conversions */
#ifdef _UNICODE
/* textlen is in multibyte or wide characters for _UNICODE versions */
wchar_t wchar; /* temp wchar_t */
wchar_t *wchar_p; /* temp wchar_t pointer */
int bufferiswide; /* non-zero = buffer contains wide chars already */
#endif /* _UNICODE */
int _tflag=0;
charsout = 0; /* no characters written yet */
state = ST_NORMAL; /* starting state */
/* main loop -- loop while format character exist and no I/O errors */
while ((ch = *format++) != '\0' && charsout >= 0) {
chclass = find_char_class(ch); /* find character class */
state = find_next_state(chclass, state); /* find next state */
/* execute code for each state */
switch (state) {
NORMAL_STATE:
case ST_NORMAL:
/* normal state -- just write character */
#ifndef _UNICODE
if (isleadbyte((int)ch)) {
WRITE_CHAR(ch, &charsout);
ch = *format++;
_ASSERTE (ch != '0'); /* UNDONE: don't fall off format string */
}
WRITE_CHAR(ch, &charsout);
#else /* _UNICODE */
format += (mbtowc (&wchar, format-1, MB_CUR_MAX) - 1);
/* UNDONE: check for mbtowc failure */
WRITE_CHAR(wchar, &charsout);
#endif /* _UNICODE */
break;
case ST_PERCENT:
/* set default value of conversion parameters */
prefixlen = fldwidth = no_output = capexp = 0;
flags = 0;
precision = -1;
#ifdef _UNICODE
bufferiswide = 0;
#endif /* _UNICODE */
break;
case ST_FLAG:
/* set flag based on which flag character */
switch (ch) {
case '-':
flags |= FL_LEFT; /* '-' => left justify */
break;
case '+':
flags |= FL_SIGN; /* '+' => force sign indicator */
break;
case ' ':
flags |= FL_SIGNSP; /* ' ' => force sign or space */
break;
case '#':
flags |= FL_ALTERNATE; /* '#' => alternate form */
break;
case '0':
flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
break;
}
break;
case ST_WIDTH:
/* update width value */
if (ch == '*') {
/* get width from arg list */
fldwidth = get_int_arg(&argptr);
if (fldwidth < 0) {
/* ANSI says neg fld width means '-' flag and pos width */
flags |= FL_LEFT;
fldwidth = -fldwidth;
}
}
else {
/* add digit to current field width */
fldwidth = fldwidth * 10 + (ch - '0');
}
break;
case ST_DOT:
/* zero the precision, since dot with no number means 0
not default, according to ANSI */
precision = 0;
break;
case ST_PRECIS:
/* update precison value */
if (ch == '*') {
/* get precision from arg list */
precision = get_int_arg(&argptr);
if (precision < 0)
precision = -1; /* neg precision means default */
}
else {
/* add digit to current precision */
precision = precision * 10 + (ch - '0');
}
break;
case ST_SIZE:
/* just read a size specifier, set the flags based on it */
switch (ch) {
#if !LONG_IS_INT
case 'l':
flags |= FL_LONG; /* 'l' => long int */
break;
#endif /* !LONG_IS_INT */
#if !LONGDOUBLE_IS_DOUBLE
case 'L':
flags |= FL_LONGDOUBLE; /* 'L' => long double */
break;
#endif /* !LONGDOUBLE_IS_DOUBLE */
case 'I':
/*
* In order to handle the I64 size modifier, we depart from
* the simple deterministic state machine. The code below
* scans
*/
if ( (*format == '6') && (*(format + 1) == '4') ) {
format += 2;
flags |= FL_I64; /* I64 => __int64 */
}
else {
state = ST_NORMAL;
goto NORMAL_STATE;
}
break;
#if !SHORT_IS_INT
case 'h':
flags |= FL_SHORT; /* 'h' => short int */
break;
#endif /* !SHORT_IS_INT */
}
break;
case ST_TYPE:
/* we have finally read the actual type character, so we */
/* now format and "print" the output. We use a big switch */
/* statement that sets 'text' to point to the text that should */
/* be printed, and 'textlen' to the length of this text. */
/* Common code later on takes care of justifying it and */
/* other miscellaneous chores. Note that cases share code, */
/* in particular, all integer formatting is doen in one place. */
/* Look at those funky goto statements! */
/*
* Generic string handling support: %tc, %ts accept
* either chars or wide-chars depending on _tflag.
* _tflag == 1 means wide-chars.
*/
if (ch == 't') {
if (_tflag == 1)
ch = 'w';
else {
ch = *format++;
_ASSERTE (ch!='0'); /* UNDONE: don't fall off format string */
}
}
switch (ch) {
case 'c': {
/* print a single character specified by int argument */
buffer[0] = (char) get_int_arg(&argptr); /* get char to print */
text = buffer;
textlen = 1; /* print just a single character */
}
break;
case 's': {
/* print a string -- */
/* ANSI rules on how much of string to print: */
/* all if precision is default, */
/* min(precision, length) if precision given. */
/* prints '(null)' if a null string is passed */
int i;
char *p; /* temps */
text = get_ptr_arg(&argptr);
if (text == NULL) {
/* null ptr passed, use special string */
text = nullstring;
}
/* At this point it is tempting to use strlen(), but */
/* if a precision is specified, we're not allowed to */
/* scan past there, because there might be no null */
/* at all. Thus, we must do our own scan. */
i = (precision == -1) ? INT_MAX : precision;
p = text;
/* scan for null upto i characters */
#ifndef _UNICODE
while (i-- && *p)
++p;
textlen = p - text; /* length of the string */
#else /* _UNICODE */
for (textlen=0; textlen<i && *p; textlen++) {
if (isleadbyte((int)*p))
++p;
++p;
}
/* textlen now contains length in multibyte chars */
#endif /* _UNICODE */
}
break;
case 'w': {
/* 'wc' print a wide character */
/* 'ws' print a wide string */
#ifdef _UNICODE
bufferiswide = 1;
#endif /* _UNICODE */
ch = *format++;
_ASSERTE (ch!='0'); /* UNDONE: don't fall off format string */
switch (ch) {
/* 'wc' case */
case 'c': {
#ifndef _UNICODE
wchar_t temp;
temp = (wchar_t) get_short_arg(&argptr);
/* convert to multibyte characters */
textlen = wctomb(buffer, temp);
text = buffer;
/* check that conversion was successful */
if (textlen < 0)
no_output = 1;
#else /* _UNICODE */
/* copy wide-char directly to buffer */
*(wchar_t *)buffer = (wchar_t) get_short_arg(&argptr);
textlen = 1; /* number of wide-chars */
text = buffer;
#endif /* _UNICODE */
}
break;
/* 'ws' case */
case 's': {
#ifndef _UNICODE
int i;
text = get_ptr_arg(&argptr);
if (text == NULL) {
/*
* null ptr passed, use special string
*/
text = nullstring;
textlen = strlen(nullstring);
/*
* if precision is specified (!= -1) use
* minimum of precision and textlen, else use textlen
*/
textlen = (precision < 0) ? textlen :
((precision < textlen) ? precision : textlen);
} else {
/* convert to multibyte characters */
i = (precision < 0) ? BUFFERSIZE : precision;
textlen = (int) wcstombs(buffer, (wchar_t *)text, (size_t)i);
text = buffer;
if (flags & FL_ALTERNATE) {
/*
* Don't write more than precision wide chars
*/
char *p;
p = text;
while (i-- && *p) {
if (isleadbyte((int)*p))
++p;
++p;
}
textlen = p - text; /* length of the string */
}
/* check that conversion was successful */
if (textlen < 0)
no_output = 1;
}
#else /* _UNICODE */
int i;
wchar_t *p; /* temps */
text = get_ptr_arg(&argptr);
if (text == NULL) {
/* null ptr passed, use special string */
text = (char *) wnullstring;
}
i = (precision == -1) ? INT_MAX : precision;
p = (wchar_t *) text;
/* scan for null up to i wide characters */
while (i-- && *p)
++p;
textlen = p - (wchar_t *) text; /* length in wide-chars */
#endif /* _UNICODE */
}
break;
default:
/* error: unrecognized */
/* ANSI: undefined behavior */
no_output = 1;
} /* switch */
} /* case 'w' */
break;
case 'n': {
/* write count of characters seen so far into */
/* short/int/long thru ptr read from args */
void *p; /* temp */
p = get_ptr_arg(&argptr);
/* store chars out into short/long/int depending on flags */
#if !LONG_IS_INT
if (flags & FL_LONG)
*(long *)p = charsout;
else
#endif /* !LONG_IS_INT */
#if !SHORT_IS_INT
if (flags & FL_SHORT)
*(short *)p = (short) charsout;
else
#endif /* !SHORT_IS_INT */
*(int *)p = charsout;
no_output = 1; /* force no output */
}
break;
case 'E':
case 'G':
capexp = 1; /* capitalize exponent */
ch += 'a' - 'A'; /* convert format char to lower */
/* DROP THROUGH */
case 'e':
case 'f':
case 'g': {
/* floating point conversion -- we call cfltcvt routines */
/* to do the work for us. */
flags |= FL_SIGNED; /* floating point is signed conversion */
text = buffer; /* put result in buffer */
/* compute the precision value */
if (precision < 0)
precision = 6; /* default precision: 6 */
else if (precision == 0 && ch == 'g')
precision = 1; /* ANSI specified */
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags & FL_LONGDOUBLE) {
_cldcvt((LONGDOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, LONGDOUBLE);
}
else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
_cfltcvt((DOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, DOUBLE);
}
/* '#' and precision == 0 means force a decimal point */
if ((flags & FL_ALTERNATE) && precision == 0)
_forcdecpt(text);
/* 'g' format means crop zero unless '#' given */
if (ch == 'g' && !(flags & FL_ALTERNATE))
_cropzeros(text);
/* check if result was negative, save '-' for later */
/* and point to positive part (this is for '0' padding) */
if (*text == '-') {
flags |= FL_NEGATIVE;
++text;
}
textlen = strlen(text); /* compute length of text */
}
break;
case 'd':
case 'i':
/* signed decimal output */
flags |= FL_SIGNED;
radix = 10;
goto COMMON_INT;
case 'u':
radix = 10;
goto COMMON_INT;
case 'p':
/* write a pointer -- this is like an integer or long */
/* except we force precision to pad with zeros and */
/* output in big hex. */
precision = 2 * sizeof(void *); /* number of hex digits needed */
#ifndef _MAC
#if !PTR_IS_INT
flags |= FL_LONG; /* assume we're converting a long */
#endif /* !PTR_IS_INT */
#else /* _MAC */
flags &= ~FL_SHORT; /* assume we're converting a long */
flags |= FL_LONG; /* assume we're converting a long */
#endif /* _MAC */
/* DROP THROUGH to hex formatting */
case 'X':
/* unsigned upper hex output */
hexadd = 'A' - '9' - 1; /* set hexadd for uppercase hex */
goto COMMON_HEX;
case 'x':
/* unsigned lower hex output */
hexadd = 'a' - '9' - 1; /* set hexadd for lowercase hex */
/* DROP THROUGH TO COMMON_HEX */
COMMON_HEX:
radix = 16;
if (flags & FL_ALTERNATE) {
/* alternate form means '0x' prefix */
prefix[0] = '0';
prefix[1] = (char)('x' - 'a' + '9' + 1 + hexadd); /* 'x' or 'X' */
prefixlen = 2;
}
goto COMMON_INT;
case 'o':
/* unsigned octal output */
radix = 8;
if (flags & FL_ALTERNATE) {
/* alternate form means force a leading 0 */
flags |= FL_FORCEOCTAL;
}
/* DROP THROUGH to COMMON_INT */
COMMON_INT: {
/* This is the general integer formatting routine. */
/* Basically, we get an argument, make it positive */
/* if necessary, and convert it according to the */
/* correct radix, setting text and textlen */
/* appropriately. */
unsigned __int64 number; /* number to convert */
int digit; /* ascii value of digit */
__int64 l; /* temp long value */
/* 1. read argument into l, sign extend as needed */
if (flags & FL_I64)
l = get_int64_arg(&argptr);
else
#if !LONG_IS_INT
if (flags & FL_LONG)
l = get_long_arg(&argptr);
else
#endif /* !LONG_IS_INT */
#if !SHORT_IS_INT
if (flags & FL_SHORT) {
if (flags & FL_SIGNED)
l = (short) get_int_arg(&argptr); /* sign extend */
else
l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
}
else
#endif /* !SHORT_IS_INT */
{
if (flags & FL_SIGNED)
l = get_int_arg(&argptr); /* sign extend */
else
l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
}
/* 2. check for negative; copy into number */
if ( (flags & FL_SIGNED) && l < 0) {
number = -l;
flags |= FL_NEGATIVE; /* remember negative sign */
}
else {
number = l;
}
/* 3. check precision value for default; non-default */
/* turns off 0 flag, according to ANSI. */
if (precision < 0)
precision = 1; /* default precision */
else
flags &= ~FL_LEADZERO;
/* 4. Check if data is 0; if so, turn off hex prefix */
if (number == 0)
prefixlen = 0;
/* 5. Convert data to ASCII -- note if precision is zero */
/* and number is zero, we get no digits at all. */
text = &buffer[BUFFERSIZE-1]; /* last digit at end of buffer */
while (precision-- > 0 || number != 0) {
digit = (int)(number % radix) + '0';
number /= radix; /* reduce number */
if (digit > '9') {
/* a hex digit, make it a letter */
digit += hexadd;
}
*text-- = (char)digit; /* store the digit */
}
textlen = (char *)&buffer[BUFFERSIZE-1] - text; /* compute length of number */
++text; /* text points to first digit now */
/* 6. Force a leading zero if FORCEOCTAL flag set */
if ((flags & FL_FORCEOCTAL) && (text[0] != '0' || textlen == 0)) {
*--text = '0';
++textlen; /* add a zero */
}
}
break;
}
/* At this point, we have done the specific conversion, and */
/* 'text' points to text to print; 'textlen' is length. Now we */
/* justify it, put on prefixes, leading zeros, and then */
/* print it. */
if (!no_output) {
int padding; /* amount of padding, negative means zero */
if (flags & FL_SIGNED) {
if (flags & FL_NEGATIVE) {
/* prefix is a '-' */
prefix[0] = '-';
prefixlen = 1;
}
else if (flags & FL_SIGN) {
/* prefix is '+' */
prefix[0] = '+';
prefixlen = 1;
}
else if (flags & FL_SIGNSP) {
/* prefix is ' ' */
prefix[0] = ' ';
prefixlen = 1;
}
}
/* calculate amount of padding -- might be negative, */
/* but this will just mean zero */
padding = fldwidth - textlen - prefixlen;
/* put out the padding, prefix, and text, in the correct order */
if (!(flags & (FL_LEFT | FL_LEADZERO))) {
/* pad on left with blanks */
WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
}
/* write prefix */
#ifndef _UNICODE
WRITE_STRING(prefix, prefixlen, &charsout);
#else /* _UNICODE */
if (prefixlen > 0) {
wchar_p = (wchar_t *)_malloc_crt (prefixlen * sizeof(wchar_t));
if (wchar_p == NULL) {
charsout = -1;
break;
}
/* assume prefix string is single-byte characters */
mbstowcs (wchar_p, prefix, prefixlen);
/* UNDONE: check for mbstowcs failure */
WRITE_STRING(wchar_p, prefixlen, &charsout);
_free_crt (wchar_p);
}
#endif /* _UNICODE */
if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
/* write leading zeros */
WRITE_MULTI_CHAR(_T('0'), padding, &charsout);
}
/* write text */
#ifndef _UNICODE
WRITE_STRING(text, textlen, &charsout);
#else /* _UNICODE */
if (!bufferiswide && textlen > 0) {
wchar_p = (wchar_t *)_malloc_crt (textlen * sizeof(wchar_t));
if (wchar_p == NULL) {
charsout = -1;
break;
}
mbstowcs (wchar_p, text, textlen);
/* UNDONE: check for mbstowcs failure */
WRITE_STRING(wchar_p, textlen, &charsout);
_free_crt (wchar_p);
} else {
WRITE_STRING((wchar_t *)text, textlen, &charsout);
}
#endif /* _UNICODE */
if (flags & FL_LEFT) {
/* pad on right with blanks */
WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
}
/* we're done! */
}
break;
}
}
return charsout; /* return value = number of characters written */
}
/*
* Future Optimizations for wsprintf:
* - Prefix is always 1 or 2 characters. Make this memory static rather
* than malloc'ed.
* - Don't free the memory used for converting the buffer to wide chars.
* Use realloc if the memory is not sufficient. Free it at the end.
* - Printing wide-character literal char format strings may be common.
* This may be optimized by performing a single mbstowcs rather than
* many mbtowc (i.e. look ahead in format string until hit a %, then
* write it all out).
*/
/***
*void write_char(int ch, int *pnumwritten)
*void write_char(int ch, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes a single character to the given file/console. If no error occurs,
* then *pnumwritten is incremented; otherwise, *pnumwritten is set
* to -1.
*
*Entry:
* int ch - character to write
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_char (
int ch,
int *pnumwritten
)
{
if (_putch_lk(ch) == EOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#elif defined (_UNICODE)
LOCAL(void) write_char (
wchar_t ch,
FILE *f,
int *pnumwritten
)
{
/* following code is machine dependent */
_putc_lk((int)(ch & 0xff), f);
if (_putc_lk((int)(ch >> 8), f) == EOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#else /* defined (_UNICODE) */
LOCAL(void) write_char (
int ch,
FILE *f,
int *pnumwritten
)
{
if (_putc_lk(ch, f) == EOF)
*pnumwritten = -1;
else
++(*pnumwritten);
}
#endif /* defined (_UNICODE) */
/***
*void write_multi_char(int ch, int num, int *pnumwritten)
*void write_multi_char(int ch, int num, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes num copies of a character to the given file/console. If no error occurs,
* then *pnumwritten is incremented by num; otherwise, *pnumwritten is set
* to -1. If num is negative, it is treated as zero.
*
*Entry:
* int ch - character to write
* int num - number of times to write the characters
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_multi_char (
int ch,
int num,
int *pnumwritten
)
{
while (num-- > 0)
write_char(ch, pnumwritten);
}
#elif defined (_UNICODE)
LOCAL(void) write_multi_char (
wchar_t ch,
int num,
FILE *f,
int *pnumwritten
)
{
while (num-- > 0)
write_char(ch, f, pnumwritten);
}
#else /* defined (_UNICODE) */
LOCAL(void) write_multi_char (
int ch,
int num,
FILE *f,
int *pnumwritten
)
{
while (num-- > 0)
write_char(ch, f, pnumwritten);
}
#endif /* defined (_UNICODE) */
/***
*void write_string(char *string, int len, int *pnumwritten)
*void write_string(char *string, int len, FILE *f, int *pnumwritten)
*void write_wstring(wchar_t *string, int len, int *pnumwritten)
*void write_wstring(wchar_t *string, int len, FILE *f, int *pnumwritten)
*
*Purpose:
* Writes a string of the given length to the given file. If no error occurs,
* then *pnumwritten is incremented by len; otherwise, *pnumwritten is set
* to -1. If len is negative, it is treated as zero.
*
*Entry:
* char *string - string to write (NOT null-terminated)
* int len - length of string
* FILE *f - file to write to
* int *pnumwritten - pointer to integer to update with total chars written
*
*Exit:
* No return value.
*
*Exceptions:
*
*******************************************************************************/
#ifdef CPRFLAG
LOCAL(void) write_string (
char *string,
int len,
int *pnumwritten
)
{
while (len-- > 0)
write_char(*string++, pnumwritten);
}
#elif defined (_UNICODE)
LOCAL(void) write_string (
wchar_t *string,
int len,
FILE *f,
int *pnumwritten
)
{
while (len-- > 0)
write_char(*string++, f, pnumwritten);
}
#else /* defined (_UNICODE) */
LOCAL(void) write_string (
char *string,
int len,
FILE *f,
int *pnumwritten
)
{
while (len-- > 0)
write_char(*string++, f, pnumwritten);
}
#endif /* defined (_UNICODE) */
/***
*int get_int_arg(va_list *pargptr)
*
*Purpose:
* Gets an int argument off the given argument list and updates *pargptr.
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the integer argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
LOCAL(int) get_int_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, int);
}
/***
*long get_long_arg(va_list *pargptr)
*
*Purpose:
* Gets an long argument off the given argument list and updates *pargptr.
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the long argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
#if !LONG_IS_INT
LOCAL(long) get_long_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, long);
}
#endif /* !LONG_IS_INT */
/***
*__int64 get_int86_arg(va_list *pargptr)
*
*Purpose:
* Gets an __int64 short argument off the given argument list and updates *pargptr.
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the __int64 argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
LOCAL(__int64) get_int64_arg (
va_list *pargptr
)
{
double dblTemp;
dblTemp = va_arg(*pargptr, double);
return *(__int64 *)&dblTemp;
// return va_arg(*pargptr, __int64);
}
/***
*short get_short_arg(va_list *pargptr)
*
*Purpose:
* Gets a short argument off the given argument list and updates *pargptr.
* *** CURRENTLY ONLY USED TO GET A WCHAR_T, IFDEF _INTL ***
*
*Entry:
* va_list *pargptr - pointer to argument list; updated by function
*
*Exit:
* Returns the short argument read from the argument list.
*
*Exceptions:
*
*******************************************************************************/
LOCAL(short) get_short_arg (
va_list *pargptr
)
{
return va_arg(*pargptr, short);
}
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
#endif /* _WIN32 */