LCOV - code coverage report
Current view: top level - Python - mysnprintf.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 11 13 84.6 %
Date: 2017-04-19 Functions: 2 2 100.0 %

          Line data    Source code
       1             : #include "Python.h"
       2             : #include <ctype.h>
       3             : 
       4             : /* snprintf() wrappers.  If the platform has vsnprintf, we use it, else we
       5             :    emulate it in a half-hearted way.  Even if the platform has it, we wrap
       6             :    it because platforms differ in what vsnprintf does in case the buffer
       7             :    is too small:  C99 behavior is to return the number of characters that
       8             :    would have been written had the buffer not been too small, and to set
       9             :    the last byte of the buffer to \0.  At least MS _vsnprintf returns a
      10             :    negative value instead, and fills the entire buffer with non-\0 data.
      11             : 
      12             :    The wrappers ensure that str[size-1] is always \0 upon return.
      13             : 
      14             :    PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
      15             :    (including the trailing '\0') into str.
      16             : 
      17             :    If the platform doesn't have vsnprintf, and the buffer size needed to
      18             :    avoid truncation exceeds size by more than 512, Python aborts with a
      19             :    Py_FatalError.
      20             : 
      21             :    Return value (rv):
      22             : 
      23             :     When 0 <= rv < size, the output conversion was unexceptional, and
      24             :     rv characters were written to str (excluding a trailing \0 byte at
      25             :     str[rv]).
      26             : 
      27             :     When rv >= size, output conversion was truncated, and a buffer of
      28             :     size rv+1 would have been needed to avoid truncation.  str[size-1]
      29             :     is \0 in this case.
      30             : 
      31             :     When rv < 0, "something bad happened".  str[size-1] is \0 in this
      32             :     case too, but the rest of str is unreliable.  It could be that
      33             :     an error in format codes was detected by libc, or on platforms
      34             :     with a non-C99 vsnprintf simply that the buffer wasn't big enough
      35             :     to avoid truncation, or on platforms without any vsnprintf that
      36             :     PyMem_Malloc couldn't obtain space for a temp buffer.
      37             : 
      38             :    CAUTION:  Unlike C99, str != NULL and size > 0 are required.
      39             : */
      40             : 
      41             : int
      42          62 : PyOS_snprintf(char *str, size_t size, const  char  *format, ...)
      43             : {
      44             :     int rc;
      45             :     va_list va;
      46             : 
      47          62 :     va_start(va, format);
      48          62 :     rc = PyOS_vsnprintf(str, size, format, va);
      49          62 :     va_end(va);
      50          62 :     return rc;
      51             : }
      52             : 
      53             : int
      54          62 : PyOS_vsnprintf(char *str, size_t size, const char  *format, va_list va)
      55             : {
      56             :     int len;  /* # bytes written, excluding \0 */
      57             : #ifdef HAVE_SNPRINTF
      58             : #define _PyOS_vsnprintf_EXTRA_SPACE 1
      59             : #else
      60             : #define _PyOS_vsnprintf_EXTRA_SPACE 512
      61             :     char *buffer;
      62             : #endif
      63             :     assert(str != NULL);
      64             :     assert(size > 0);
      65             :     assert(format != NULL);
      66             :     /* We take a size_t as input but return an int.  Sanity check
      67             :      * our input so that it won't cause an overflow in the
      68             :      * vsnprintf return value or the buffer malloc size.  */
      69          62 :     if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) {
      70           0 :         len = -666;
      71           0 :         goto Done;
      72             :     }
      73             : 
      74             : #ifdef HAVE_SNPRINTF
      75          62 :     len = vsnprintf(str, size, format, va);
      76             : #else
      77             :     /* Emulate it. */
      78             :     buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
      79             :     if (buffer == NULL) {
      80             :         len = -666;
      81             :         goto Done;
      82             :     }
      83             : 
      84             :     len = vsprintf(buffer, format, va);
      85             :     if (len < 0)
      86             :         /* ignore the error */;
      87             : 
      88             :     else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE)
      89             :         Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
      90             : 
      91             :     else {
      92             :         const size_t to_copy = (size_t)len < size ?
      93             :                                 (size_t)len : size - 1;
      94             :         assert(to_copy < size);
      95             :         memcpy(str, buffer, to_copy);
      96             :         str[to_copy] = '\0';
      97             :     }
      98             :     PyMem_FREE(buffer);
      99             : #endif
     100             : Done:
     101          62 :     if (size > 0)
     102          62 :         str[size-1] = '\0';
     103          62 :     return len;
     104             : #undef _PyOS_vsnprintf_EXTRA_SPACE
     105             : }

Generated by: LCOV version 1.10