| // |
| // Copyright (c) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| // |
| |
| /*** |
| *vsprintf.c - print formatted data into a string from var arg list |
| * |
| |
| * |
| *Purpose: |
| * defines vsprintf(), _vsnprintf() and _vsnprintf_s() - print formatted output to |
| * a string, get the data from an argument ptr instead of explicit |
| * arguments. |
| * |
| *******************************************************************************/ |
| |
| #include <string.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include "internal_securecrt.h" |
| |
| #include "mbusafecrt_internal.h" |
| |
| typedef int (*OUTPUTFN)(miniFILE *, const char *, va_list); |
| |
| static int _vsnprintf_helper( OUTPUTFN outfn, char *string, size_t count, const char *format, va_list ap ); |
| |
| /*** |
| *ifndef _COUNT_ |
| *int vsprintf(string, format, ap) - print formatted data to string from arg ptr |
| *else |
| *int _vsnprintf(string, cnt, format, ap) - print formatted data to string from arg ptr |
| *endif |
| * |
| *Purpose: |
| * Prints formatted data, but to a string and gets data from an argument |
| * pointer. |
| * Sets up a FILE so file i/o operations can be used, make string look |
| * like a huge buffer to it, but _flsbuf will refuse to flush it if it |
| * fills up. Appends '\0' to make it a true string. |
| * |
| * Allocate the 'fake' _iob[] entryit statically instead of on |
| * the stack so that other routines can assume that _iob[] entries are in |
| * are in DGROUP and, thus, are near. |
| * |
| *ifdef _COUNT_ |
| * The _vsnprintf() flavor takes a count argument that is |
| * the max number of bytes that should be written to the |
| * user's buffer. |
| *endif |
| * |
| * Multi-thread: (1) Since there is no stream, this routine must never try |
| * to get the stream lock (i.e., there is no stream lock either). (2) |
| * Also, since there is only one staticly allocated 'fake' iob, we must |
| * lock/unlock to prevent collisions. |
| * |
| *Entry: |
| * char *string - place to put destination string |
| *ifdef _COUNT_ |
| * size_t count - max number of bytes to put in buffer |
| *endif |
| * char *format - format string, describes format of data |
| * va_list ap - varargs argument pointer |
| * |
| *Exit: |
| * returns number of characters in string |
| * returns -2 if the string has been truncated (only in _vsnprintf_helper) |
| * returns -1 in other error cases |
| * |
| *Exceptions: |
| * |
| *******************************************************************************/ |
| |
| int __cdecl _vsnprintf_helper ( |
| OUTPUTFN outfn, |
| char *string, |
| size_t count, |
| const char *format, |
| va_list ap |
| ) |
| { |
| miniFILE str; |
| miniFILE *outfile = &str; |
| int retval; |
| |
| _VALIDATE_RETURN( (format != NULL), EINVAL, -1); |
| |
| _VALIDATE_RETURN( (count == 0) || (string != NULL), EINVAL, -1 ); |
| |
| if(count>INT_MAX) |
| { |
| /* old-style functions allow any large value to mean unbounded */ |
| outfile->_cnt = INT_MAX; |
| } |
| else |
| { |
| outfile->_cnt = (int)count; |
| } |
| |
| outfile->_flag = _IOWRT|_IOSTRG; |
| outfile->_ptr = outfile->_base = string; |
| |
| retval = outfn(outfile, format, ap ); |
| |
| if ( string==NULL) |
| return(retval); |
| |
| if((retval >= 0) && (_putc_nolock('\0',outfile) != EOF)) |
| return(retval); |
| |
| string[count - 1] = 0; |
| |
| if (outfile->_cnt < 0) |
| { |
| /* the buffer was too small; we return -2 to indicate truncation */ |
| return -2; |
| } |
| return -1; |
| } |
| |
| int __cdecl _vsprintf_s ( |
| char *string, |
| size_t sizeInBytes, |
| const char *format, |
| va_list ap |
| ) |
| { |
| int retvalue = -1; |
| |
| /* validation section */ |
| _VALIDATE_RETURN(format != NULL, EINVAL, -1); |
| _VALIDATE_RETURN(string != NULL && sizeInBytes > 0, EINVAL, -1); |
| |
| retvalue = _vsnprintf_helper(_output_s, string, sizeInBytes, format, ap); |
| if (retvalue < 0) |
| { |
| string[0] = 0; |
| _SECURECRT__FILL_STRING(string, sizeInBytes, 1); |
| } |
| if (retvalue == -2) |
| { |
| _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1); |
| } |
| if (retvalue >= 0) |
| { |
| _SECURECRT__FILL_STRING(string, sizeInBytes, retvalue + 1); |
| } |
| |
| return retvalue; |
| } |
| |
| int __cdecl vsprintf_s(char *_Dst, size_t _SizeInBytes, const char *_Format, va_list _ArgList) |
| { |
| return _vsprintf_s(_Dst, _SizeInBytes, _Format, _ArgList); |
| } |
| |
| int __cdecl _vsnprintf_s ( |
| char *string, |
| size_t sizeInBytes, |
| size_t count, |
| const char *format, |
| va_list ap |
| ) |
| { |
| int retvalue = -1; |
| errno_t save_errno = 0; |
| |
| /* validation section */ |
| _VALIDATE_RETURN(format != NULL, EINVAL, -1); |
| if (count == 0 && string == NULL && sizeInBytes == 0) |
| { |
| /* this case is allowed; nothing to do */ |
| return 0; |
| } |
| _VALIDATE_RETURN(string != NULL && sizeInBytes > 0, EINVAL, -1); |
| |
| if (sizeInBytes > count) |
| { |
| save_errno = errno; |
| retvalue = _vsnprintf_helper(_output_s, string, count + 1, format, ap); |
| if (retvalue == -2) |
| { |
| /* the string has been truncated, return -1 */ |
| _SECURECRT__FILL_STRING(string, sizeInBytes, count + 1); |
| if (errno == ERANGE) |
| { |
| errno = save_errno; |
| } |
| return -1; |
| } |
| } |
| else /* sizeInBytes <= count */ |
| { |
| save_errno = errno; |
| retvalue = _vsnprintf_helper(_output_s, string, sizeInBytes, format, ap); |
| string[sizeInBytes - 1] = 0; |
| /* we allow truncation if count == _TRUNCATE */ |
| if (retvalue == -2 && count == _TRUNCATE) |
| { |
| if (errno == ERANGE) |
| { |
| errno = save_errno; |
| } |
| return -1; |
| } |
| } |
| |
| if (retvalue < 0) |
| { |
| string[0] = 0; |
| _SECURECRT__FILL_STRING(string, sizeInBytes, 1); |
| if (retvalue == -2) |
| { |
| _VALIDATE_RETURN(("Buffer too small" && 0), ERANGE, -1); |
| } |
| return -1; |
| } |
| |
| _SECURECRT__FILL_STRING(string, sizeInBytes, retvalue + 1); |
| |
| return (retvalue < 0 ? -1 : retvalue); |
| } |