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