summaryrefslogtreecommitdiffstats
path: root/private/crt32/stdio/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/crt32/stdio/output.c')
-rw-r--r--private/crt32/stdio/output.c1393
1 files changed, 1393 insertions, 0 deletions
diff --git a/private/crt32/stdio/output.c b/private/crt32/stdio/output.c
new file mode 100644
index 000000000..b2f85638f
--- /dev/null
+++ b/private/crt32/stdio/output.c
@@ -0,0 +1,1393 @@
+/***
+*output.c - printf style output to a FILE
+*
+* Copyright (c) 1989-1991, 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 ****
+*
+*Revision History:
+* 06-01-89 PHG Module created
+* 08-28-89 JCR Added cast to get rid of warning (no object changes)
+* 02-15-90 GJF Fixed copyright
+* 03-19-90 GJF Made calling type _CALLTYPE1 and added #include
+* <cruntime.h>.
+* 03-26-90 GJF Changed LOCAL macro to incorporate _CALLTYPE4. Placed
+* prototype for _output() in internal.h and #include-d
+* it.
+* 08-01-90 SBM Compiles cleanly with -W3, moved _cfltcvt_tab and
+* typedefs DOUBLE and LONGDOUBLE to new header
+* <fltintrn.h>, formerly named <struct.h>
+* 09-05-90 SBM First attempt at adding CPRFLAG and code to generate
+* cprintf. Anything in #ifdef CPRFLAG untested.
+* Still needs to have locking added for MTHREAD case.
+* 10-03-90 GJF New-style function declarators.
+* 01-02-91 SRW Added _WIN32_ conditional for 'C' and 'S' format chars.
+* 01-16-91 GJF ANSI naming.
+* 01-16-91 SRW Added #include of maketabc.out (_WIN32_)
+* 04-09-91 PNT Use the _CRUISER_ mapping for _MAC_
+* 04-16-91 SRW Fixed #include of maketabc.out (_WIN32_)
+* 04-25-91 SRW Made nullstring static
+* 05-20-91 GJF Moved state table for Win32 inline (_WIN32_).
+* 09-12-91 JCR Bumped conversion buffer size to be ANSI-compliant
+* 09-17-91 IHJ Add partial UNICODE (%ws, %wc) support
+* 09-28-91 GJF Merged with crt32 and crtdll versions. For now, 9-17-91
+* change is built only for Win32, not Dosx32 (_WIN32_).
+* 10-22-91 ETC Complete wchar_t/mb support under _INTL. For now,
+* 9-28-91 change is additionally under !_INTL. Bug fix:
+* ints and pointers are longs.
+* 11-19-91 ETC Added support for _wsprintf, _vwsprintf with WPRFLAG;
+* added %tc %ts (generic string handling).
+* 12-05-91 GDP Bug fix: va_arg was used inconsistently for double
+* 12-19-91 ETC Added some comments on wsprintf optimization, undones;
+* check return on malloc.
+* 03-25-92 DJM POSIX support
+* 04-16-92 KRS Support new ISO {s|f}wprintf with Unicode format string.
+* 06-08-92 SRW Modified to not use free and malloc for mbtowc conversion.
+* 06-10-92 KRS Fix glitch in previous change.
+* 07-14-92 TVB Added Alpha support (quad stuff).
+* 07-17-92 KRS Fix typo which broke WPRFLAG support.
+* 04-16-93 SKS Fix bug in 'S' option logic.
+* 07-16-93 SRW ALPHA Merge
+* 08-17-93 CFW Avoid mapping tchar macros incorrectly if _MBCS defined.
+*
+*******************************************************************************/
+
+/* temporary hack to minimize changes. This should go into fltintrn.h */
+#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC)
+#define DOUBLE double
+#endif
+
+
+#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 <assert.h>
+
+/* inline keyword is non-ANSI C7 extension */
+/* CONSIDER: move to cruntime.h! */
+#if !defined(_MSC_VER) || defined(__STDC__)
+#define __inline static
+#else
+/* UNDONE: compiler is broken */
+#define __inline static
+#endif
+
+#if defined(WPRFLAG) && !defined(_UNICODE)
+#define _UNICODE 1
+#endif
+#ifdef _MBCS /* always want either Unicode or SBCS for tchar.h */
+#undef _MBCS
+#endif
+#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 _CALLTYPE4
+
+/* 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
+
+#ifndef WPRFLAG
+#if SHORT_IS_INT
+ #define get_short_arg(x) (short)get_int_arg(x)
+#endif
+#endif
+
+#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
+ #error Size of pointer must be same as size of int or long
+#endif
+
+
+
+/* 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
+
+/* 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 */
+
+/* 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(WPRFLAG) || defined(CPRFLAG)
+extern char *__nullstring; /* string to print on null ptr */
+extern wchar_t *__wnullstring; /* string to print on null ptr */
+#else /* WPRFLAG || CPRFLAG */
+char *__nullstring = "(null)"; /* string to print on null ptr */
+wchar_t *__wnullstring = L"(null)";/* string to print on null ptr */
+#endif /* WPRFLAG || 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(WPRFLAG) || defined(CPRFLAG)
+extern const char __lookuptable[];
+#else /* WPRFLAG/CPRFLAG */
+#if defined(_CRUISER_) || defined(_MAC_)
+
+/* Table generated by maketabc.c built with -D_CRUISER_. */
+
+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, 0x20, 0x38, 0x50, 0x58, 0x07, 0x08,
+ 0x00, 0x30, 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, 0x00, 0x08, 0x00, 0x07,
+ 0x08
+};
+
+#else /* ndef _CRUISER_ || _MAC_ */
+
+#if defined(_WIN32_) || defined(_POSIX_)
+
+/* 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, 0x20, 0x38, 0x50, 0x58, 0x07, 0x08,
+ 0x00, 0x30, 0x30, 0x30, 0x57, 0x50, 0x07, 0x00,
+ 0x00, 0x20, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x60, 0x68, 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,
+ 0x08
+};
+
+#else /* ndef _WIN32_ */
+
+#error ERROR - ONLY CRUISER, WIN32, POSIX, OR MAC TARGET SUPPORTED!
+
+#endif /* _WIN32_ || _POSIX_ */
+
+#endif /* _CRUISER_ || _MAC_ */
+
+#endif /* WPRFLAG || 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 WPRFLAG 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 WPRFLAG
+
+#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(int 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
+
+#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
+
+__inline int _CALLTYPE4 get_int_arg(va_list *pargptr);
+
+#ifndef WPRFLAG
+#if !SHORT_IS_INT
+__inline short _CALLTYPE4 get_short_arg(va_list *pargptr);
+#endif
+#endif
+
+#if !LONG_IS_INT
+__inline long _CALLTYPE4 get_long_arg(va_list *pargptr);
+#endif
+
+#ifdef _ALPHA_
+__inline __int64 _CALLTYPE4 get_quad_arg(va_list *pargptr);
+#endif
+
+#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 _CALLTYPE2 _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 WPRFLAG
+* The wide-character flavour returns the number of wide-characters written.
+#endif
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+#ifdef CPRFLAG
+LOCAL(int) output (
+#elif WPRFLAG
+int _CALLTYPE1 _woutput (
+ FILE *stream,
+#else
+int _CALLTYPE1 _output (
+ FILE *stream,
+#endif
+ 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 WPRFLAG */
+ union {
+ char sz[BUFFERSIZE];
+#ifdef WPRFLAG
+ wchar_t wz[BUFFERSIZE];
+#endif
+ } buffer;
+ wchar_t wchar; /* temp wchar_t */
+ int bufferiswide; /* non-zero = buffer contains wide chars already */
+
+ 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 -- just write character */
+#ifdef WPRFLAG
+ bufferiswide = 1;
+#else
+ bufferiswide = 0;
+ if (isleadbyte((int)(unsigned char)ch)) {
+ WRITE_CHAR(ch, &charsout);
+ ch = *format++;
+ assert (ch != _T('\0')); /* UNDONE: don't fall off format string */
+ }
+#endif /* !WPRFLAG */
+ 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
+
+#if !LONGDOUBLE_IS_DOUBLE || defined(_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_LONGDOUBLE; /* 'L' => long double */
+ break;
+#endif
+
+#if !SHORT_IS_INT || defined(_UNICODE)
+ case _T('h'):
+ flags |= FL_SHORT; /* 'h' => short int or char */
+ break;
+#endif
+
+/* 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 WPRFLAG
+ /* CONSIDER: non-standard */
+ flags |= FL_SHORT;
+#else
+ flags |= FL_WIDECHAR; /* ISO std. */
+#endif
+ /* fall into 'c' case */
+
+ case _T('c'): {
+ /* print a single character specified by int argument */
+#ifdef WPRFLAG
+ 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];
+#ifdef _OUT
+ if (isleadbyte(wchar >> 8))
+ {
+ tempchar[0] = (wchar >> 8);
+ tempchar[1] = (wchar & 0x00ff);
+ }
+ else
+#endif /* _OUT */
+ {
+ 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 /* WPRFLAG */
+ 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);
+#ifdef _OUT
+ if (isleadbyte(temp >> 8))
+ {
+ buffer.sz[0] = temp >> 8;
+ buffer.sz[1] = temp & 0x00ff;
+ textlen = 2;
+ }
+ else
+#endif /* _OUT */
+ {
+ buffer.sz[0] = (char) temp;
+ textlen = 1;
+ }
+ }
+ text.sz = buffer.sz;
+#endif /* WPRFLAG */
+ }
+ break;
+
+#if defined(_WIN32_) && !defined(_DOSX32_) /* UNDONE: NT hack */
+ case _T('Z'): {
+ /* print a Counted String
+
+ int i;
+ char *p; /* temps */
+ struct 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;
+#endif
+
+ case _T('S'): /* ISO wide character string */
+#ifndef WPRFLAG
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_WIDECHAR;
+#else
+ if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
+ flags |= FL_SHORT;
+#endif
+ 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 WPRFLAG
+ 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 /* WPRFLAG */
+ if (flags & (FL_LONG|FL_WIDECHAR))
+ {
+ size_t temp;
+ char tchr[MB_LEN_MAX];
+ if (text.wz == NULL) /* NULL passed, use special string */
+ text.wz = __wnullstring;
+ bufferiswide = 1;
+ pwch = text.wz;
+ for (textlen=0; textlen<i && *pwch; pwch++)
+ {
+ if ((temp = wctomb(tchr, *pwch))<=0) break;
+ textlen += temp;
+ }
+ /* textlen now contains length in bytes */
+ }
+ 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 /* WPRFLAG */
+ }
+ 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
+
+#if !SHORT_IS_INT
+ if (flags & FL_SHORT)
+ *(short *)p = (short) charsout;
+ else
+#endif
+ *(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
+ {
+ 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
+ /* 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. */
+
+#ifdef _ALPHA_
+ unsigned __int64 number; /* number to convert */
+ int digit; /* ascii value of digit */
+ __int64 l; /* temp long value */
+#else
+ unsigned long number; /* number to convert */
+ int digit; /* ascii value of digit */
+ long l; /* temp long value */
+#endif
+
+ /* 1. read argument into l, sign extend as needed */
+#ifdef _ALPHA_
+ if (flags & FL_LONGDOUBLE)
+ l = get_quad_arg(&argptr);
+ else
+#endif
+#if !LONG_IS_INT
+ if (flags & FL_LONG)
+ l = get_long_arg(&argptr);
+ else
+#endif
+
+#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
+ {
+ 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;
+ }
+#ifdef _ALPHA_
+ if ((flags & FL_LONGDOUBLE) == 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
+
+ /* 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 WPRFLAG
+ 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
+ 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 /* WPRFLAG */
+
+ 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 WPRFLAG
+*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 WPRFLAG
+
+LOCAL(void) write_char (
+ int ch,
+ FILE *f,
+ int *pnumwritten
+ )
+{
+ if (_putwc_lk(ch, f) == WEOF)
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#else
+
+LOCAL(void) write_char (
+ int ch,
+ FILE *f,
+ int *pnumwritten
+ )
+{
+ if (_putc_lk(ch, f) == EOF)
+ *pnumwritten = -1;
+ else
+ ++(*pnumwritten);
+}
+
+#endif
+
+/***
+*void write_multi_char(int ch, int num, int *pnumwritten)
+#ifdef WPRFLAG
+*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);
+}
+
+#else /* CPRFLAG */
+#if WPRFLAG
+
+LOCAL(void) write_multi_char (
+ wchar_t ch,
+ int num,
+ FILE *f,
+ int *pnumwritten
+ )
+#else
+
+LOCAL(void) write_multi_char (
+ int ch,
+ int num,
+ FILE *f,
+ int *pnumwritten
+ )
+#endif /* WPRFLAG */
+{
+ while (num-- > 0)
+ write_char(ch, f, pnumwritten);
+}
+
+#endif /* CPRFLAG */
+
+/***
+*void write_string(char *string, int len, int *pnumwritten)
+*void write_string(char *string, int len, FILE *f, int *pnumwritten)
+#ifdef WPRFLAG
+*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);
+}
+
+#else /* CPRFLAG */
+#if WPRFLAG
+
+LOCAL(void) write_string (
+ wchar_t *string,
+ int len,
+ FILE *f,
+ int *pnumwritten
+ )
+#else
+
+LOCAL(void) write_string (
+ char *string,
+ int len,
+ FILE *f,
+ int *pnumwritten
+ )
+#endif /* WPRFLAG */
+{
+#ifdef _POSIX_
+ while (len-- > 0) {
+ write_char(*string++, f, pnumwritten);
+ if (*pnumwritten < 0)
+ return;
+ }
+#else
+ while (len-- > 0)
+ write_char(*string++, f, pnumwritten);
+#endif
+}
+#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 _CALLTYPE4 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 _CALLTYPE4 get_long_arg (
+ va_list *pargptr
+ )
+{
+ return va_arg(*pargptr, long);
+}
+#endif
+
+#ifdef _ALPHA_
+__inline __int64 _CALLTYPE4 get_quad_arg (
+ va_list *pargptr
+ )
+{
+ return va_arg(*pargptr, __int64);
+}
+#endif
+
+#ifndef WPRFLAG
+/***
+*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 _CALLTYPE4 get_short_arg (
+ va_list *pargptr
+ )
+{
+ return va_arg(*pargptr, short);
+}
+#endif
+#endif