diff options
Diffstat (limited to 'src/pal/src/safecrt/safecrt_output_l.cpp')
-rw-r--r-- | src/pal/src/safecrt/safecrt_output_l.cpp | 1467 |
1 files changed, 1467 insertions, 0 deletions
diff --git a/src/pal/src/safecrt/safecrt_output_l.cpp b/src/pal/src/safecrt/safecrt_output_l.cpp new file mode 100644 index 0000000000..d6844f4f8b --- /dev/null +++ b/src/pal/src/safecrt/safecrt_output_l.cpp @@ -0,0 +1,1467 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*** +*safecrt_output_l.c - implementation of the _output family for safercrt.lib +* + +* +*Purpose: +* This file contains the implementation of the _output family for safercrt.lib. +* +*Revision History: +* 07-08-04 SJ Stub module created. +* 07-13-04 AC Added support for floating-point types. +* 07-29-04 AC Added macros for a safecrt version of mctowc and wctomb, which target ntdll.dll or msvcrt.dll +* based on the _NTSUBSET_ #define +* 09-24-04 MSL Prefix disallow NULL deref +* +****/ + +#define _SAFECRT_IMPL + +#define __STDC_LIMIT_MACROS +#include "pal/palinternal.h" +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <inttypes.h> +#include "internal_securecrt.h" + +#include "mbusafecrt_internal.h" + +#define _CFLTCVT _safecrt_cfltcvt + +//typedef __int64_t __int64; +typedef double _CRT_DOUBLE; +typedef char _TCHAR; +typedef char TCHAR; +#define _T(x) x +/* +Buffer size required to be passed to _gcvt, fcvt and other fp conversion routines +*/ +#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */ + +//------------------------------------------------------------------------------ +// This code was taken from the 'ouput.c' file located in Visual Studio 8 (i.e. 2005) +// in the '\Microsoft Visual Studio 8\VC\crt\src' directory. It was moved into +// this file to support only the '_output' function used by _vscprintf() in vsprintf.c +// UNUSED / NON-RELEVANT PORTIONS OF THE CODE HAVE BEEN REMOVED - do not try and +// use it to generate any other safecrt 'output' functions +// +// Noteable modifications +// - changed FILE to miniFILE (defined in mbusafecrt_internal.h) +// - removed _soutput_s - it was unused in this case and conflicted with output.inl +// - changed #define SHORT_IS_INT to true varargs promotes shorts to ints in GCC +// - removed definition of __lookuptable_s when using FORMAT_VALIDATIONS, we don't use them + +// 7/03/07 - Created by Stephen Shaw (steshaw) +//------------------------------------------------------------------------------ + + +/* temporary work-around for compiler without 64-bit support */ +#ifndef _INTEGRAL_MAX_BITS +#define _INTEGRAL_MAX_BITS 64 +#endif /* _INTEGRAL_MAX_BITS */ + +#include <limits.h> +#include <string.h> +#include <stddef.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> + +#define _MBTOWC(x,y,z) _minimal_chartowchar( x, y ) + +#undef _malloc_crt +#define _malloc_crt malloc + +#undef _free_crt +#define _free_crt free + +// SNIP -srs 7/3/07 + +#ifndef _CFLTCVT +#define _CFLTCVT _cfltcvt +#endif /* _CFLTCVT */ + +#ifndef _CLDCVT +#define _CLDCVT _cldcvt +#endif /* _CLDCVT */ + +#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 */ +#if __LP64__ + #define LONG_IS_INT 0 + CASSERT(sizeof(long) > sizeof(int)); +#else + #define LONG_IS_INT 1 /* 1 means long is same size as int */ + CASSERT(sizeof(long) == sizeof(int)); +#endif + +// GCC: short is not int, but GCC promotes va_arg to int +#define SHORT_IS_INT 1 + +#define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64 */ + CASSERT(sizeof(long long) == sizeof(int64_t)); + +#if defined (_WIN64) + #define PTR_IS_INT 0 /* 1 means ptr is same size as int */ + CASSERT(sizeof(void *) != sizeof(int)); + #if __LP64__ + #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */ + CASSERT(sizeof(void *) == sizeof(long)); + #else + #define PTR_IS_LONG 0 /* 1 means ptr is same size as long */ + CASSERT(sizeof(void *) != sizeof(long)); + #endif + #define PTR_IS_INT64 1 /* 1 means ptr is same size as int64 */ + CASSERT(sizeof(void *) == sizeof(int64_t)); +#else /* defined (_WIN64) */ + #define PTR_IS_INT 1 /* 1 means ptr is same size as int */ + CASSERT(sizeof(void *) == sizeof(int)); + #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */ + CASSERT(sizeof(void *) == sizeof(long)); + #define PTR_IS_INT64 0 /* 1 means ptr is same size as int64 */ + CASSERT(sizeof(void *) != sizeof(int64_t)); +#endif /* defined (_WIN64) */ + +/* CONSTANTS */ + +/* size of conversion buffer (ANSI-specified minimum is 509) */ + +#define BUFFERSIZE 512 +#define MAXPRECISION BUFFERSIZE + +#if BUFFERSIZE < _CVTBUFSIZE + 6 +/* + * Buffer needs to be big enough for default minimum precision + * when converting floating point needs bigger buffer, and malloc + * fails + */ +#error Conversion buffer too small for max double. +#endif /* BUFFERSIZE < _CVTBUFSIZE + 6 */ + +/* 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_LONGLONG 0x01000 /* long long value given */ +#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 */ +#ifdef FORMAT_VALIDATIONS + ,ST_INVALID /* Invalid format */ +#endif /* FORMAT_VALIDATIONS */ + +}; + +#ifdef FORMAT_VALIDATIONS +#define NUMSTATES (ST_INVALID + 1) +#else /* FORMAT_VALIDATIONS */ +#define NUMSTATES (ST_TYPE + 1) +#endif /* FORMAT_VALIDATIONS */ + +/* 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) || defined (FORMAT_VALIDATIONS) +extern const char __nullstring[]; /* string to print on null ptr */ +extern const wchar_t __wnullstring[]; /* string to print on null ptr */ +#else /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */ +static const char __nullstring[] = "(null)"; /* string to print on null ptr */ +static const wchar_t __wnullstring[] = { '(', 'n', 'u', 'l', 'l', ')', '\0' };/* string to print on null ptr */ +#endif /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */ + +/* 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. */ + +#ifndef FORMAT_VALIDATIONS +#if defined (_UNICODE) || defined (CPRFLAG) +extern const char __lookuptable[]; +#else /* defined (_UNICODE) || defined (CPRFLAG) */ +//extern const char __lookuptable[] = { +const char __lookuptable[] = { + /* ' ' */ 0x06, + /* '!' */ 0x00, + /* '"' */ 0x00, + /* '#' */ 0x06, + /* '$' */ 0x00, + /* '%' */ 0x01, + /* '&' */ 0x00, + /* ''' */ 0x00, + /* '(' */ 0x10, + /* ')' */ 0x00, + /* '*' */ 0x03, + /* '+' */ 0x06, + /* ',' */ 0x00, + /* '-' */ 0x06, + /* '.' */ 0x02, + /* '/' */ 0x10, + /* '0' */ 0x04, + /* '1' */ 0x45, + /* '2' */ 0x45, + /* '3' */ 0x45, + /* '4' */ 0x05, + /* '5' */ 0x05, + /* '6' */ 0x05, + /* '7' */ 0x05, + /* '8' */ 0x05, + /* '9' */ 0x35, + /* ':' */ 0x30, + /* ';' */ 0x00, + /* '<' */ 0x50, + /* '=' */ 0x00, + /* '>' */ 0x00, + /* '?' */ 0x00, + /* '@' */ 0x00, + /* 'A' */ 0x20, // Disable %A format + /* 'B' */ 0x20, + /* 'C' */ 0x38, + /* 'D' */ 0x50, + /* 'E' */ 0x58, + /* 'F' */ 0x07, + /* 'G' */ 0x08, + /* 'H' */ 0x00, + /* 'I' */ 0x37, + /* 'J' */ 0x30, + /* 'K' */ 0x30, + /* 'L' */ 0x57, + /* 'M' */ 0x50, + /* 'N' */ 0x07, + /* 'O' */ 0x00, + /* 'P' */ 0x00, + /* 'Q' */ 0x20, + /* 'R' */ 0x20, + /* 'S' */ 0x08, + /* 'T' */ 0x00, + /* 'U' */ 0x00, + /* 'V' */ 0x00, + /* 'W' */ 0x00, + /* 'X' */ 0x08, + /* 'Y' */ 0x60, + /* 'Z' */ 0x68, + /* '[' */ 0x60, + /* '\' */ 0x60, + /* ']' */ 0x60, + /* '^' */ 0x60, + /* '_' */ 0x00, + /* '`' */ 0x00, + /* 'a' */ 0x70, // Disable %a format + /* 'b' */ 0x70, + /* 'c' */ 0x78, + /* 'd' */ 0x78, + /* 'e' */ 0x78, + /* 'f' */ 0x78, + /* 'g' */ 0x08, + /* 'h' */ 0x07, + /* 'i' */ 0x08, + /* 'j' */ 0x00, + /* 'k' */ 0x00, + /* 'l' */ 0x07, + /* 'm' */ 0x00, + /* 'n' */ 0x00, // Disable %n format + /* 'o' */ 0x08, + /* 'p' */ 0x08, + /* 'q' */ 0x00, + /* 'r' */ 0x00, + /* 's' */ 0x08, + /* 't' */ 0x00, + /* 'u' */ 0x08, + /* 'v' */ 0x00, + /* 'w' */ 0x07, + /* 'x' */ 0x08 +}; + +#endif /* defined (_UNICODE) || defined (CPRFLAG) */ + +#else /* FORMAT_VALIDATIONS */ +// SNIP -srs 7/3/07 +#error code has been removed +#endif /* FORMAT_VALIDATIONS */ + +#define FIND_CHAR_CLASS(lookuptbl, c) \ + ((c) < _T(' ') || (c) > _T('x') ? \ + CH_OTHER \ + : \ + (enum CHARTYPE)(lookuptbl[(c)-_T(' ')] & 0xF)) + +#define FIND_NEXT_STATE(lookuptbl, class, state) \ + (enum STATE)(lookuptbl[(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(_TCHAR ch, int *pnumwritten); +LOCAL(void) write_multi_char(_TCHAR ch, int num, int *pnumwritten); +LOCAL(void) write_string(const _TCHAR *string, int len, int *numwritten); +LOCAL(void) write_wstring(const wchar_t *string, int len, int *numwritten); + +#else /* CPRFLAG */ + +#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(_TCHAR ch, miniFILE *f, int *pnumwritten); +LOCAL(void) write_multi_char(_TCHAR ch, int num, miniFILE *f, int *pnumwritten); +LOCAL(void) write_string(const _TCHAR *string, int len, miniFILE *f, int *numwritten); +//LOCAL(void) write_wstring(const wchar_t *string, int len, miniFILE *f, int *numwritten); + +#endif /* CPRFLAG */ + +#define get_short_arg(list) va_arg(*list, int) // GCC promotes va_arg shorts into int values +#define get_int_arg(list) va_arg(*list, int) +#define get_long_arg(list) va_arg(*list, long) +#define get_long_long_arg(list) va_arg(*list, long long) +#define get_int64_arg(list) va_arg(*list, __int64) +#define get_crtdouble_arg(list) va_arg(*list, _CRT_DOUBLE) +#define get_ptr_arg(list) va_arg(*list, void *) + +#ifdef CPRFLAG +LOCAL(int) output(const _TCHAR *, _locale_t , va_list); +_CRTIMP int __cdecl _vtcprintf_l (const _TCHAR *, _locale_t, va_list); +_CRTIMP int __cdecl _vtcprintf_s_l (const _TCHAR *, _locale_t, va_list); +_CRTIMP int __cdecl _vtcprintf_p_l (const _TCHAR *, _locale_t, 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: +* +*******************************************************************************/ +#ifndef FORMAT_VALIDATIONS +_CRTIMP int __cdecl _tcprintf_l ( + const _TCHAR * format, + _locale_t plocinfo, + ... + ) +#else /* FORMAT_VALIDATIONS */ +_CRTIMP int __cdecl _tcprintf_s_l ( + const _TCHAR * format, + _locale_t plocinfo, + ... + ) +#endif /* FORMAT_VALIDATIONS */ + +{ + int ret; + va_list arglist; + va_start(arglist, plocinfo); + +#ifndef FORMAT_VALIDATIONS + ret = _vtcprintf_l(format, plocinfo, arglist); +#else /* FORMAT_VALIDATIONS */ + ret = _vtcprintf_s_l(format, plocinfo, arglist); + +#endif /* FORMAT_VALIDATIONS */ + + va_end(arglist); + + return ret; +} + +#ifndef FORMAT_VALIDATIONS +_CRTIMP int __cdecl _tcprintf ( + const _TCHAR * format, + ... + ) +#else /* FORMAT_VALIDATIONS */ +_CRTIMP int __cdecl _tcprintf_s ( + const _TCHAR * format, + ... + ) +#endif /* FORMAT_VALIDATIONS */ + +{ + int ret; + va_list arglist; + + va_start(arglist, format); + +#ifndef FORMAT_VALIDATIONS + ret = _vtcprintf_l(format, NULL, arglist); +#else /* FORMAT_VALIDATIONS */ + ret = _vtcprintf_s_l(format, NULL, arglist); + +#endif /* FORMAT_VALIDATIONS */ + + va_end(arglist); + + return ret; +} + +#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 +#ifndef FORMAT_VALIDATIONS +_CRTIMP int __cdecl _vtcprintf ( + const _TCHAR *format, + va_list argptr + ) +{ + return _vtcprintf_l(format, NULL, argptr); +} + +#else /* FORMAT_VALIDATIONS */ +_CRTIMP int __cdecl _vtcprintf_s ( + const _TCHAR *format, + va_list argptr + ) +{ + return _vtcprintf_s_l(format, NULL, argptr); +} + +#endif /* FORMAT_VALIDATIONS */ +#endif /* CPRFLAG */ + +#ifdef CPRFLAG +#ifndef FORMAT_VALIDATIONS +_CRTIMP int __cdecl _vtcprintf_l ( +#else /* FORMAT_VALIDATIONS */ +_CRTIMP int __cdecl _vtcprintf_s_l ( +#endif /* FORMAT_VALIDATIONS */ +#else /* CPRFLAG */ + +#ifdef _UNICODE +#ifndef FORMAT_VALIDATIONS +int __cdecl _woutput ( + miniFILE *stream, +#else /* FORMAT_VALIDATIONS */ +int __cdecl _woutput_s ( + miniFILE *stream, +#endif /* FORMAT_VALIDATIONS */ +#else /* _UNICODE */ +#ifndef FORMAT_VALIDATIONS +int __cdecl _output ( + miniFILE *stream, +#else /* FORMAT_VALIDATIONS */ + int __cdecl _output_s ( + miniFILE *stream, + +#endif /* FORMAT_VALIDATIONS */ +#endif /* _UNICODE */ + +#endif /* CPRFLAG */ + const _TCHAR *format, + va_list argptr + ) +{ + int hexadd=0; /* offset to add to number to get 'a'..'f' */ + TCHAR ch; /* character just read */ + int flags=0; /* 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 = 0; /* selected field width -- 0 means default */ + int precision = 0; /* selected precision -- -1 means default */ + TCHAR prefix[2]; /* numeric prefix -- up to two characters */ + int prefixlen=0; /* length of prefix -- 0 means no prefix */ + int capexp = 0; /* non-zero = 'E' exponent signifient, zero = 'e' */ + int no_output=0; /* non-zero = prodcue no output for this specifier */ + union { + const char *sz; /* pointer text to be printed, not zero terminated */ + const 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 buffersize; /* size of text.sz (used only for the call to _cfltcvt) */ + int bufferiswide=0; /* non-zero = buffer contains wide chars already */ + +#ifndef CPRFLAG + _VALIDATE_RETURN( (stream != NULL), EINVAL, -1); +#endif /* CPRFLAG */ + _VALIDATE_RETURN( (format != NULL), EINVAL, -1); + + charsout = 0; /* no characters written yet */ + textlen = 0; /* no text yet */ + state = ST_NORMAL; /* starting state */ + buffersize = 0; + + /* main loop -- loop while format character exist and no I/O errors */ + while ((ch = *format++) != _T('\0') && charsout >= 0) { +#ifndef FORMAT_VALIDATIONS + chclass = FIND_CHAR_CLASS(__lookuptable, ch); /* find character class */ + state = FIND_NEXT_STATE(__lookuptable, chclass, state); /* find next state */ +#else /* FORMAT_VALIDATIONS */ + chclass = FIND_CHAR_CLASS(__lookuptable_s, ch); /* find character class */ + state = FIND_NEXT_STATE(__lookuptable_s, chclass, state); /* find next state */ + + _VALIDATE_RETURN((state != ST_INVALID), EINVAL, -1); + +#endif /* FORMAT_VALIDATIONS */ + + /* 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; +#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) { + case _T('l'): + /* + * In order to handle the ll case, we depart from the + * simple deterministic state machine. + */ + if (*format == _T('l')) + { + ++format; + flags |= FL_LONGLONG; /* 'll' => long long */ + } + else + { + flags |= FL_LONG; /* 'l' => long int or wchar_t */ + } + break; + + case _T('I'): + /* + * In order to handle the I, I32, and I64 size modifiers, we + * depart from the simple deterministic state machine. The + * code below scans for characters following the 'I', + * and defaults to 64 bit on WIN64 and 32 bit on WIN32 + */ +#if PTR_IS_INT64 + flags |= FL_I64; /* 'I' => __int64 on WIN64 systems */ +#endif /* PTR_IS_INT64 */ + if ( (*format == _T('6')) && (*(format + 1) == _T('4')) ) + { + format += 2; + flags |= FL_I64; /* I64 => __int64 */ + } + else if ( (*format == _T('3')) && (*(format + 1) == _T('2')) ) + { + format += 2; + flags &= ~FL_I64; /* I32 => __int32 */ + } + else if ( (*format == _T('d')) || + (*format == _T('i')) || + (*format == _T('o')) || + (*format == _T('u')) || + (*format == _T('x')) || + (*format == _T('X')) ) + { + /* + * Nothing further needed. %Id (et al) is + * handled just like %d, except that it defaults to 64 bits + * on WIN64. Fall through to the next iteration. + */ + } + else { + state = ST_NORMAL; + goto NORMAL_STATE; + } + break; + + case _T('h'): + flags |= FL_SHORT; /* 'h' => short int or char */ + break; + + 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); + no_output = 1; + } else { + /* format multibyte character */ + /* this is an extension of ANSI */ + unsigned short temp; + wchar = (wchar_t)get_int_arg(&argptr); + temp = (unsigned short)wchar; + { + buffer.sz[0] = (char) temp; + textlen = 1; + } + } + text.sz = buffer.sz; +#endif /* _UNICODE */ + } + break; + + case _T('Z'): { + /* print a Counted String */ + struct _count_string { + short Length; + short MaximumLength; + char *Buffer; + } *pstr; + + pstr = (struct _count_string *)get_ptr_arg(&argptr); + if (pstr == NULL || pstr->Buffer == NULL) { + /* null ptr passed, use special string */ + text.sz = __nullstring; + textlen = (int)strlen(text.sz); + } else { + if (flags & FL_WIDECHAR) { + text.wz = (wchar_t *)pstr->Buffer; + textlen = pstr->Length / (int)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; + const char *p; /* temps */ + const 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 = (char *)get_ptr_arg(&argptr); + + /* 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++) { + ++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 = (int)(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 = (int)(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 = (int)(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); + + /* %n is disabled */ + _VALIDATE_RETURN(("'n' format specifier disabled" && 0), EINVAL, -1); + break; + + /* 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'): + case _T('A'): + 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'): + case _T('a'): { + /* 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 */ + buffersize = BUFFERSIZE; + + /* compute the precision value */ + if (precision < 0) + precision = 6; /* default precision: 6 */ + else if (precision == 0 && ch == _T('g')) + precision = 1; /* ANSI specified */ + else if (precision > MAXPRECISION) + precision = MAXPRECISION; + + if (precision > BUFFERSIZE - _CVTBUFSIZE) { + precision = BUFFERSIZE - _CVTBUFSIZE; + } + + /* for safecrt, we pass along the FL_ALTERNATE flag to _safecrt_cfltcvt */ + if (flags & FL_ALTERNATE) + { + capexp |= FL_ALTERNATE; + } + + _CRT_DOUBLE tmp; + tmp=va_arg(argptr, _CRT_DOUBLE); + /* Note: assumes ch is in ASCII range */ + /* In safecrt, we provide a special version of _cfltcvt which internally calls printf (see safecrt_output_s.c) */ + _CFLTCVT(&tmp, buffer.sz, buffersize, (char)ch, precision, capexp); + + /* 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 = (int)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_INT64 + flags |= FL_I64; /* assume we're converting an int64 */ +#elif !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 */ + uint64_t 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 (flags & FL_LONGLONG) + l = get_long_long_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; + } + +#if _INTEGRAL_MAX_BITS >= 64 + if ( (flags & FL_I64) == 0 && (flags & FL_LONGLONG) == 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; + if (precision > MAXPRECISION) + precision = MAXPRECISION; + } + + /* 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. */ + + char *sz; + 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; + } + *sz-- = (char)digit; /* store the digit */ + } + + textlen = (int)((char *)&buffer.sz[BUFFERSIZE-1] - sz); /* compute length of number */ + ++sz; /* text points to first digit now */ + + + /* 6. Force a leading zero if FORCEOCTAL flag set */ + if ((flags & FL_FORCEOCTAL) && (textlen == 0 || sz[0] != '0')) { + *--sz = '0'; + ++textlen; /* add a zero */ + } + + text.sz = sz; + } + 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)) { + charsout = -1; + } else { + WRITE_STRING(text.sz, textlen, &charsout); + } +#else /* _UNICODE */ + if (!bufferiswide && textlen > 0) { + char *p; + int retval = 0 + int count; + + p = text.sz; + count = textlen; + while (count-- > 0) { + retval = _MBTOWC(&wchar, p, MB_CUR_MAX); + if (retval <= 0) { + charsout = -1; + break; + } + WRITE_CHAR(wchar, &charsout); + p += retval; + } + } else { + WRITE_STRING(text.wz, textlen, &charsout); + } +#endif /* _UNICODE */ + + if (charsout >= 0 && (flags & FL_LEFT)) { + /* pad on right with blanks */ + WRITE_MULTI_CHAR(_T(' '), padding, &charsout); + } + + /* we're done! */ + } + break; + } + } + +#ifdef FORMAT_VALIDATIONS + /* The format string shouldn't be incomplete - i.e. when we are finished + with the format string, the last thing we should have encountered + should have been a regular char to be output or a type specifier. Else + the format string was incomplete */ + _VALIDATE_RETURN(((state == ST_NORMAL) || (state == ST_TYPE)), EINVAL, -1); +#endif /* FORMAT_VALIDATIONS */ + + 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(char ch, int *pnumwritten) +*ifdef _UNICODE +*void write_char(wchar_t ch, FILE *f, int *pnumwritten) +*endif +*void write_char(char 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: +* _TCHAR 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 ( + _TCHAR ch, + int *pnumwritten + ) +{ +#ifdef _UNICODE + if (_putwch_nolock(ch) == WEOF) +#else /* _UNICODE */ + if (_putch_nolock(ch) == EOF) +#endif /* _UNICODE */ + *pnumwritten = -1; + else + ++(*pnumwritten); +} + +#else /* CPRFLAG */ + +LOCAL(void) write_char ( + _TCHAR ch, + miniFILE *f, + int *pnumwritten + ) +{ + if ( (f->_flag & _IOSTRG) && f->_base == NULL) + { + ++(*pnumwritten); + return; + } +#ifdef _UNICODE + if (_putwc_nolock(ch, f) == WEOF) +#else /* _UNICODE */ + if (_putc_nolock(ch, f) == EOF) +#endif /* _UNICODE */ + *pnumwritten = -1; + else + ++(*pnumwritten); +} + +#endif /* CPRFLAG */ + +/*** +*void write_multi_char(char 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(char 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: +* _TCHAR 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 ( + _TCHAR ch, + int num, + int *pnumwritten + ) +{ + while (num-- > 0) { + write_char(ch, pnumwritten); + if (*pnumwritten == -1) + break; + } +} + +#else /* CPRFLAG */ + +LOCAL(void) write_multi_char ( + _TCHAR ch, + int num, + miniFILE *f, + int *pnumwritten + ) +{ + while (num-- > 0) { + write_char(ch, f, pnumwritten); + if (*pnumwritten == -1) + break; + } +} + +#endif /* CPRFLAG */ + +/*** +*void write_string(const char *string, int len, int *pnumwritten) +*void write_string(const char *string, int len, FILE *f, int *pnumwritten) +*ifdef _UNICODE +*void write_string(const wchar_t *string, int len, FILE *f, int *pnumwritten) +*endif +*void write_wstring(const wchar_t *string, int len, int *pnumwritten) +*void write_wstring(const 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: +* _TCHAR *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 ( + const _TCHAR *string, + int len, + int *pnumwritten + ) +{ + while (len-- > 0) { + write_char(*string++, pnumwritten); + if (*pnumwritten == -1) + { + if (errno == EILSEQ) + write_char(_T('?'), pnumwritten); + else + break; + } + } +} + +#else /* CPRFLAG */ + +LOCAL(void) write_string ( + const _TCHAR *string, + int len, + miniFILE *f, + int *pnumwritten + ) +{ + if ( (f->_flag & _IOSTRG) && f->_base == NULL) + { + (*pnumwritten) += len; + return; + } + while (len-- > 0) { + write_char(*string++, f, pnumwritten); + if (*pnumwritten == -1) + { + if (errno == EILSEQ) + write_char(_T('?'), f, pnumwritten); + else + break; + } + } +} +#endif /* CPRFLAG */ |