Search code examples
clocalizationlocaleglibc

nl_langinfo() - too much information


The function nl_langinfo(INT_CURR_SYMBOL) returns a pointer to a string constant that is of the format:

$(THREE-LETTER-PSEUDOACRONYM) $(SIGN)$(SYMBOL)

so in my locale (en_GB.UTF-8), this would be "GBP -£". I only want the first three letters, so is there another way to do this apart from assigning a nul character to the third element, or using strncpy()?

strcpy(int_curr_symbol, nl_langinfo(INT_CURR_SYMBOL));  // "GBP -£"
int_curr_symbol[3] = '\0';

// or

strncpy(int_curr_symbol, nl_langinfo(INT_CURR_SYMBOL), 3));  // "GBP"

Additionally, nl_langinfo(CRNCYSTR), also returns the sign and the symbol, whereas only the symbol in needed. "-£" -> "£"


Solution

  • INT_CURR_SYMBOL appears to be a GNU extension. In stead, I would suggest localeconv() and the int_curr_symbol field of the struct lconv. This is from POSIX specification for the field:

    The international currency symbol applicable to the current locale. The first three characters contain the alphabetic international currency symbol in accordance with those specified in the ISO 4217:1995 standard. The fourth character (immediately preceding the null byte) is the character used to separate the international currency symbol from the monetary quantity.

    As you can see, it is supposed to contain what you are getting.

    About your second approach:

    Additionally, nl_langinfo(CRNCYSTR), also returns the sign and the symbol, whereas only the symbol in needed. "-£" -> "£"

    This is because the sign defines whether the symbol is to be placed before the value (+), after the value (-), or if it should replace the radix character (.). This is from POSIX specification of langinfo.h:

    Local currency symbol, preceded by '-' if the symbol should appear before the value, '+' if the symbol should appear after the value, or '.' if the symbol should replace the radix character. If the local currency symbol is the empty string, implementations may return the empty string ( "" ).

    As for the method of obtaining the part of the string, I would suggest copying the relevant part out of the returned values into your own buffer right after the call. You have to do this because the structure and the various pointers returned by either of the functions are allocated and maintained by the C library.

    The reason given by POSIX for nl_langinfo() returned values:

    The application shall not modify the string returned. The pointer returned by nl_langinfo() might be invalidated or the string content might be overwritten by a subsequent call to nl_langinfo() in any thread or to nl_langinfo_l() in the same thread or the initial thread, by subsequent calls to setlocale() with a category corresponding to the category of item (see ) or the category LC_ALL, or by subsequent calls to uselocale() which change the category corresponding to the category of item. The pointer returned by nl_langinfo_l() might be invalidated or the string content might be overwritten by a subsequent call to nl_langinfo_l() in the same thread or to nl_langinfo() in any thread, or by subsequent calls to freelocale() or newlocale() which free or modify the locale object that was passed to nl_langinfo_l().

    And here is similar reason given for localeconv():

    The localeconv() function shall return a pointer to the filled-in object. The application shall not modify the structure to which the return value points, nor any storage areas pointed to by pointers within the structure. The returned pointer, and pointers within the structure, might be invalidated or the structure or the storage areas might be overwritten by a subsequent call to localeconv(). In addition, the returned pointer, and pointers within the structure, might be invalidated or the structure or the storage areas might be overwritten by subsequent calls to setlocale() with the categories LC_ALL, LC_MONETARY, or LC_NUMERIC, or by calls to uselocale() which change the categories LC_MONETARY or LC_NUMERIC.