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