[Python-checkins] CVS: python/dist/src/Python getargs.c,2.89,2.90 mysnprintf.c,2.3,2.4 sysmodule.c,2.97,2.98

Tim Peters tim_one@users.sourceforge.net
Sun, 02 Dec 2001 16:43:35 -0800


Update of /cvsroot/python/python/dist/src/Python
In directory usw-pr-cvs1:/tmp/cvs-serv19336/python/Python

Modified Files:
	getargs.c mysnprintf.c sysmodule.c 
Log Message:
mysnprintf.c:  Massive rewrite of PyOS_snprintf and PyOS_vsnprintf, to
use wrappers on all platforms, to make this as consistent as possible x-
platform (in particular, make sure there's at least one \0 byte in
the output buffer).  Also document more of the truth about what these do.

getargs.c, seterror():  Three computations of remaining buffer size were
backwards, thus telling PyOS_snprintf the buffer is larger than it
actually is.  This matters a lot now that PyOS_snprintf ensures there's a
trailing \0 byte (because it didn't get the truth about the buffer size,
it was storing \0 beyond the true end of the buffer).

sysmodule.c, mywrite():  Simplify, now that PyOS_vsnprintf guarantees to
produce a \0 byte.


Index: getargs.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/getargs.c,v
retrieving revision 2.89
retrieving revision 2.90
diff -C2 -d -r2.89 -r2.90
*** getargs.c	2001/11/29 03:26:37	2.89
--- getargs.c	2001/12/03 00:43:33	2.90
***************
*** 232,236 ****
  		}
  		if (iarg != 0) {
! 			PyOS_snprintf(p, sizeof(buf) - (buf - p),
  				      "argument %d", iarg);
  			i = 0;
--- 232,236 ----
  		}
  		if (iarg != 0) {
! 			PyOS_snprintf(p, sizeof(buf) - (p - buf),
  				      "argument %d", iarg);
  			i = 0;
***************
*** 244,251 ****
  		}
  		else {
! 			PyOS_snprintf(p, sizeof(buf) - (buf - p), "argument");
  			p += strlen(p);
  		}
! 		PyOS_snprintf(p, sizeof(buf) - (buf - p), " %.256s", msg);
  		message = buf;
  	}
--- 244,251 ----
  		}
  		else {
! 			PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument");
  			p += strlen(p);
  		}
! 		PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg);
  		message = buf;
  	}

Index: mysnprintf.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/mysnprintf.c,v
retrieving revision 2.3
retrieving revision 2.4
diff -C2 -d -r2.3 -r2.4
*** mysnprintf.c	2001/12/01 16:00:10	2.3
--- mysnprintf.c	2001/12/03 00:43:33	2.4
***************
*** 1,97 ****
- 
  #include "Python.h"
  
! /* snprintf() emulation for platforms which don't have it (yet). 
!    
!    Return value
  
!        The number of characters printed (not including the trailing
!        `\0' used to end output to strings) or a negative number in
!        case of an error.
  
!        PyOS_snprintf and PyOS_vsnprintf do not write more than size
!        bytes (including the trailing '\0').
  
!        If the output would have been truncated, they return the number
!        of characters (excluding the trailing '\0') which would have
!        been written to the final string if enough space had been
!        available. This is inline with the C99 standard.
  
! */
  
! #include <ctype.h>
  
! #ifndef HAVE_SNPRINTF
  
! static
! int myvsnprintf(char *str, size_t size, const char  *format, va_list va)
! {
!     char *buffer = PyMem_Malloc(size + 512);
!     int len;
!     
!     if (buffer == NULL)
! 	return -1;
!     len = vsprintf(buffer, format, va);
!     if (len < 0) {
! 	PyMem_Free(buffer);
! 	return len;
!     }
!     len++;
!     assert(len >= 0);
!     if ((size_t)len > size + 512)
! 	Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
!     if ((size_t)len > size)
! 	buffer[size-1] = '\0';
!     else
! 	size = len;
!     memcpy(str, buffer, size);
!     PyMem_Free(buffer);
!     return len - 1;
! }
  
! int PyOS_snprintf(char *str, size_t size, const  char  *format, ...)
  {
!     int rc;
!     va_list va;
  
!     va_start(va, format);
!     rc = myvsnprintf(str, size, format, va);
!     va_end(va);
!     return rc;
  }
  
! int PyOS_vsnprintf(char *str, size_t size, const char  *format, va_list va)
  {
!     return myvsnprintf(str, size, format, va);
! }
  
  #else
! 
! /* Make sure that a C API is included in the lib */
! 
! #ifdef PyOS_snprintf
! # undef PyOS_snprintf
! #endif
  
! int PyOS_snprintf(char *str, size_t size, const  char  *format, ...)
! {
!     int rc;
!     va_list va;
  
!     va_start(va, format);
!     rc = vsnprintf(str, size, format, va);
!     va_end(va);
!     return rc;
! }
  
! #ifdef PyOS_vsnprintf
! # undef PyOS_vsnprintf
  #endif
! 
! int PyOS_vsnprintf(char *str, size_t size, const char  *format, va_list va)
! {
!     return vsnprintf(str, size, format, va);
  }
- 
- #endif
- 
--- 1,93 ----
  #include "Python.h"
+ #include <ctype.h>
  
! /* snprintf() wrappers.  If the platform has vsnprintf, we use it, else we
!    emulate it in a half-hearted way.  Even if the platform has it, we wrap
!    it because platforms differ in what vsnprintf does in case the buffer
!    is too small:  C99 behavior is to return the number of characters that
!    would have been written had the buffer not been too small, and to set
!    the last byte of the buffer to \0.  At least MS _vsnprintf returns a
!    negative value instead, and fills the entire buffer with non-\0 data.
  
!    The wrappers ensure that str[size-1] is always \0 upon return.
  
!    PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
!    (including the trailing '\0') into str.
  
!    If the platform doesn't have vsnprintf, and the buffer size needed to
!    avoid truncation exceeds size by more than 512, Python aborts with a
!    Py_FatalError.
  
!    Return value (rv):
  
! 	When 0 <= rv < size, the output conversion was unexceptional, and
! 	rv characters were written to str (excluding a trailing \0 byte at
! 	str[rv]).
  
! 	When rv >= size, output conversion was truncated, and a buffer of
! 	size rv+1 would have been needed to avoid truncation.  str[size-1]
! 	is \0 in this case.
  
! 	When rv < 0, "something bad happened".  str[size-1] is \0 in this
! 	case too, but the rest of str is unreliable.  It could be that
! 	an error in format codes was detected by libc, or on platforms
! 	with a non-C99 vsnprintf simply that the buffer wasn't big enough
! 	to avoid truncation, or on platforms without any vsnprintf that
! 	PyMem_Malloc couldn't obtain space for a temp buffer.
  
!    CAUTION:  Unlike C99, str != NULL and size > 0 are required.
! */
! 
! int
! PyOS_snprintf(char *str, size_t size, const  char  *format, ...)
  {
! 	int rc;
! 	va_list va;
  
! 	va_start(va, format);
! 	rc = PyOS_vsnprintf(str, size, format, va);
! 	va_end(va);
! 	return rc;
  }
  
! int
! PyOS_vsnprintf(char *str, size_t size, const char  *format, va_list va)
  {
! 	int len;  /* # bytes written, excluding \0 */
! #ifndef HAVE_SNPRINTF
! 	char *buffer;
! #endif
! 	assert(str != NULL);
! 	assert(size > 0);
! 	assert(format != NULL);
  
+ #ifdef HAVE_SNPRINTF
+ 	len = vsnprintf(str, size, format, va);
  #else
! 	/* Emulate it. */
! 	buffer = PyMem_Malloc(size + 512);
! 	if (buffer == NULL) {
! 		len = -666;
! 		goto Done;
! 	}
  
! 	len = vsprintf(buffer, format, va);
! 	if (len < 0)
! 		/* ignore the error */;
  
! 	else if ((size_t)len >= size + 512)
! 		Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
  
! 	else {
! 		const size_t to_copy = (size_t)len < size ?
! 					(size_t)len : size - 1;
! 		assert(to_copy < size);
! 		memcpy(str, buffer, to_copy);
! 		str[to_copy] = '\0';
! 	}
! 	PyMem_Free(buffer);
! Done:
  #endif
! 	str[size-1] = '\0';
! 	return len;
  }

Index: sysmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/sysmodule.c,v
retrieving revision 2.97
retrieving revision 2.98
diff -C2 -d -r2.97 -r2.98
*** sysmodule.c	2001/12/02 08:29:16	2.97
--- sysmodule.c	2001/12/03 00:43:33	2.98
***************
*** 1026,1041 ****
  		const int written = PyOS_vsnprintf(buffer, sizeof(buffer),
  						   format, va);
- 		const int trouble = written < 0 || written >= sizeof(buffer);
- 		if (trouble) {
- 			/* Ensure there's a trailing null byte -- MS
- 			   vsnprintf fills the buffer to the very end
- 			   if it's not big enough. */
- 			buffer[sizeof(buffer) - 1] = '\0';
- 		}
  		if (PyFile_WriteString(buffer, file) != 0) {
  			PyErr_Clear();
  			fputs(buffer, fp);
  		}
! 		if (trouble) {
  			const char *truncated = "... truncated";
  			if (PyFile_WriteString(truncated, file) != 0) {
--- 1026,1034 ----
  		const int written = PyOS_vsnprintf(buffer, sizeof(buffer),
  						   format, va);
  		if (PyFile_WriteString(buffer, file) != 0) {
  			PyErr_Clear();
  			fputs(buffer, fp);
  		}
! 		if (written < 0 || written >= sizeof(buffer)) {
  			const char *truncated = "... truncated";
  			if (PyFile_WriteString(truncated, file) != 0) {