Anteru
Anteru

Reputation: 19384

How to use strerror_l with current locale?

I'm fixing some Linux code which used strerror (not thread-safe) for multi-threading. I found that strerror_r and strerror_l are both thread-safe. Due to different definitions for strerror_r (depending on _GNU_SOURCE it is differently defined) I'd like to use the newer strerror_l function, but how am I supposed to obtain a locale_t object for the current locale? I'm not using iconv or anything, just plain libc, and I don't see how I can obtain a "default locale" object (I don't care in what language the error is printed, I just want a human readable string.)

Upvotes: 10

Views: 2020

Answers (2)

clockley1
clockley1

Reputation: 494

If you pass "" to the locale parameter newlocale will allocate a locale object set to the current native locale[1]

[1]http://pubs.opengroup.org/onlinepubs/9699919799/functions/newlocale.html

static  locale_t locale;

bool MyStrerrorInit(void)
{
    locale = newlocale(LC_ALL_MASK,"",(locale_t)0);
    
    if (locale == (locale_t)0) {
       return false;
    }
 
    return true;
}
    
char * MyStrerror(int error)
{
    return strerror_l(error, locale);
}

Upvotes: 4

Daniel Le
Daniel Le

Reputation: 1830

You could use POSIX uselocale:

strerror_l(errno, uselocale((locate_t)0));

@TavianBarnes pointed out in the comment that this code can exhibit undefined behavior:

[CX] [Option Start] The behavior is undefined if the locale argument to strerror_l() is the special locale object LC_GLOBAL_LOCALE or is not a valid locale object handle. [Option End]

https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html

Upon successful completion, the uselocale() function shall return a handle for the thread-local locale that was in use as the current locale for the calling thread on entry to the function, or LC_GLOBAL_LOCALE if no thread-local locale was in use.

https://pubs.opengroup.org/onlinepubs/9699919799/functions/uselocale.html

Use of LC_GLOBAL_LOCALE with *_l() functions clarified the UB behavior of *_l() functions when given LC_GLOBAL_LOCALE as the locale, provided a version without undefined behavior and discussed alternatives.

An obvious version that isn't thread safe:

locale_t locale = uselocale((locate_t)0);

if (locale == LC_GLOBAL_LOCALE) {
  strerror(errno);
} else {
  strerror_l(errno, locale);
}

A thread-safe version:

locale_t locale = uselocale((locate_t)0);
locale_t copy = loc;

if (copy == LC_GLOBAL_LOCALE) {
  copy = duplocale(copy);
}

strerror_l(errno, copy);

if (loc == LC_GLOBAL_LOCALE) {
  freelocale(copy);
}

Here's a full working example from @TavianBarnes.

Upvotes: 5

Related Questions