1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/***
6*output.c - printf style output to a FILE
7*
8
9*
10*Purpose:
11* This file contains the code that does all the work for the
12* printf family of functions. It should not be called directly, only
13* by the *printf functions. We don't make any assumtions about the
14* sizes of ints, longs, shorts, or long doubles, but if types do overlap,
15* we also try to be efficient. We do assume that pointers are the same
16* size as either ints or longs.
17* If CPRFLAG is defined, defines _cprintf instead.
18* **** DOESN'T CURRENTLY DO MTHREAD LOCKING ****
19*
20*Note:
21* this file is included in safecrt.lib build directly, plese refer
22* to safecrt_[w]output_s.c
23*
24*******************************************************************************/
25
26
27//typedef __int64_t __int64;
28
29
30#define FORMAT_VALIDATIONS
31
32typedef double _CRT_DOUBLE;
33
34//typedef int* intptr_t;
35
36/*
37Buffer size required to be passed to _gcvt, fcvt and other fp conversion routines
38*/
39#define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */
40
41/* temporary work-around for compiler without 64-bit support */
42#ifndef _INTEGRAL_MAX_BITS
43#define _INTEGRAL_MAX_BITS 64
44#endif /* _INTEGRAL_MAX_BITS */
45
46//#include <mtdll.h>
47//#include <cruntime.h>
48//#include <limits.h>
49//#include <string.h>
50//#include <stddef.h>
51//#include <crtdefs.h>
52//#include <stdio.h>
53//#include <stdarg.h>
54//#include <cvt.h>
55//#include <conio.h>
56//#include <internal.h>
57//#include <fltintrn.h>
58//#include <stdlib.h>
59//#include <ctype.h>
60//#include <dbgint.h>
61//#include <setlocal.h>
62
63#define _MBTOWC(x,y,z) _minimal_chartowchar( x, y )
64
65#ifndef _WCTOMB_S
66#define _WCTOMB_S wctomb_s
67#endif /* _WCTOMB_S */
68
69#undef _malloc_crt
70#define _malloc_crt malloc
71
72#undef _free_crt
73#define _free_crt free
74
75/* Wrapper for _output_s so that we do not expose FILE in the _output_s signature.
76 * Always ensure null-termination. Returns the number of written chars, not including the terminating null.
77 * Returns -1 if something went wrong during the formatting (in _output_s), e.g. mbcs conversions.
78 * Returns -2 if the string has been truncated.
79 * _output_s calls _invalid_parameter (and returns -1, possibly) if the format string is malformed.
80 */
81#ifndef _UNICODE
82int __cdecl _soutput_s(char *_Dst, size_t _Size, const char *_Format, va_list _ArgList)
83#else /* _UNICODE */
84int __cdecl _swoutput_s(wchar_t *_Dst, size_t _Size, const wchar_t *_Format, va_list _ArgList)
85#endif /* _UNICODE */
86{
87 miniFILE stream;
88 miniFILE *outfile = &stream;
89 int written = -1;
90
91 /* validation section */
92#ifndef _UNICODE
93 if(_Size==SIZE_MAX)
94 {
95 /* user is attempting to make us unbounded, but we don't fit that much */
96 outfile->_cnt = INT_MAX;
97 }
98 else
99 {
100 _VALIDATE_RETURN(_Size <= INT_MAX, EINVAL, -1);
101 outfile->_cnt = (int)_Size;
102 }
103 outfile->_ptr = outfile->_base = _Dst;
104#else /* _UNICODE */
105 if(_Size==SIZE_MAX)
106 {
107 /* user is attempting to make us unbounded, but we don't fit that much */
108 outfile->_cnt = INT_MAX;
109 }
110 else if(_Size>(INT_MAX/sizeof(wchar_t)))
111 {
112 /* we can't represent the amount of output the user asked for */
113 _VALIDATE_RETURN( 0 /* FALSE */, EINVAL, -1 );
114 }
115 else
116 {
117 outfile->_cnt = (int)(_Size*sizeof(wchar_t));
118 }
119 outfile->_ptr = outfile->_base = (char*)_Dst;
120#endif /* _UNICODE */
121 outfile->_flag = _IOWRT | _IOSTRG;
122
123#ifndef _UNICODE
124 written = _output_s(outfile, _Format, _ArgList);
125#else /* _UNICODE */
126 written = _woutput_s(outfile, _Format, _ArgList);
127#endif /* _UNICODE */
128 _Dst[_Size - 1] = 0;
129 if (written < 0)
130 {
131 if (outfile->_cnt < 0)
132 {
133 /* the buffer was too small; we return -2 to indicate truncation */
134 return -2;
135 }
136 /* otherwise, something else failed: we reset the string and we return */
137 if (_Dst != NULL && _Size > 0)
138 {
139 *_Dst = 0;
140 }
141 return written;
142 }
143
144#ifndef _UNICODE
145 if ((_putc_nolock('\0', outfile) != EOF))
146#else /* _UNICODE */
147 if ((_putc_nolock('\0', outfile) != EOF) && (_putc_nolock('\0', outfile) != EOF))
148#endif /* _UNICODE */
149 {
150 return written;
151 }
152 /* the last putc failed, so it means there is not enough space in the buffer */
153 return -2;
154}
155
156
157#ifndef _CFLTCVT
158#define _CFLTCVT _cfltcvt
159#endif /* _CFLTCVT */
160
161#ifndef _CLDCVT
162#define _CLDCVT _cldcvt
163#endif /* _CLDCVT */
164
165#ifdef _MBCS
166#undef _MBCS
167#endif /* _MBCS */
168//#include <tchar.h>
169
170/* this macro defines a function which is private and as fast as possible: */
171/* for example, in C 6.0, it might be static _fastcall <type> near. */
172#define LOCAL(x) static x __cdecl
173
174/* int/long/short/pointer sizes */
175
176/* the following should be set depending on the sizes of various types */
177#if __LP64__
178 #define LONG_IS_INT 0
179 CASSERT(sizeof(long) > sizeof(int));
180#else
181 #define LONG_IS_INT 1 /* 1 means long is same size as int */
182 CASSERT(sizeof(long) == sizeof(int));
183#endif
184
185#define SHORT_IS_INT 0 /* 1 means short is same size as int */
186#define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64 */
187#if defined (_WIN64)
188 #define PTR_IS_INT 0 /* 1 means ptr is same size as int */
189 CASSERT(sizeof(void *) != sizeof(int));
190 #if __LP64__
191 #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
192 CASSERT(sizeof(void *) == sizeof(long));
193 #else
194 #define PTR_IS_LONG 0 /* 1 means ptr is same size as long */
195 CASSERT(sizeof(void *) != sizeof(long));
196 #endif
197 #define PTR_IS_INT64 1 /* 1 means ptr is same size as int64 */
198 CASSERT(sizeof(void *) == sizeof(int64_t));
199#else /* defined (_WIN64) */
200 #define PTR_IS_INT 1 /* 1 means ptr is same size as int */
201 CASSERT(sizeof(void *) == sizeof(int));
202 #define PTR_IS_LONG 1 /* 1 means ptr is same size as long */
203 CASSERT(sizeof(void *) == sizeof(long));
204 #define PTR_IS_INT64 0 /* 1 means ptr is same size as int64 */
205 CASSERT(sizeof(void *) != sizeof(int64_t));
206#endif /* defined (_WIN64) */
207
208/* CONSTANTS */
209
210/* size of conversion buffer (ANSI-specified minimum is 509) */
211
212#define BUFFERSIZE 512
213#define MAXPRECISION BUFFERSIZE
214
215#if BUFFERSIZE < _CVTBUFSIZE + 6
216/*
217 * Buffer needs to be big enough for default minimum precision
218 * when converting floating point needs bigger buffer, and malloc
219 * fails
220 */
221#error Conversion buffer too small for max double.
222#endif /* BUFFERSIZE < _CVTBUFSIZE + 6 */
223
224/* flag definitions */
225#define FL_SIGN 0x00001 /* put plus or minus in front */
226#define FL_SIGNSP 0x00002 /* put space or minus in front */
227#define FL_LEFT 0x00004 /* left justify */
228#define FL_LEADZERO 0x00008 /* pad with leading zeros */
229#define FL_LONG 0x00010 /* long value given */
230#define FL_SHORT 0x00020 /* short value given */
231#define FL_SIGNED 0x00040 /* signed data given */
232#define FL_ALTERNATE 0x00080 /* alternate form requested */
233#define FL_NEGATIVE 0x00100 /* value is negative */
234#define FL_FORCEOCTAL 0x00200 /* force leading '0' for octals */
235#define FL_LONGDOUBLE 0x00400 /* long double value given */
236#define FL_WIDECHAR 0x00800 /* wide characters */
237#define FL_LONGLONG 0x01000 /* long long value given */
238#define FL_I64 0x08000 /* __int64 value given */
239
240/* state definitions */
241enum STATE {
242 ST_NORMAL, /* normal state; outputting literal chars */
243 ST_PERCENT, /* just read '%' */
244 ST_FLAG, /* just read flag character */
245 ST_WIDTH, /* just read width specifier */
246 ST_DOT, /* just read '.' */
247 ST_PRECIS, /* just read precision specifier */
248 ST_SIZE, /* just read size specifier */
249 ST_TYPE /* just read type specifier */
250#ifdef FORMAT_VALIDATIONS
251 ,ST_INVALID /* Invalid format */
252#endif /* FORMAT_VALIDATIONS */
253
254};
255
256#ifdef FORMAT_VALIDATIONS
257#define NUMSTATES (ST_INVALID + 1)
258#else /* FORMAT_VALIDATIONS */
259#define NUMSTATES (ST_TYPE + 1)
260#endif /* FORMAT_VALIDATIONS */
261
262/* character type values */
263enum CHARTYPE {
264 CH_OTHER, /* character with no special meaning */
265 CH_PERCENT, /* '%' */
266 CH_DOT, /* '.' */
267 CH_STAR, /* '*' */
268 CH_ZERO, /* '0' */
269 CH_DIGIT, /* '1'..'9' */
270 CH_FLAG, /* ' ', '+', '-', '#' */
271 CH_SIZE, /* 'h', 'l', 'L', 'N', 'F', 'w' */
272 CH_TYPE /* type specifying character */
273};
274
275/* static data (read only, since we are re-entrant) */
276//#if defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS)
277//extern const char __nullstring[]; /* string to print on null ptr */
278//extern const wchar_t __wnullstring[]; /* string to print on null ptr */
279//#else /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
280static const char __nullstring[] = "(null)"; /* string to print on null ptr */
281static const wchar_t __wnullstring[] = {'(', 'n', 'u', 'l', 'l', ')', '\0'};/* string to print on null ptr */
282//#endif /* defined (_UNICODE) || defined (CPRFLAG) || defined (FORMAT_VALIDATIONS) */
283
284/* The state table. This table is actually two tables combined into one. */
285/* The lower nybble of each byte gives the character class of any */
286/* character; while the uper nybble of the byte gives the next state */
287/* to enter. See the macros below the table for details. */
288/* */
289/* The table is generated by maketabc.c -- use this program to make */
290/* changes. */
291
292#ifndef FORMAT_VALIDATIONS
293
294//#if defined (_UNICODE) || defined (CPRFLAG)
295//extern const char __lookuptable[];
296//#else /* defined (_UNICODE) || defined (CPRFLAG) */
297extern const char __lookuptable[] = {
298 /* ' ' */ 0x06,
299 /* '!' */ 0x00,
300 /* '"' */ 0x00,
301 /* '#' */ 0x06,
302 /* '$' */ 0x00,
303 /* '%' */ 0x01,
304 /* '&' */ 0x00,
305 /* ''' */ 0x00,
306 /* '(' */ 0x10,
307 /* ')' */ 0x00,
308 /* '*' */ 0x03,
309 /* '+' */ 0x06,
310 /* ',' */ 0x00,
311 /* '-' */ 0x06,
312 /* '.' */ 0x02,
313 /* '/' */ 0x10,
314 /* '0' */ 0x04,
315 /* '1' */ 0x45,
316 /* '2' */ 0x45,
317 /* '3' */ 0x45,
318 /* '4' */ 0x05,
319 /* '5' */ 0x05,
320 /* '6' */ 0x05,
321 /* '7' */ 0x05,
322 /* '8' */ 0x05,
323 /* '9' */ 0x35,
324 /* ':' */ 0x30,
325 /* ';' */ 0x00,
326 /* '<' */ 0x50,
327 /* '=' */ 0x00,
328 /* '>' */ 0x00,
329 /* '?' */ 0x00,
330 /* '@' */ 0x00,
331 /* 'A' */ 0x20, // Disable %A format
332 /* 'B' */ 0x20,
333 /* 'C' */ 0x38,
334 /* 'D' */ 0x50,
335 /* 'E' */ 0x58,
336 /* 'F' */ 0x07,
337 /* 'G' */ 0x08,
338 /* 'H' */ 0x00,
339 /* 'I' */ 0x37,
340 /* 'J' */ 0x30,
341 /* 'K' */ 0x30,
342 /* 'L' */ 0x57,
343 /* 'M' */ 0x50,
344 /* 'N' */ 0x07,
345 /* 'O' */ 0x00,
346 /* 'P' */ 0x00,
347 /* 'Q' */ 0x20,
348 /* 'R' */ 0x20,
349 /* 'S' */ 0x08,
350 /* 'T' */ 0x00,
351 /* 'U' */ 0x00,
352 /* 'V' */ 0x00,
353 /* 'W' */ 0x00,
354 /* 'X' */ 0x08,
355 /* 'Y' */ 0x60,
356 /* 'Z' */ 0x68,
357 /* '[' */ 0x60,
358 /* '\' */ 0x60,
359 /* ']' */ 0x60,
360 /* '^' */ 0x60,
361 /* '_' */ 0x00,
362 /* '`' */ 0x00,
363 /* 'a' */ 0x70, // Disable %a format
364 /* 'b' */ 0x70,
365 /* 'c' */ 0x78,
366 /* 'd' */ 0x78,
367 /* 'e' */ 0x78,
368 /* 'f' */ 0x78,
369 /* 'g' */ 0x08,
370 /* 'h' */ 0x07,
371 /* 'i' */ 0x08,
372 /* 'j' */ 0x00,
373 /* 'k' */ 0x00,
374 /* 'l' */ 0x07,
375 /* 'm' */ 0x00,
376 /* 'n' */ 0x00, // Disable %n format
377 /* 'o' */ 0x08,
378 /* 'p' */ 0x08,
379 /* 'q' */ 0x00,
380 /* 'r' */ 0x00,
381 /* 's' */ 0x08,
382 /* 't' */ 0x00,
383 /* 'u' */ 0x08,
384 /* 'v' */ 0x00,
385 /* 'w' */ 0x07,
386 /* 'x' */ 0x08
387};
388
389//#endif /* defined (_UNICODE) || defined (CPRFLAG) */
390
391#else /* FORMAT_VALIDATIONS */
392
393//#if defined (_UNICODE) || defined (CPRFLAG)
394//extern const unsigned char __lookuptable_s[];
395//#else /* defined (_UNICODE) || defined (CPRFLAG) */
396static const unsigned char __lookuptable_s[] = {
397 /* ' ' */ 0x06,
398 /* '!' */ 0x80,
399 /* '"' */ 0x80,
400 /* '#' */ 0x86,
401 /* '$' */ 0x80,
402 /* '%' */ 0x81,
403 /* '&' */ 0x80,
404 /* ''' */ 0x00,
405 /* '(' */ 0x00,
406 /* ')' */ 0x10,
407 /* '*' */ 0x03,
408 /* '+' */ 0x86,
409 /* ',' */ 0x80,
410 /* '-' */ 0x86,
411 /* '.' */ 0x82,
412 /* '/' */ 0x80,
413 /* '0' */ 0x14,
414 /* '1' */ 0x05,
415 /* '2' */ 0x05,
416 /* '3' */ 0x45,
417 /* '4' */ 0x45,
418 /* '5' */ 0x45,
419 /* '6' */ 0x85,
420 /* '7' */ 0x85,
421 /* '8' */ 0x85,
422 /* '9' */ 0x05,
423 /* ':' */ 0x00,
424 /* ';' */ 0x00,
425 /* '<' */ 0x30,
426 /* '=' */ 0x30,
427 /* '>' */ 0x80,
428 /* '?' */ 0x50,
429 /* '@' */ 0x80,
430 /* 'A' */ 0x80, // Disable %A format
431 /* 'B' */ 0x00,
432 /* 'C' */ 0x08,
433 /* 'D' */ 0x00,
434 /* 'E' */ 0x28,
435 /* 'F' */ 0x27,
436 /* 'G' */ 0x38,
437 /* 'H' */ 0x50,
438 /* 'I' */ 0x57,
439 /* 'J' */ 0x80,
440 /* 'K' */ 0x00,
441 /* 'L' */ 0x07,
442 /* 'M' */ 0x00,
443 /* 'N' */ 0x37,
444 /* 'O' */ 0x30,
445 /* 'P' */ 0x30,
446 /* 'Q' */ 0x50,
447 /* 'R' */ 0x50,
448 /* 'S' */ 0x88,
449 /* 'T' */ 0x00,
450 /* 'U' */ 0x00,
451 /* 'V' */ 0x00,
452 /* 'W' */ 0x20,
453 /* 'X' */ 0x28,
454 /* 'Y' */ 0x80,
455 /* 'Z' */ 0x88,
456 /* '[' */ 0x80,
457 /* '\' */ 0x80,
458 /* ']' */ 0x00,
459 /* '^' */ 0x00,
460 /* '_' */ 0x00,
461 /* '`' */ 0x60,
462 /* 'a' */ 0x60, // Disable %a format
463 /* 'b' */ 0x60,
464 /* 'c' */ 0x68,
465 /* 'd' */ 0x68,
466 /* 'e' */ 0x68,
467 /* 'f' */ 0x08,
468 /* 'g' */ 0x08,
469 /* 'h' */ 0x07,
470 /* 'i' */ 0x78,
471 /* 'j' */ 0x70,
472 /* 'k' */ 0x70,
473 /* 'l' */ 0x77,
474 /* 'm' */ 0x70,
475 /* 'n' */ 0x70,
476 /* 'o' */ 0x08,
477 /* 'p' */ 0x08,
478 /* 'q' */ 0x00,
479 /* 'r' */ 0x00,
480 /* 's' */ 0x08,
481 /* 't' */ 0x00,
482 /* 'u' */ 0x08,
483 /* 'v' */ 0x00,
484 /* 'w' */ 0x07,
485 /* 'x' */ 0x08
486};
487//#endif /* defined (_UNICODE) || defined (CPRFLAG) */
488
489#endif /* FORMAT_VALIDATIONS */
490
491#define FIND_CHAR_CLASS(lookuptbl, c) \
492 ((c) < _T(' ') || (c) > _T('x') ? \
493 CH_OTHER \
494 : \
495 (enum CHARTYPE)(lookuptbl[(c)-_T(' ')] & 0xF))
496
497#define FIND_NEXT_STATE(lookuptbl, class, state) \
498 (enum STATE)(lookuptbl[(class) * NUMSTATES + (state)] >> 4)
499
500/*
501 * Note: CPRFLAG and _UNICODE cases are currently mutually exclusive.
502 */
503
504/* prototypes */
505
506#ifdef CPRFLAG
507
508#define WRITE_CHAR(ch, pnw) write_char(ch, pnw)
509#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, pnw)
510#define WRITE_STRING(s, len, pnw) write_string(s, len, pnw)
511
512LOCAL(void) write_char(_TCHAR ch, int *pnumwritten);
513LOCAL(void) write_multi_char(_TCHAR ch, int num, int *pnumwritten);
514LOCAL(void) write_string(const _TCHAR *string, int len, int *numwritten);
515
516#else /* CPRFLAG */
517
518#define WRITE_CHAR(ch, pnw) write_char(ch, stream, pnw)
519#define WRITE_MULTI_CHAR(ch, num, pnw) write_multi_char(ch, num, stream, pnw)
520#define WRITE_STRING(s, len, pnw) write_string(s, len, stream, pnw)
521
522LOCAL(void) write_char(_TCHAR ch, miniFILE *f, int *pnumwritten);
523LOCAL(void) write_multi_char(_TCHAR ch, int num, miniFILE *f, int *pnumwritten);
524LOCAL(void) write_string(const _TCHAR *string, int len, miniFILE *f, int *numwritten);
525
526#endif /* CPRFLAG */
527
528#define get_int_arg(list) va_arg(*list, int)
529#define get_long_arg(list) va_arg(*list, long)
530#define get_long_long_arg(list) va_arg(*list, long long)
531#define get_int64_arg(list) va_arg(*list, __int64)
532#define get_crtdouble_arg(list) va_arg(*list, _CRT_DOUBLE)
533#define get_ptr_arg(list) va_arg(*list, void *)
534
535#ifdef CPRFLAG
536LOCAL(int) output(const _TCHAR *, _locale_t , va_list);
537_CRTIMP int __cdecl _vtcprintf_l (const _TCHAR *, _locale_t, va_list);
538_CRTIMP int __cdecl _vtcprintf_s_l (const _TCHAR *, _locale_t, va_list);
539_CRTIMP int __cdecl _vtcprintf_p_l (const _TCHAR *, _locale_t, va_list);
540
541
542/***
543*int _cprintf(format, arglist) - write formatted output directly to console
544*
545*Purpose:
546* Writes formatted data like printf, but uses console I/O functions.
547*
548*Entry:
549* char *format - format string to determine data formats
550* arglist - list of POINTERS to where to put data
551*
552*Exit:
553* returns number of characters written
554*
555*Exceptions:
556*
557*******************************************************************************/
558#ifndef FORMAT_VALIDATIONS
559_CRTIMP int __cdecl _tcprintf_l (
560 const _TCHAR * format,
561 _locale_t plocinfo,
562 ...
563 )
564#else /* FORMAT_VALIDATIONS */
565_CRTIMP int __cdecl _tcprintf_s_l (
566 const _TCHAR * format,
567 _locale_t plocinfo,
568 ...
569 )
570#endif /* FORMAT_VALIDATIONS */
571
572{
573 int ret;
574 va_list arglist;
575 va_start(arglist, plocinfo);
576
577#ifndef FORMAT_VALIDATIONS
578 ret = _vtcprintf_l(format, plocinfo, arglist);
579#else /* FORMAT_VALIDATIONS */
580 ret = _vtcprintf_s_l(format, plocinfo, arglist);
581
582#endif /* FORMAT_VALIDATIONS */
583
584 va_end(arglist);
585
586 return ret;
587}
588
589#ifndef FORMAT_VALIDATIONS
590_CRTIMP int __cdecl _tcprintf (
591 const _TCHAR * format,
592 ...
593 )
594#else /* FORMAT_VALIDATIONS */
595_CRTIMP int __cdecl _tcprintf_s (
596 const _TCHAR * format,
597 ...
598 )
599#endif /* FORMAT_VALIDATIONS */
600
601{
602 int ret;
603 va_list arglist;
604
605 va_start(arglist, format);
606
607#ifndef FORMAT_VALIDATIONS
608 ret = _vtcprintf_l(format, NULL, arglist);
609#else /* FORMAT_VALIDATIONS */
610 ret = _vtcprintf_s_l(format, NULL, arglist);
611
612#endif /* FORMAT_VALIDATIONS */
613
614 va_end(arglist);
615
616 return ret;
617}
618
619#endif /* CPRFLAG */
620
621
622/***
623*int _output(stream, format, argptr), static int output(format, argptr)
624*
625*Purpose:
626* Output performs printf style output onto a stream. It is called by
627* printf/fprintf/sprintf/vprintf/vfprintf/vsprintf to so the dirty
628* work. In multi-thread situations, _output assumes that the given
629* stream is already locked.
630*
631* Algorithm:
632* The format string is parsed by using a finite state automaton
633* based on the current state and the current character read from
634* the format string. Thus, looping is on a per-character basis,
635* not a per conversion specifier basis. Once the format specififying
636* character is read, output is performed.
637*
638*Entry:
639* FILE *stream - stream for output
640* char *format - printf style format string
641* va_list argptr - pointer to list of subsidiary arguments
642*
643*Exit:
644* Returns the number of characters written, or -1 if an output error
645* occurs.
646*ifdef _UNICODE
647* The wide-character flavour returns the number of wide-characters written.
648*endif
649*
650*Exceptions:
651*
652*******************************************************************************/
653#ifdef CPRFLAG
654#ifndef FORMAT_VALIDATIONS
655_CRTIMP int __cdecl _vtcprintf (
656 const _TCHAR *format,
657 va_list argptr
658 )
659{
660 return _vtcprintf_l(format, NULL, argptr);
661}
662
663#else /* FORMAT_VALIDATIONS */
664_CRTIMP int __cdecl _vtcprintf_s (
665 const _TCHAR *format,
666 va_list argptr
667 )
668{
669 return _vtcprintf_s_l(format, NULL, argptr);
670}
671
672#endif /* FORMAT_VALIDATIONS */
673#endif /* CPRFLAG */
674
675#ifdef CPRFLAG
676#ifndef FORMAT_VALIDATIONS
677_CRTIMP int __cdecl _vtcprintf_l (
678#else /* FORMAT_VALIDATIONS */
679_CRTIMP int __cdecl _vtcprintf_s_l (
680#endif /* FORMAT_VALIDATIONS */
681#else /* CPRFLAG */
682
683#ifdef _UNICODE
684#ifndef FORMAT_VALIDATIONS
685int __cdecl _woutput (
686 miniFILE *stream,
687#else /* FORMAT_VALIDATIONS */
688int __cdecl _woutput_s (
689 miniFILE *stream,
690#endif /* FORMAT_VALIDATIONS */
691#else /* _UNICODE */
692#ifndef FORMAT_VALIDATIONS
693int __cdecl _output (
694 miniFILE *stream,
695#else /* FORMAT_VALIDATIONS */
696 int __cdecl _output_s (
697 miniFILE *stream,
698
699#endif /* FORMAT_VALIDATIONS */
700#endif /* _UNICODE */
701
702#endif /* CPRFLAG */
703 const _TCHAR *format,
704 va_list argptr
705 )
706{
707 int hexadd=0; /* offset to add to number to get 'a'..'f' */
708 TCHAR ch; /* character just read */
709 int flags=0; /* flag word -- see #defines above for flag values */
710 enum STATE state; /* current state */
711 enum CHARTYPE chclass; /* class of current character */
712 int radix; /* current conversion radix */
713 int charsout; /* characters currently written so far, -1 = IO error */
714 int fldwidth = 0; /* selected field width -- 0 means default */
715 int precision = 0; /* selected precision -- -1 means default */
716 TCHAR prefix[2]; /* numeric prefix -- up to two characters */
717 int prefixlen=0; /* length of prefix -- 0 means no prefix */
718 int capexp = 0; /* non-zero = 'E' exponent signifient, zero = 'e' */
719 int no_output=0; /* non-zero = prodcue no output for this specifier */
720 union {
721 const char *sz; /* pointer text to be printed, not zero terminated */
722 const wchar_t *wz;
723 } text;
724
725 int textlen; /* length of the text in bytes/wchars to be printed.
726 textlen is in multibyte or wide chars if _UNICODE */
727 union {
728 char sz[BUFFERSIZE];
729#ifdef _UNICODE
730 wchar_t wz[BUFFERSIZE];
731#endif /* _UNICODE */
732 } buffer;
733 wchar_t wchar; /* temp wchar_t */
734 int buffersize; /* size of text.sz (used only for the call to _cfltcvt) */
735 int bufferiswide=0; /* non-zero = buffer contains wide chars already */
736
737#ifndef CPRFLAG
738 _VALIDATE_RETURN( (stream != NULL), EINVAL, -1);
739#endif /* CPRFLAG */
740 _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
741
742 charsout = 0; /* no characters written yet */
743 textlen = 0; /* no text yet */
744 state = ST_NORMAL; /* starting state */
745 buffersize = 0;
746
747 /* main loop -- loop while format character exist and no I/O errors */
748 while ((ch = *format++) != _T('\0') && charsout >= 0) {
749#ifndef FORMAT_VALIDATIONS
750 chclass = FIND_CHAR_CLASS(__lookuptable, ch); /* find character class */
751 state = FIND_NEXT_STATE(__lookuptable, chclass, state); /* find next state */
752#else /* FORMAT_VALIDATIONS */
753 chclass = FIND_CHAR_CLASS(__lookuptable_s, ch); /* find character class */
754 state = FIND_NEXT_STATE(__lookuptable_s, chclass, state); /* find next state */
755
756 _VALIDATE_RETURN((state != ST_INVALID), EINVAL, -1);
757
758#endif /* FORMAT_VALIDATIONS */
759
760 /* execute code for each state */
761 switch (state) {
762
763 case ST_NORMAL:
764
765 NORMAL_STATE:
766
767 /* normal state -- just write character */
768#ifdef _UNICODE
769 bufferiswide = 1;
770#else /* _UNICODE */
771 bufferiswide = 0;
772#endif /* _UNICODE */
773 WRITE_CHAR(ch, &charsout);
774 break;
775
776 case ST_PERCENT:
777 /* set default value of conversion parameters */
778 prefixlen = fldwidth = no_output = capexp = 0;
779 flags = 0;
780 precision = -1;
781 bufferiswide = 0; /* default */
782 break;
783
784 case ST_FLAG:
785 /* set flag based on which flag character */
786 switch (ch) {
787 case _T('-'):
788 flags |= FL_LEFT; /* '-' => left justify */
789 break;
790 case _T('+'):
791 flags |= FL_SIGN; /* '+' => force sign indicator */
792 break;
793 case _T(' '):
794 flags |= FL_SIGNSP; /* ' ' => force sign or space */
795 break;
796 case _T('#'):
797 flags |= FL_ALTERNATE; /* '#' => alternate form */
798 break;
799 case _T('0'):
800 flags |= FL_LEADZERO; /* '0' => pad with leading zeros */
801 break;
802 }
803 break;
804
805 case ST_WIDTH:
806 /* update width value */
807 if (ch == _T('*')) {
808 /* get width from arg list */
809 fldwidth = get_int_arg(&argptr);
810 if (fldwidth < 0) {
811 /* ANSI says neg fld width means '-' flag and pos width */
812 flags |= FL_LEFT;
813 fldwidth = -fldwidth;
814 }
815 }
816 else {
817 /* add digit to current field width */
818 fldwidth = fldwidth * 10 + (ch - _T('0'));
819 }
820 break;
821
822 case ST_DOT:
823 /* zero the precision, since dot with no number means 0
824 not default, according to ANSI */
825 precision = 0;
826 break;
827
828 case ST_PRECIS:
829 /* update precison value */
830 if (ch == _T('*')) {
831 /* get precision from arg list */
832 precision = get_int_arg(&argptr);
833 if (precision < 0)
834 precision = -1; /* neg precision means default */
835 }
836 else {
837 /* add digit to current precision */
838 precision = precision * 10 + (ch - _T('0'));
839 }
840 break;
841
842 case ST_SIZE:
843 /* just read a size specifier, set the flags based on it */
844 switch (ch) {
845 case _T('l'):
846 /*
847 * In order to handle the ll case, we depart from the
848 * simple deterministic state machine.
849 */
850 if (*format == _T('l'))
851 {
852 ++format;
853 flags |= FL_LONGLONG; /* 'll' => long long */
854 }
855 else
856 {
857 flags |= FL_LONG; /* 'l' => long int or wchar_t */
858 }
859 break;
860 case _T('L'):
861 if (*format == _T('p'))
862 {
863 flags |= FL_LONG;
864 }
865 break;
866
867 case _T('I'):
868 /*
869 * In order to handle the I, I32, and I64 size modifiers, we
870 * depart from the simple deterministic state machine. The
871 * code below scans for characters following the 'I',
872 * and defaults to 64 bit on WIN64 and 32 bit on WIN32
873 */
874#if PTR_IS_INT64
875 flags |= FL_I64; /* 'I' => __int64 on WIN64 systems */
876#endif /* PTR_IS_INT64 */
877 if ( (*format == _T('6')) && (*(format + 1) == _T('4')) )
878 {
879 format += 2;
880 flags |= FL_I64; /* I64 => __int64 */
881 }
882 else if ( (*format == _T('3')) && (*(format + 1) == _T('2')) )
883 {
884 format += 2;
885 flags &= ~FL_I64; /* I32 => __int32 */
886 }
887 else if ( (*format == _T('d')) ||
888 (*format == _T('i')) ||
889 (*format == _T('o')) ||
890 (*format == _T('u')) ||
891 (*format == _T('x')) ||
892 (*format == _T('X')) )
893 {
894 /*
895 * Nothing further needed. %Id (et al) is
896 * handled just like %d, except that it defaults to 64 bits
897 * on WIN64. Fall through to the next iteration.
898 */
899 }
900 else {
901 state = ST_NORMAL;
902 goto NORMAL_STATE;
903 }
904 break;
905
906 case _T('h'):
907 flags |= FL_SHORT; /* 'h' => short int or char */
908 break;
909
910 case _T('w'):
911 flags |= FL_WIDECHAR; /* 'w' => wide character */
912 break;
913
914 }
915 break;
916
917 case ST_TYPE:
918 /* we have finally read the actual type character, so we */
919 /* now format and "print" the output. We use a big switch */
920 /* statement that sets 'text' to point to the text that should */
921 /* be printed, and 'textlen' to the length of this text. */
922 /* Common code later on takes care of justifying it and */
923 /* other miscellaneous chores. Note that cases share code, */
924 /* in particular, all integer formatting is done in one place. */
925 /* Look at those funky goto statements! */
926
927 switch (ch) {
928
929 case _T('C'): /* ISO wide character */
930 if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
931#ifdef _UNICODE
932 flags |= FL_SHORT;
933#else /* _UNICODE */
934 flags |= FL_WIDECHAR; /* ISO std. */
935#endif /* _UNICODE */
936 /* fall into 'c' case */
937
938 case _T('c'): {
939 /* print a single character specified by int argument */
940#ifdef _UNICODE
941 bufferiswide = 1;
942 wchar = (wchar_t) get_int_arg(&argptr);
943 if (flags & FL_SHORT) {
944 /* format multibyte character */
945 /* this is an extension of ANSI */
946 char tempchar[2];
947 {
948 tempchar[0] = (char)(wchar & 0x00ff);
949 tempchar[1] = '\0';
950 }
951
952 if (_MBTOWC(buffer.wz,tempchar, MB_CUR_MAX) < 0)
953 {
954 /* ignore if conversion was unsuccessful */
955 no_output = 1;
956 }
957 } else {
958 buffer.wz[0] = wchar;
959 }
960 text.wz = buffer.wz;
961 textlen = 1; /* print just a single character */
962#else /* _UNICODE */
963 if (flags & (FL_LONG|FL_WIDECHAR)) {
964 wchar = (wchar_t) get_int_arg(&argptr);
965 textlen = snprintf(buffer.sz, BUFFERSIZE, "%lc", wchar);
966 if (textlen == 0)
967 {
968 no_output = 1;
969 }
970 } else {
971 /* format multibyte character */
972 /* this is an extension of ANSI */
973 unsigned short temp;
974 wchar = (wchar_t)get_int_arg(&argptr);
975 temp = (unsigned short)wchar;
976 {
977 buffer.sz[0] = (char) temp;
978 textlen = 1;
979 }
980 }
981 text.sz = buffer.sz;
982#endif /* _UNICODE */
983 }
984 break;
985
986 case _T('Z'): {
987 /* print a Counted String */
988 struct _count_string {
989 short Length;
990 short MaximumLength;
991 char *Buffer;
992 } *pstr;
993
994 pstr = (struct _count_string *)get_ptr_arg(&argptr);
995 if (pstr == NULL || pstr->Buffer == NULL) {
996 /* null ptr passed, use special string */
997 text.sz = __nullstring;
998 textlen = (int)strlen(text.sz);
999 } else {
1000 if (flags & FL_WIDECHAR) {
1001 text.wz = (wchar_t *)pstr->Buffer;
1002 textlen = pstr->Length / (int)sizeof(wchar_t);
1003 bufferiswide = 1;
1004 } else {
1005 bufferiswide = 0;
1006 text.sz = pstr->Buffer;
1007 textlen = pstr->Length;
1008 }
1009 }
1010 }
1011 break;
1012
1013 case _T('S'): /* ISO wide character string */
1014#ifndef _UNICODE
1015 if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
1016 flags |= FL_WIDECHAR;
1017#else /* _UNICODE */
1018 if (!(flags & (FL_SHORT|FL_LONG|FL_WIDECHAR)))
1019 flags |= FL_SHORT;
1020#endif /* _UNICODE */
1021
1022 case _T('s'): {
1023 /* print a string -- */
1024 /* ANSI rules on how much of string to print: */
1025 /* all if precision is default, */
1026 /* min(precision, length) if precision given. */
1027 /* prints '(null)' if a null string is passed */
1028
1029 int i;
1030 const char *p; /* temps */
1031 const wchar_t *pwch;
1032
1033 /* At this point it is tempting to use strlen(), but */
1034 /* if a precision is specified, we're not allowed to */
1035 /* scan past there, because there might be no null */
1036 /* at all. Thus, we must do our own scan. */
1037
1038 i = (precision == -1) ? INT_MAX : precision;
1039 text.sz = (char *)get_ptr_arg(&argptr);
1040
1041 /* scan for null upto i characters */
1042#ifdef _UNICODE
1043 if (flags & FL_SHORT) {
1044 if (text.sz == NULL) /* NULL passed, use special string */
1045 text.sz = __nullstring;
1046 p = text.sz;
1047 for (textlen=0; textlen<i && *p; textlen++) {
1048 ++p;
1049 }
1050 /* textlen now contains length in multibyte chars */
1051 } else {
1052 if (text.wz == NULL) /* NULL passed, use special string */
1053 text.wz = __wnullstring;
1054 bufferiswide = 1;
1055 pwch = text.wz;
1056 while (i-- && *pwch)
1057 ++pwch;
1058 textlen = (int)(pwch - text.wz); /* in wchar_ts */
1059 /* textlen now contains length in wide chars */
1060 }
1061#else /* _UNICODE */
1062 if (flags & (FL_LONG|FL_WIDECHAR)) {
1063 if (text.wz == NULL) /* NULL passed, use special string */
1064 text.wz = __wnullstring;
1065 bufferiswide = 1;
1066 pwch = text.wz;
1067 while ( i-- && *pwch )
1068 ++pwch;
1069 textlen = (int)(pwch - text.wz);
1070 /* textlen now contains length in wide chars */
1071 } else {
1072 if (text.sz == NULL) /* NULL passed, use special string */
1073 text.sz = __nullstring;
1074 p = text.sz;
1075 while (i-- && *p)
1076 ++p;
1077 textlen = (int)(p - text.sz); /* length of the string */
1078 }
1079
1080#endif /* _UNICODE */
1081 }
1082 break;
1083
1084
1085 case _T('n'): {
1086 /* write count of characters seen so far into */
1087 /* short/int/long thru ptr read from args */
1088
1089 void *p; /* temp */
1090
1091 p = get_ptr_arg(&argptr);
1092
1093 /* %n is disabled */
1094 _VALIDATE_RETURN(("'n' format specifier disabled" && 0), EINVAL, -1);
1095 break;
1096
1097 /* store chars out into short/long/int depending on flags */
1098#if !LONG_IS_INT
1099 if (flags & FL_LONG)
1100 *(long *)p = charsout;
1101 else
1102#endif /* !LONG_IS_INT */
1103
1104#if !SHORT_IS_INT
1105 if (flags & FL_SHORT)
1106 *(short *)p = (short) charsout;
1107 else
1108#endif /* !SHORT_IS_INT */
1109 *(int *)p = charsout;
1110
1111 no_output = 1; /* force no output */
1112 }
1113 break;
1114
1115 case _T('E'):
1116 case _T('G'):
1117 case _T('A'):
1118 capexp = 1; /* capitalize exponent */
1119 ch += _T('a') - _T('A'); /* convert format char to lower */
1120 /* DROP THROUGH */
1121 case _T('e'):
1122 case _T('f'):
1123 case _T('g'):
1124 case _T('a'): {
1125 /* floating point conversion -- we call cfltcvt routines */
1126 /* to do the work for us. */
1127 flags |= FL_SIGNED; /* floating point is signed conversion */
1128 text.sz = buffer.sz; /* put result in buffer */
1129 buffersize = BUFFERSIZE;
1130
1131 /* compute the precision value */
1132 if (precision < 0)
1133 precision = 6; /* default precision: 6 */
1134 else if (precision == 0 && ch == _T('g'))
1135 precision = 1; /* ANSI specified */
1136 else if (precision > MAXPRECISION)
1137 precision = MAXPRECISION;
1138
1139 if (precision > BUFFERSIZE - _CVTBUFSIZE) {
1140 /* cap precision further */
1141 precision = BUFFERSIZE - _CVTBUFSIZE;
1142 }
1143
1144 /* for safecrt, we pass along the FL_ALTERNATE flag to _safecrt_cfltcvt */
1145 if (flags & FL_ALTERNATE)
1146 {
1147 capexp |= FL_ALTERNATE;
1148 }
1149
1150 _CRT_DOUBLE tmp;
1151 tmp=va_arg(argptr, _CRT_DOUBLE);
1152 /* Note: assumes ch is in ASCII range */
1153 /* In safecrt, we provide a special version of _cfltcvt which internally calls printf (see safecrt_output_s.c) */
1154 _CFLTCVT(&tmp, buffer.sz, buffersize, (char)ch, precision, capexp);
1155
1156 /* check if result was negative, save '-' for later */
1157 /* and point to positive part (this is for '0' padding) */
1158 if (*text.sz == '-') {
1159 flags |= FL_NEGATIVE;
1160 ++text.sz;
1161 }
1162
1163 textlen = (int)strlen(text.sz); /* compute length of text */
1164 }
1165 break;
1166
1167 case _T('d'):
1168 case _T('i'):
1169 /* signed decimal output */
1170 flags |= FL_SIGNED;
1171 radix = 10;
1172 goto COMMON_INT;
1173
1174 case _T('u'):
1175 radix = 10;
1176 goto COMMON_INT;
1177
1178 case _T('p'):
1179 /* write a pointer -- this is like an integer or long */
1180 /* except we force precision to pad with zeros and */
1181 /* output in big hex. */
1182
1183 precision = 2 * sizeof(void *); /* number of hex digits needed */
1184#if PTR_IS_INT64
1185 if (flags & (FL_LONG | FL_SHORT))
1186 {
1187 /* %lp, %Lp or %hp - these print 8 hex digits*/
1188 precision = 2 * sizeof(int32_t);
1189 }
1190 else
1191 {
1192 flags |= FL_I64; /* assume we're converting an int64 */
1193 }
1194#elif !PTR_IS_INT
1195 flags |= FL_LONG; /* assume we're converting a long */
1196#endif /* !PTR_IS_INT */
1197 /* DROP THROUGH to hex formatting */
1198
1199 case _T('X'):
1200 /* unsigned upper hex output */
1201 hexadd = _T('A') - _T('9') - 1; /* set hexadd for uppercase hex */
1202 goto COMMON_HEX;
1203
1204 case _T('x'):
1205 /* unsigned lower hex output */
1206 hexadd = _T('a') - _T('9') - 1; /* set hexadd for lowercase hex */
1207 /* DROP THROUGH TO COMMON_HEX */
1208
1209 COMMON_HEX:
1210 radix = 16;
1211 if (flags & FL_ALTERNATE) {
1212 /* alternate form means '0x' prefix */
1213 prefix[0] = _T('0');
1214 prefix[1] = (TCHAR)(_T('x') - _T('a') + _T('9') + 1 + hexadd); /* 'x' or 'X' */
1215 prefixlen = 2;
1216 }
1217 goto COMMON_INT;
1218
1219 case _T('o'):
1220 /* unsigned octal output */
1221 radix = 8;
1222 if (flags & FL_ALTERNATE) {
1223 /* alternate form means force a leading 0 */
1224 flags |= FL_FORCEOCTAL;
1225 }
1226 /* DROP THROUGH to COMMON_INT */
1227
1228 COMMON_INT: {
1229 /* This is the general integer formatting routine. */
1230 /* Basically, we get an argument, make it positive */
1231 /* if necessary, and convert it according to the */
1232 /* correct radix, setting text and textlen */
1233 /* appropriately. */
1234
1235#if _INTEGRAL_MAX_BITS >= 64
1236 uint64_t number; /* number to convert */
1237 int digit; /* ascii value of digit */
1238 __int64 l; /* temp long value */
1239#else /* _INTEGRAL_MAX_BITS >= 64 */
1240 unsigned long number; /* number to convert */
1241 int digit; /* ascii value of digit */
1242 long l; /* temp long value */
1243#endif /* _INTEGRAL_MAX_BITS >= 64 */
1244
1245 /* 1. read argument into l, sign extend as needed */
1246#if _INTEGRAL_MAX_BITS >= 64
1247 if (flags & FL_I64)
1248 l = get_int64_arg(&argptr);
1249 else
1250#endif /* _INTEGRAL_MAX_BITS >= 64 */
1251
1252 if (flags & FL_LONGLONG)
1253 l = get_long_long_arg(&argptr);
1254
1255 else
1256
1257#if !LONG_IS_INT
1258 if (flags & FL_LONG)
1259 l = get_long_arg(&argptr);
1260 else
1261#endif /* !LONG_IS_INT */
1262
1263#if !SHORT_IS_INT
1264 if (flags & FL_SHORT) {
1265 if (flags & FL_SIGNED)
1266 l = (short) get_int_arg(&argptr); /* sign extend */
1267 else
1268 l = (unsigned short) get_int_arg(&argptr); /* zero-extend*/
1269
1270 } else
1271#endif /* !SHORT_IS_INT */
1272 {
1273 if (flags & FL_SIGNED)
1274 l = get_int_arg(&argptr); /* sign extend */
1275 else
1276 l = (unsigned int) get_int_arg(&argptr); /* zero-extend*/
1277 }
1278
1279 /* 2. check for negative; copy into number */
1280 if ( (flags & FL_SIGNED) && l < 0) {
1281 number = -l;
1282 flags |= FL_NEGATIVE; /* remember negative sign */
1283 } else {
1284 number = l;
1285 }
1286
1287#if _INTEGRAL_MAX_BITS >= 64
1288 if ( (flags & FL_I64) == 0 && (flags & FL_LONGLONG) == 0 ) {
1289 /*
1290 * Unless printing a full 64-bit value, insure values
1291 * here are not in cananical longword format to prevent
1292 * the sign extended upper 32-bits from being printed.
1293 */
1294 number &= 0xffffffff;
1295 }
1296#endif /* _INTEGRAL_MAX_BITS >= 64 */
1297
1298 /* 3. check precision value for default; non-default */
1299 /* turns off 0 flag, according to ANSI. */
1300 if (precision < 0)
1301 precision = 1; /* default precision */
1302 else {
1303 flags &= ~FL_LEADZERO;
1304 if (precision > MAXPRECISION)
1305 precision = MAXPRECISION;
1306 }
1307
1308 /* 4. Check if data is 0; if so, turn off hex prefix */
1309 if (number == 0)
1310 prefixlen = 0;
1311
1312 /* 5. Convert data to ASCII -- note if precision is zero */
1313 /* and number is zero, we get no digits at all. */
1314
1315 char *sz;
1316 sz = &buffer.sz[BUFFERSIZE-1]; /* last digit at end of buffer */
1317
1318 while (precision-- > 0 || number != 0) {
1319 digit = (int)(number % radix) + '0';
1320 number /= radix; /* reduce number */
1321 if (digit > '9') {
1322 /* a hex digit, make it a letter */
1323 digit += hexadd;
1324 }
1325 *sz-- = (char)digit; /* store the digit */
1326 }
1327
1328 textlen = (int)((char *)&buffer.sz[BUFFERSIZE-1] - sz); /* compute length of number */
1329 ++sz; /* text points to first digit now */
1330
1331
1332 /* 6. Force a leading zero if FORCEOCTAL flag set */
1333 if ((flags & FL_FORCEOCTAL) && (textlen == 0 || sz[0] != '0')) {
1334 *--sz = '0';
1335 ++textlen; /* add a zero */
1336 }
1337
1338 text.sz = sz;
1339 }
1340 break;
1341 }
1342
1343
1344 /* At this point, we have done the specific conversion, and */
1345 /* 'text' points to text to print; 'textlen' is length. Now we */
1346 /* justify it, put on prefixes, leading zeros, and then */
1347 /* print it. */
1348
1349 if (!no_output) {
1350 int padding; /* amount of padding, negative means zero */
1351
1352 if (flags & FL_SIGNED) {
1353 if (flags & FL_NEGATIVE) {
1354 /* prefix is a '-' */
1355 prefix[0] = _T('-');
1356 prefixlen = 1;
1357 }
1358 else if (flags & FL_SIGN) {
1359 /* prefix is '+' */
1360 prefix[0] = _T('+');
1361 prefixlen = 1;
1362 }
1363 else if (flags & FL_SIGNSP) {
1364 /* prefix is ' ' */
1365 prefix[0] = _T(' ');
1366 prefixlen = 1;
1367 }
1368 }
1369
1370 /* calculate amount of padding -- might be negative, */
1371 /* but this will just mean zero */
1372 padding = fldwidth - textlen - prefixlen;
1373
1374 /* put out the padding, prefix, and text, in the correct order */
1375
1376 if (!(flags & (FL_LEFT | FL_LEADZERO))) {
1377 /* pad on left with blanks */
1378 WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
1379 }
1380
1381 /* write prefix */
1382 WRITE_STRING(prefix, prefixlen, &charsout);
1383
1384 if ((flags & FL_LEADZERO) && !(flags & FL_LEFT)) {
1385 /* write leading zeros */
1386 WRITE_MULTI_CHAR(_T('0'), padding, &charsout);
1387 }
1388
1389 /* write text */
1390#ifndef _UNICODE
1391 if (bufferiswide && (textlen > 0)) {
1392 const WCHAR *p;
1393 int mbCharCount;
1394 int count;
1395 char mbStr[5];
1396
1397 p = text.wz;
1398 count = textlen;
1399 while (count-- > 0) {
1400 mbCharCount = snprintf(mbStr, sizeof(mbStr), "%lc", *p);
1401 if (mbCharCount == 0) {
1402 charsout = -1;
1403 break;
1404 }
1405 WRITE_STRING(mbStr, mbCharCount, &charsout);
1406 p++;
1407 }
1408 } else {
1409 WRITE_STRING(text.sz, textlen, &charsout);
1410 }
1411#else /* _UNICODE */
1412 if (!bufferiswide && textlen > 0) {
1413 const char *p;
1414 int retval = 0;
1415 int count;
1416
1417 p = text.sz;
1418 count = textlen;
1419 while (count-- > 0) {
1420 retval = _MBTOWC(&wchar, p, MB_CUR_MAX);
1421 if (retval <= 0) {
1422 charsout = -1;
1423 break;
1424 }
1425 WRITE_CHAR(wchar, &charsout);
1426 p += retval;
1427 }
1428 } else {
1429 WRITE_STRING(text.wz, textlen, &charsout);
1430 }
1431#endif /* _UNICODE */
1432
1433 if (charsout >= 0 && (flags & FL_LEFT)) {
1434 /* pad on right with blanks */
1435 WRITE_MULTI_CHAR(_T(' '), padding, &charsout);
1436 }
1437
1438 /* we're done! */
1439 }
1440 break;
1441 case ST_INVALID:
1442 _VALIDATE_RETURN(0 /* FALSE */, EINVAL, -1);
1443 break;
1444 }
1445 }
1446
1447#ifdef FORMAT_VALIDATIONS
1448 /* The format string shouldn't be incomplete - i.e. when we are finished
1449 with the format string, the last thing we should have encountered
1450 should have been a regular char to be output or a type specifier. Else
1451 the format string was incomplete */
1452 _VALIDATE_RETURN(((state == ST_NORMAL) || (state == ST_TYPE)), EINVAL, -1);
1453#endif /* FORMAT_VALIDATIONS */
1454
1455 return charsout; /* return value = number of characters written */
1456}
1457
1458/*
1459 * Future Optimizations for swprintf:
1460 * - Don't free the memory used for converting the buffer to wide chars.
1461 * Use realloc if the memory is not sufficient. Free it at the end.
1462 */
1463
1464/***
1465*void write_char(char ch, int *pnumwritten)
1466*ifdef _UNICODE
1467*void write_char(wchar_t ch, FILE *f, int *pnumwritten)
1468*endif
1469*void write_char(char ch, FILE *f, int *pnumwritten)
1470*
1471*Purpose:
1472* Writes a single character to the given file/console. If no error occurs,
1473* then *pnumwritten is incremented; otherwise, *pnumwritten is set
1474* to -1.
1475*
1476*Entry:
1477* _TCHAR ch - character to write
1478* FILE *f - file to write to
1479* int *pnumwritten - pointer to integer to update with total chars written
1480*
1481*Exit:
1482* No return value.
1483*
1484*Exceptions:
1485*
1486*******************************************************************************/
1487
1488#ifdef CPRFLAG
1489
1490LOCAL(void) write_char (
1491 _TCHAR ch,
1492 int *pnumwritten
1493 )
1494{
1495#ifdef _UNICODE
1496 if (_putwch_nolock(ch) == WEOF)
1497#else /* _UNICODE */
1498 if (_putch_nolock(ch) == EOF)
1499#endif /* _UNICODE */
1500 *pnumwritten = -1;
1501 else
1502 ++(*pnumwritten);
1503}
1504
1505#else /* CPRFLAG */
1506
1507LOCAL(void) write_char (
1508 _TCHAR ch,
1509 miniFILE *f,
1510 int *pnumwritten
1511 )
1512{
1513 if ( (f->_flag & _IOSTRG) && f->_base == NULL)
1514 {
1515 ++(*pnumwritten);
1516 return;
1517 }
1518#ifdef _UNICODE
1519 if (_putwc_nolock(ch, f) == WEOF)
1520#else /* _UNICODE */
1521 if (_putc_nolock(ch, f) == EOF)
1522#endif /* _UNICODE */
1523 *pnumwritten = -1;
1524 else
1525 ++(*pnumwritten);
1526}
1527
1528#endif /* CPRFLAG */
1529
1530/***
1531*void write_multi_char(char ch, int num, int *pnumwritten)
1532*ifdef _UNICODE
1533*void write_multi_char(wchar_t ch, int num, FILE *f, int *pnumwritten)
1534*endif
1535*void write_multi_char(char ch, int num, FILE *f, int *pnumwritten)
1536*
1537*Purpose:
1538* Writes num copies of a character to the given file/console. If no error occurs,
1539* then *pnumwritten is incremented by num; otherwise, *pnumwritten is set
1540* to -1. If num is negative, it is treated as zero.
1541*
1542*Entry:
1543* _TCHAR ch - character to write
1544* int num - number of times to write the characters
1545* FILE *f - file to write to
1546* int *pnumwritten - pointer to integer to update with total chars written
1547*
1548*Exit:
1549* No return value.
1550*
1551*Exceptions:
1552*
1553*******************************************************************************/
1554
1555#ifdef CPRFLAG
1556LOCAL(void) write_multi_char (
1557 _TCHAR ch,
1558 int num,
1559 int *pnumwritten
1560 )
1561{
1562 while (num-- > 0) {
1563 write_char(ch, pnumwritten);
1564 if (*pnumwritten == -1)
1565 break;
1566 }
1567}
1568
1569#else /* CPRFLAG */
1570
1571LOCAL(void) write_multi_char (
1572 _TCHAR ch,
1573 int num,
1574 miniFILE *f,
1575 int *pnumwritten
1576 )
1577{
1578 while (num-- > 0) {
1579 write_char(ch, f, pnumwritten);
1580 if (*pnumwritten == -1)
1581 break;
1582 }
1583}
1584
1585#endif /* CPRFLAG */
1586
1587/***
1588*void write_string(const char *string, int len, int *pnumwritten)
1589*void write_string(const char *string, int len, FILE *f, int *pnumwritten)
1590*ifdef _UNICODE
1591*void write_string(const wchar_t *string, int len, FILE *f, int *pnumwritten)
1592*endif
1593*
1594*Purpose:
1595* Writes a string of the given length to the given file. If no error occurs,
1596* then *pnumwritten is incremented by len; otherwise, *pnumwritten is set
1597* to -1. If len is negative, it is treated as zero.
1598*
1599*Entry:
1600* _TCHAR *string - string to write (NOT null-terminated)
1601* int len - length of string
1602* FILE *f - file to write to
1603* int *pnumwritten - pointer to integer to update with total chars written
1604*
1605*Exit:
1606* No return value.
1607*
1608*Exceptions:
1609*
1610*******************************************************************************/
1611
1612#ifdef CPRFLAG
1613
1614LOCAL(void) write_string (
1615 const _TCHAR *string,
1616 int len,
1617 int *pnumwritten
1618 )
1619{
1620 while (len-- > 0) {
1621 write_char(*string++, pnumwritten);
1622 if (*pnumwritten == -1)
1623 {
1624 if (errno == EILSEQ)
1625 write_char(_T('?'), pnumwritten);
1626 else
1627 break;
1628 }
1629 }
1630}
1631
1632#else /* CPRFLAG */
1633
1634LOCAL(void) write_string (
1635 const _TCHAR *string,
1636 int len,
1637 miniFILE *f,
1638 int *pnumwritten
1639 )
1640{
1641 if ( (f->_flag & _IOSTRG) && f->_base == NULL)
1642 {
1643 (*pnumwritten) += len;
1644 return;
1645 }
1646 while (len-- > 0) {
1647 write_char(*string++, f, pnumwritten);
1648 if (*pnumwritten == -1)
1649 {
1650 if (errno == EILSEQ)
1651 write_char(_T('?'), f, pnumwritten);
1652 else
1653 break;
1654 }
1655 }
1656}
1657#endif /* CPRFLAG */
1658