#include <sys/cdefs.h>
__RCSID("$NetBSD: newlocale.c,v 1.5 2024/01/20 14:52:48 christos Exp $");
#include "namespace.h"
#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include "setlocale_local.h"
__weak_alias(newlocale, _newlocale)
locale_t
newlocale(int mask, const char *name, locale_t src)
{
struct _locale *dst;
char head[_LOCALENAME_LEN_MAX * (_LC_LAST - 1)], *tail;
const char *tokens[_LC_LAST - 1];
_locale_set_t l;
int i, howmany, categories[_LC_LAST - 1];
if (name == NULL)
name = _C_LOCALE;
dst = malloc(sizeof(*dst));
if (dst == NULL)
return (locale_t)NULL;
if (src == NULL)
src = _current_locale();
memcpy(dst, src, sizeof(*src));
if (strlcpy(&head[0], name, sizeof(head)) >= sizeof(head)) {
free(dst);
return (locale_t)NULL;
}
tokens[0] = (const char *)&head[0];
tail = __UNCONST(strchr(tokens[0], '/'));
if (tail == NULL) {
for (i = 1; i < _LC_LAST; ++i) {
if (mask & (1 << i)) {
l = _find_category(i);
_DIAGASSERT(l != NULL);
(*l)(tokens[0], dst);
}
}
} else {
*tail++ = '\0';
howmany = 0;
for (i = 1; i < _LC_LAST; ++i) {
if (mask & (1 << i))
categories[howmany++] = i;
}
if (howmany-- > 0) {
for (i = 1; i < howmany; ++i) {
*tail++ = '\0';
tokens[i] = (const char *)tail;
tail = __UNCONST(strchr(tokens[i], '/'));
if (tail == NULL) {
free(dst);
return NULL;
}
}
*tail++ = '\0';
tokens[howmany] = tail;
tail = __UNCONST(strchr(tokens[howmany], '/'));
if (tail != NULL) {
free(dst);
return NULL;
}
for (i = 0; i <= howmany; ++i) {
l = _find_category(categories[i]);
_DIAGASSERT(l != NULL);
(*l)(tokens[i], dst);
}
}
}
if (_setlocale_cache(dst, NULL)) {
free(dst);
return NULL;
}
return (locale_t)dst;
}