root/lib/libc/locale/nb_lc_template.h
/* $NetBSD: nb_lc_template.h,v 1.9 2022/04/19 20:32:15 rillig Exp $ */

/*-
 * Copyright (c)1999, 2008 Citrus Project,
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Paul Kranenburg.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Paul Borman at Krystal Technologies.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef _NB_LC_TEMPLATE_H_
#define _NB_LC_TEMPLATE_H_

#define _nb_part_t              _PREFIX(part_t)
#define _nb_part_cache          _PREFIX(part_cache)
#define _nb_mutex               _PREFIX(mutex)

typedef struct _nb_part_t {
        char name[_LOCALENAME_LEN_MAX];
        _CATEGORY_TYPE *impl;
        SIMPLEQ_ENTRY(_nb_part_t) entry;
} _nb_part_t;

static SIMPLEQ_HEAD(, _nb_part_t) _nb_part_cache =
    SIMPLEQ_HEAD_INITIALIZER(_nb_part_cache);

#ifdef _REENTRANT
static mutex_t _nb_mutex = MUTEX_INITIALIZER;
#endif

static int
_PREFIX(load_sub)(const char * __restrict name, const char * __restrict real,
    const char ** __restrict out_name, _CATEGORY_TYPE ** __restrict out_impl,
    int force)
{
        const char *cached_name;
        _CATEGORY_TYPE *cached_impl;
        _nb_part_t *p;
        int ret;

        _DIAGASSERT(name != NULL);
        _DIAGASSERT(out_name != NULL);
        _DIAGASSERT(out_impl != NULL);

        if (!strcmp(_C_LOCALE, name)) {
                cached_name = _lc_C_locale.part_name[_CATEGORY_ID];
                cached_impl = _lc_C_locale.part_impl[_CATEGORY_ID];
        } else if (!strcmp(_POSIX_LOCALE, name)) {
                cached_name = _POSIX_LOCALE;
                cached_impl = _lc_C_locale.part_impl[_CATEGORY_ID];
        } else {
                SIMPLEQ_FOREACH(p, &_nb_part_cache, entry) {
                        if (!strcmp((const char *)&p->name[0], name)) {
                                cached_name = p->name;
                                cached_impl = p->impl;
                                goto found;
                        }
                }
                p = malloc(sizeof(*p));
                if (p == NULL)
                        return ENOMEM;
                if (force) {
                        p->impl = _lc_C_locale.part_impl[_CATEGORY_ID];
                } else {
                        _DIAGASSERT(_PathLocale != NULL);
                        ret = _PREFIX(create_impl)((const char *)_PathLocale,
                            name, &p->impl);
                        if (ret) {
                                free(p);
                                return ret;
                        }
                }
                strlcpy(&p->name[0], name, sizeof(p->name));
                SIMPLEQ_INSERT_TAIL(&_nb_part_cache, p, entry);
                cached_name = p->name;
                cached_impl = p->impl;
        }
found:
        if (real != NULL) {
                p = malloc(sizeof(*p));
                if (p == NULL)
                        return ENOMEM;
                strlcpy(&p->name[0], real, sizeof(p->name));
                cached_name = p->name;
                p->impl = cached_impl;
                SIMPLEQ_INSERT_TAIL(&_nb_part_cache, p, entry);
        }
        *out_name = cached_name;
        *out_impl = cached_impl;
        return 0;
}

static __inline int
_PREFIX(load)(const char * __restrict name,
    const char ** __restrict out_name, _CATEGORY_TYPE ** __restrict out_impl)
{
        int ret, force;
        char path[PATH_MAX + 1], loccat[PATH_MAX + 1], buf[PATH_MAX + 1];
        const char *aliaspath, *alias;

#define _LOAD_SUB_ALIAS(key)                                            \
do {                                                                    \
        alias = __unaliasname(aliaspath, key, &buf[0], sizeof(buf));    \
        if (alias != NULL) {                                            \
                ret = (force = !__isforcemapping(alias))                \
                    ? _PREFIX(load_sub)(name, NULL, out_name, out_impl, \
                                        force)  \
                    : _PREFIX(load_sub)(alias, name, out_name, out_impl, \
                                        force); \
                _DIAGASSERT(!ret || !force);                            \
                goto done;                                              \
        }                                                               \
} while (0)

        /* (1) non-aliased file */
        mutex_lock(&_nb_mutex);
        ret = _PREFIX(load_sub)(name, NULL, out_name, out_impl, 0);
        if (ret != ENOENT)
                goto done;

        /* (2) lookup locname/catname type alias */
        _DIAGASSERT(_PathLocale != NULL);
        snprintf(&path[0], sizeof(path),
            "%s/" _LOCALE_ALIAS_NAME, _PathLocale);
        aliaspath = (const char *)&path[0];
        snprintf(&loccat[0], sizeof(loccat),
            "%s/" _CATEGORY_NAME, name);
        _LOAD_SUB_ALIAS((const char *)&loccat[0]);

        /* (3) lookup locname type alias */
        _LOAD_SUB_ALIAS(name);

done:
        mutex_unlock(&_nb_mutex);
        return ret;
}

const char *
_PREFIX(setlocale)(const char * __restrict name,
    struct _locale * __restrict locale)
{
        const char *loaded_name;
        _CATEGORY_TYPE *loaded_impl;

        /* name may be NULL */
        _DIAGASSERT(locale != NULL);

        if (name != NULL) {
                if (*name == '\0')
                        name = _get_locale_env(_CATEGORY_NAME);
                _DIAGASSERT(name != NULL);
                _DIAGASSERT(locale->part_name[(size_t)_CATEGORY_ID] != NULL);
                if (strcmp(name, locale->part_name[(size_t)_CATEGORY_ID])) {
                        if (_PREFIX(load)(name, &loaded_name, &loaded_impl))
                                return NULL;
                        locale->part_name[(size_t)_CATEGORY_ID] = loaded_name;
                        locale->part_impl[(size_t)_CATEGORY_ID] = loaded_impl;
                        if (locale == &_lc_global_locale)
                                _PREFIX(update_global)(loaded_impl);
                }
        }
        return locale->part_name[(size_t)_CATEGORY_ID];
}

#endif /*_NB_LC_TEMPLATE_H_*/