The prototype of strerror_r

strerror_r is a function specified by POSIX which is not only a thread-safe way to get a (possibly locale-dependent) error message string, but which also offers the caller control of the allocation of the string which may lend itself useful if the string is to be modified. Its prototype given by POSIX is
#include <string.h>
int strerror_r(int, char *, size_t)

where the first parameter specifies the error number we want a string for, and the other two parameters are the buffer to store it in as well as its size. Like the POSIX threads functions, strerror_r returns its error number as opposed to setting errno. ERANGE indicates the buffer was too small to store the contents of the entire string, and the contents of the buffer are undefined, although the majority of implementations store a truncated error string in the buffer and usually terminate it with a null byte as well.

Criticisms of strerror_r

The POSIX Issue 8 drafts incorporate the following addition to the Application Usage section: Applications should use strerror_l() rather than strerror() or strerror_r() to avoid thread safety and possible alternative (non-conforming) versions of these functions in some implementations.

What they're alluding to is the fact that the GNU C Library provides their own variant of strerror_r in addition to the POSIX version. Which version one gets depends on what feature test macros are defined. If one defines _POSIX_C_SOURCE or _XOPEN_SOURCE, as one always should in a program destined to be portable and to leverage the standard, then one gets the proper POSIX definition. It would be a shame if this clash with GNU would reduce the usage of an otherwise good interface.

Another criticism is, how does one figure out what size buffer is big enough? One could dynamically allocate a buffer and just keep reallocing until strerror_r no longer fails, but this is sloppy. Fortunately, POSIX also defines the constant NL_TEXTMAX in limits.h, which is the length of the longest possible message string (which includes error message strings from strerror). It is enough to declare a buffer with size NL_TEXTMAX, and strerror_r should not fail with ERANGE. This means that one can avoid the need for dynamic allocation altogether if this is desirable, although one should check that a buffer this large can be automatically allocated safely.