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*vswprint.c - print formatted data into a string from var arg list
7*
8*Purpose:
9* defines vswprintf_s() and _vsnwprintf_s() - print formatted output to
10* a string, get the data from an argument ptr instead of explicit
11* arguments.
12*
13*******************************************************************************/
14
15
16#include <string.h>
17#include <errno.h>
18#include <limits.h>
19#include "internal_securecrt.h"
20
21#include "mbusafecrt_internal.h"
22
23typedef int (*WOUTPUTFN)(miniFILE *, const wchar_t *, va_list);
24
25static int _vswprintf_helper( WOUTPUTFN outfn, wchar_t *string, size_t count, const wchar_t *format, va_list ap );
26
27/***
28*int vswprintf_s(string, sizeInWords, format, ap) - print formatted data to string from arg ptr
29*int _vsnwprintf_s(string, sizeInWords, cnt, format, ap) - print formatted data to string from arg ptr
30*Purpose:
31* Prints formatted data, but to a string and gets data from an argument
32* pointer.
33* Sets up a FILE so file i/o operations can be used, make string look
34* like a huge buffer to it, but _flsbuf will refuse to flush it if it
35* fills up. Appends '\0' to make it a true string.
36*
37* Allocate the 'fake' _iob[] entryit statically instead of on
38* the stack so that other routines can assume that _iob[] entries are in
39* are in DGROUP and, thus, are near.
40*
41* The _vsnwprintf_s() flavor takes a count argument that is
42* the max number of bytes that should be written to the
43* user's buffer.
44* We don't expose this function directly in the headers.
45*
46* Multi-thread: (1) Since there is no stream, this routine must never try
47* to get the stream lock (i.e., there is no stream lock either). (2)
48* Also, since there is only one statically allocated 'fake' iob, we must
49* lock/unlock to prevent collisions.
50*
51*Entry:
52* wchar_t *string - place to put destination string
53* size_t sizeInWords - size of the string buffer in wchar_t units
54* size_t count - max number of bytes to put in buffer
55* wchar_t *format - format string, describes format of data
56* va_list ap - varargs argument pointer
57*
58*Exit:
59* returns number of wide characters in string
60* returns -2 if the string has been truncated (only in _vsnprintf_helper)
61* returns -1 in other error cases
62*
63*Exceptions:
64*
65*******************************************************************************/
66
67int __cdecl _vswprintf_helper (
68 WOUTPUTFN woutfn,
69 wchar_t *string,
70 size_t count,
71 const wchar_t *format,
72 va_list ap
73 )
74{
75 miniFILE str;
76 miniFILE *outfile = &str;
77 int retval;
78
79 _VALIDATE_RETURN( (format != NULL), EINVAL, -1);
80
81 _VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 );
82
83 outfile->_flag = _IOWRT|_IOSTRG;
84 outfile->_ptr = outfile->_base = (char *) string;
85
86 if(count>(INT_MAX/sizeof(wchar_t)))
87 {
88 /* old-style functions allow any large value to mean unbounded */
89 outfile->_cnt = INT_MAX;
90 }
91 else
92 {
93 outfile->_cnt = (int)(count*sizeof(wchar_t));
94 }
95
96 retval = woutfn(outfile, format, ap );
97
98 if(string==NULL)
99 {
100 return retval;
101 }
102
103 if((retval >= 0) && (_putc_nolock('\0',outfile) != EOF) && (_putc_nolock('\0',outfile) != EOF))
104 return(retval);
105
106 string[count - 1] = 0;
107 if (outfile->_cnt < 0)
108 {
109 /* the buffer was too small; we return -2 to indicate truncation */
110 return -2;
111 }
112 return -1;
113}
114
115int __cdecl vswprintf_s (
116 wchar_t *string,
117 size_t sizeInWords,
118 const wchar_t *format,
119 va_list ap
120 )
121{
122 int retvalue = -1;
123
124 /* validation section */
125 _VALIDATE_RETURN(format != NULL, EINVAL, -1);
126 _VALIDATE_RETURN(string != NULL && sizeInWords > 0, EINVAL, -1);
127
128 retvalue = _vswprintf_helper(_woutput_s, string, sizeInWords, format, ap);
129 if (retvalue < 0)
130 {
131 string[0] = 0;
132 _SECURECRT__FILL_STRING(string, sizeInWords, 1);
133 }
134 if (retvalue == -2)
135 {
136 _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
137 }
138 if (retvalue >= 0)
139 {
140 _SECURECRT__FILL_STRING(string, sizeInWords, retvalue + 1);
141 }
142
143 return retvalue;
144}
145
146int __cdecl _vsnwprintf_s (
147 wchar_t *string,
148 size_t sizeInWords,
149 size_t count,
150 const wchar_t *format,
151 va_list ap
152 )
153{
154 int retvalue = -1;
155 errno_t save_errno = 0;
156
157 /* validation section */
158 _VALIDATE_RETURN(format != NULL, EINVAL, -1);
159 if (count == 0 && string == NULL && sizeInWords == 0)
160 {
161 /* this case is allowed; nothing to do */
162 return 0;
163 }
164 _VALIDATE_RETURN(string != NULL && sizeInWords > 0, EINVAL, -1);
165
166 if (sizeInWords > count)
167 {
168 save_errno = errno;
169 retvalue = _vswprintf_helper(_woutput_s, string, count + 1, format, ap);
170 if (retvalue == -2)
171 {
172 /* the string has been truncated, return -1 */
173 _SECURECRT__FILL_STRING(string, sizeInWords, count + 1);
174 if (errno == ERANGE)
175 {
176 errno = save_errno;
177 }
178 return -1;
179 }
180 }
181 else /* sizeInWords <= count */
182 {
183 save_errno = errno;
184 retvalue = _vswprintf_helper(_woutput_s, string, sizeInWords, format, ap);
185 string[sizeInWords - 1] = 0;
186 /* we allow truncation if count == _TRUNCATE */
187 if (retvalue == -2 && count == _TRUNCATE)
188 {
189 if (errno == ERANGE)
190 {
191 errno = save_errno;
192 }
193 return -1;
194 }
195 }
196
197 if (retvalue < 0)
198 {
199 string[0] = 0;
200 _SECURECRT__FILL_STRING(string, sizeInWords, 1);
201 if (retvalue == -2)
202 {
203 _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1);
204 }
205 return -1;
206 }
207
208 _SECURECRT__FILL_STRING(string, sizeInWords, retvalue + 1);
209
210 return (retvalue < 0 ? -1 : retvalue);
211}
212