libc: Support message catalog function

https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/nl_types.h.html

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
This commit is contained in:
Xiang Xiao 2021-07-30 04:53:06 +08:00 committed by Alan Carvalho de Assis
parent a262eebe34
commit 98de773081
4 changed files with 399 additions and 3 deletions

View File

@ -28,11 +28,19 @@
#include <nuttx/config.h>
#include <nuttx/compiler.h>
/****************************************************************************
* 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
}

View File

@ -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

View File

@ -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

View File

@ -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 <nuttx/config.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <nl_types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#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 <fcntl.h>.
*
* 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