From 98de773081661be7b0ea5a21a91c998482f98b07 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Fri, 30 Jul 2021 04:53:06 +0800 Subject: [PATCH] libc: Support message catalog function https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/nl_types.h.html Signed-off-by: Xiang Xiao --- include/nl_types.h | 12 ++ libs/libc/locale/Kconfig | 23 ++- libs/libc/locale/Make.defs | 2 +- libs/libc/locale/lib_catalog.c | 365 +++++++++++++++++++++++++++++++++ 4 files changed, 399 insertions(+), 3 deletions(-) create mode 100644 libs/libc/locale/lib_catalog.c diff --git a/include/nl_types.h b/include/nl_types.h index 96ed6b3595..88bf685d73 100644 --- a/include/nl_types.h +++ b/include/nl_types.h @@ -28,11 +28,19 @@ #include #include +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NL_SETD 1 +#define NL_CAT_LOCALE 1 + /**************************************************************************** * Public Type Definitions ****************************************************************************/ typedef int nl_item; +typedef FAR void *nl_catd; /**************************************************************************** * Public Function Prototypes @@ -46,6 +54,10 @@ extern "C" #define EXTERN extern #endif +nl_catd catopen(FAR const char *name, int oflag); +FAR char *catgets(nl_catd catd, int set_id, int msg_id, FAR const char *s); +int catclose(nl_catd catd); + #undef EXTERN #ifdef __cplusplus } diff --git a/libs/libc/locale/Kconfig b/libs/libc/locale/Kconfig index 4e4f193120..d6af3b596b 100644 --- a/libs/libc/locale/Kconfig +++ b/libs/libc/locale/Kconfig @@ -3,7 +3,7 @@ # see the file kconfig-language.txt in the NuttX tools repository. # -#menu "Locale Support" +menu "Locale Support" config LIBC_LOCALE bool "Enable I18N (LOCALE) support" @@ -11,4 +11,23 @@ config LIBC_LOCALE ---help--- By default, i18n (locale) support is disabled. -#endmenu # Locale Support +if LIBC_LOCALE + +config LIBC_LOCALE_CATALOG + bool "Enable X/Open Message Catalog" + depends on !DISABLE_ENVIRON + default n + ---help--- + Enable catopen, catgets and catclose support. + +config LIBC_LOCALE_PATH + string "The default search path for message catalog file" + depends on LIBC_LOCALE_CATALOG + default "/etc/locale" + ---help--- + This is the default search path to the location where + the message catalog file is expected to be found. + +endif + +endmenu # Locale Support diff --git a/libs/libc/locale/Make.defs b/libs/libc/locale/Make.defs index 35aa6961da..fa3a8114b8 100644 --- a/libs/libc/locale/Make.defs +++ b/libs/libc/locale/Make.defs @@ -24,7 +24,7 @@ ifeq ($(CONFIG_LIBC_LOCALE),y) CSRCS += lib_duplocale.c lib_freelocale.c lib_localeconv.c CSRCS += lib_newlocale.c lib_setlocale.c lib_uselocale.c -CSRCS += lib_langinfo.c +CSRCS += lib_catalog.c lib_langinfo.c # Add the locale directory to the build diff --git a/libs/libc/locale/lib_catalog.c b/libs/libc/locale/lib_catalog.c new file mode 100644 index 0000000000..32f8614698 --- /dev/null +++ b/libs/libc/locale/lib_catalog.c @@ -0,0 +1,365 @@ +/**************************************************************************** + * libs/libc/locale/lib_catalog.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_LIBC_LOCALE_CATALOG + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CAT_MAGIC 0xff88ff89 + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +begin_packed_struct +struct cathdr_s +{ + uint32_t magic; + uint32_t nsets; + uint32_t size; + uint32_t msg_offset; + uint32_t txt_offset; +} end_packed_struct; + +begin_packed_struct +struct catset_s +{ + uint32_t setno; + uint32_t nmsgs; + uint32_t index; +} end_packed_struct; + +begin_packed_struct +struct catmsg_s +{ + uint32_t msgno; + uint32_t msglen; + uint32_t offset; +} end_packed_struct; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static nl_catd catmap(FAR const char *path) +{ + FAR const struct cathdr_s *hdr; + struct stat st; + nl_catd catd; + int fd; + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + return MAP_FAILED; + } + + if (fstat(fd, &st) >= 0) + { + catd = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (catd != MAP_FAILED) + { + hdr = (FAR const struct cathdr_s *)catd; + if (CAT_MAGIC != be32toh(hdr->magic) || + st.st_size != sizeof(*hdr) + be32toh(hdr->size)) + { + munmap(catd, st.st_size); + catd = MAP_FAILED; + set_errno(ENOENT); + } + } + } + + close(fd); + return catd; +} + +static int setcmp(FAR const void *a, FAR const void *b) +{ + FAR const int *set_id = a; + FAR const struct catset_s *set = b; + + return *set_id - be32toh(set->setno); +} + +static int msgcmp(FAR const void *a, FAR const void *b) +{ + FAR const int *msg_id = a; + FAR const struct catmsg_s *msg = b; + + return *msg_id - be32toh(msg->msgno); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: catopen + * + * Description: + * The catopen() function shall open a message catalog and return a + * message catalog descriptor. The name argument specifies the name of + * the message catalog to be opened. If name contains a '/', then name + * specifies a pathname for the message catalog. Otherwise, the environment + * variable NLSPATH is used with name substituted for the %N conversion + * specification (see XBD Environment Variables); if NLSPATH exists in the + * environment when the process starts, then if the process has appropriate + * privileges, the behavior of catopen() is undefined. If NLSPATH does not + * exist in the environment, or if a message catalog cannot be found in any + * of the components specified by NLSPATH, then an implementation-defined + * default path shall be used. This default may be affected by the setting + * of LC_MESSAGES if the value of oflag is NL_CAT_LOCALE, or the LANG + * environment variable if oflag is 0. + * + * A message catalog descriptor shall remain valid in a process until that + * process closes it, or a successful call to one of the exec functions. + * A change in the setting of the LC_MESSAGES category may invalidate + * existing open catalogs. + * + * If a file descriptor is used to implement message catalog descriptors, + * the FD_CLOEXEC flag shall be set; see . + * + * If the value of the oflag argument is 0, the LANG environment variable + * is used to locate the catalog without regard to the LC_MESSAGES + * category. If the oflag argument is NL_CAT_LOCALE, the LC_MESSAGES + * category is used to locate the message catalog (see XBD + * Internationalization Variables ). + * + * Returned Value: + * Upon successful completion, catopen() shall return a message catalog + * descriptor for use on subsequent calls to catgets() and catclose(). + * Otherwise, catopen() shall return (nl_catd) -1 and set errno to + * indicate the error. + * + ****************************************************************************/ + +nl_catd catopen(FAR const char *name, int oflag) +{ + FAR const char *path; + FAR const char *lang; + FAR const char *p; + FAR const char *z; + + if (strchr(name, '/')) + { + return catmap(name); + } + + path = getenv("NLSPATH"); + if (path == NULL) + { + path = CONFIG_LIBC_LOCALE_PATH; + } + + lang = oflag ? NULL : getenv("LANG"); + if (lang == NULL) + { + lang = ""; + } + + for (p = path; *p; p = z) + { + char buf[PATH_MAX]; + nl_catd catd; + size_t i; + + z = strchr(p, ':'); + if (z == NULL) + { + z = p + strlen(p); + } + + for (i = 0; p < z; p++) + { + FAR const char *v; + size_t l; + + if (*p == '%') + { + switch (*++p) + { + case 'N': + v = name; + l = strlen(v); + break; + + case 'L': + v = lang; + l = strlen(v); + break; + + case 'l': + v = lang; + l = strcspn(v, "_.@"); + break; + + case 't': + v = strchr(lang, '_'); + if (v == NULL) + { + v = "\0"; + } + + l = strcspn(++v, ".@"); + break; + + case 'c': + v = "UTF-8"; + l = 5; + break; + + case '%': + v = "%"; + l = 1; + break; + + default: + v = NULL; + } + } + else + { + v = p; + l = 1; + } + + if (v == NULL || i + l >= sizeof(buf)) + { + break; + } + + memcpy(buf + i, v, l); + i += l; + } + + if (*z) + { + z++; + } + + if (*p != ':' && *p != '\0') + { + continue; + } + + /* Leading : or :: in NLSPATH is same as %N */ + + buf[i] = 0; + catd = catmap(i ? buf : name); + if (catd != MAP_FAILED) + { + return catd; + } + } + + set_errno(ENOENT); + return MAP_FAILED; +} + +/**************************************************************************** + * Name: catgets + * + * Description: + * The catgets() function shall attempt to read message msg_id, in set + * set_id, from the message catalog identified by catd. The catd argument + * is a message catalog descriptor returned from an earlier call to + * catopen(). The results are undefined if catd is not a value returned + * by catopen() for a message catalog still open in the process. The s + * argument points to a default message string which shall be returned by + * catgets() if it cannot retrieve the identified message. + * + * The catgets() function need not be thread-safe. + * + * Returned Value: + * If the identified message is retrieved successfully, catgets() shall + * return a pointer to an internal buffer area containing the null- + * terminated message string. If the call is unsuccessful for any reason, + * s shall be returned and errno shall be set to indicate the error. + * + ****************************************************************************/ + +FAR char *catgets(nl_catd catd, int set_id, int msg_id, FAR const char *s) +{ + FAR const struct cathdr_s *hdr = (FAR const struct cathdr_s *)catd; + FAR const struct catset_s *set = (FAR const struct catset_s *)(hdr + 1); + FAR const struct catmsg_s *msg = (FAR const struct catmsg_s *) + ((FAR const char *)(hdr + 1) + be32toh(hdr->msg_offset)); + FAR const char *string = + ((FAR const char *)(hdr + 1) + be32toh(hdr->txt_offset)); + + set = bsearch(&set_id, set, be32toh(hdr->nsets), sizeof(*set), setcmp); + if (set == NULL) + { + set_errno(ENOMSG); + return (FAR char *)s; + } + + msg += be32toh(set->index); + msg = bsearch(&msg_id, msg, be32toh(set->nmsgs), sizeof(*msg), msgcmp); + if (msg == NULL) + { + set_errno(ENOMSG); + return (FAR char *)s; + } + + return (FAR char *)(string + be32toh(msg->offset)); +} + +/**************************************************************************** + * Name: catclose + * + * Description: + * The catclose() function shall close the message catalog identified by + * catd. If a file descriptor is used to implement the type nl_catd, that + * file descriptor shall be closed. + * + * Returned Value: + * Upon successful completion, catclose() shall return 0; otherwise, + * -1 shall be returned, and errno set to indicate the error. + * + ****************************************************************************/ + +int catclose(nl_catd catd) +{ + FAR const struct cathdr_s *hdr = (FAR const struct cathdr_s *)catd; + + return munmap(catd, sizeof(*hdr) + be32toh(hdr->size)); +} + +#endif