Friday, May 22, 2009

Safely abstracting use of strerror / strerror_s (part 1)

A recent bug report on the STLSoft SourceForge project site reported that the stlsoft::error_desc component did not use the new "safe string" library function strerror_s(). This was actually a surprise, because I thought I'd already taken care of that.

Since I hadn't, I decided that I should. The change in implementation to use strerror_s() (when in the presence of the "safe string" library) goes along the lines of the following



char buff[1001];
if(0 != ::strerror_s(buff,
STLSOFT_NUM_ELEMENTS(buff) - 1, errno))
{
buff[0] = '\0';
}
else
{
buff[STLSOFT_NUM_ELEMENTS(buff) - 1] = '\0';
}



The dumb part of the strerror_s() function is that it doesn't tell you how many characters were received. This means that you cannot rely on having elicited the full message unless ::strlen() over the returned string is less than (buffer size - 1). So, the above code could return a partial error string (although the likelihood of that is, of course, vanishingly small).

Instead, what I've done is used an auto_buffer to provide resizable storage, and then strerror_s() is called in a loop until either it fails, or no more storage can be allocated, or the length of the returned string is less than (buffer size - 1). The code looks like the following:



stlsoft::auto_buffer buff(128);

for(;;)
{
int n = ::strerror_s(&buff[0], buff.size() - 1, error);

buff[buff.size() - 1u] = '\0';

if(0 == n)
{
size_t cch = ::strlen(buff.data());

if(cch < buff.size() - 2u)
{
m_length = cch;
buff.resize(cch + 1u);
break;
}
}

if(!buff.resize(1u + buff.size() * 2u))
{
buff.resize(1u);
break;
}
}



There's another problem with the class template, but that'll have to wait until a later time to discuss ...

No comments: