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 | |
23 | typedef int (*WOUTPUTFN)(miniFILE *, const wchar_t *, va_list); |
24 | |
25 | static 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 | |
67 | int __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 | |
115 | int __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 | |
146 | int __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 | |