summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoritojun <itojun@NetBSD.org>2000-10-31 10:45:04 +0000
committeritojun <itojun@NetBSD.org>2000-10-31 10:45:04 +0000
commitbb42e65bfecc3a76253c9b8889051cfe941a4b9a (patch)
treea911cb364c6dead82bf2de14b4bf9ca7ac12aa48
parentef28de501182943a2433b9a2fd6127dafe6e3583 (diff)
BSD-licensed libintl, fully compatible with GNU libintl (in GNU gettext).
from Citrus project xpg4dl repository.
-rw-r--r--lib/libintl/Makefile19
-rw-r--r--lib/libintl/gettext.3390
-rw-r--r--lib/libintl/gettext.c589
-rw-r--r--lib/libintl/libintl.h47
-rw-r--r--lib/libintl/libintl_local.h75
-rw-r--r--lib/libintl/pathnames.h29
-rw-r--r--lib/libintl/shlib_version5
-rw-r--r--lib/libintl/textdomain.c91
8 files changed, 1245 insertions, 0 deletions
diff --git a/lib/libintl/Makefile b/lib/libintl/Makefile
new file mode 100644
index 00000000000..e1e96df1d78
--- /dev/null
+++ b/lib/libintl/Makefile
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $
+
+.include <bsd.own.mk>
+
+LIB= intl
+SRCS= gettext.c textdomain.c
+INCS= libintl.h
+INCSDIR=/usr/include
+
+#CFLAGS+=-g
+CPPFLAGS+=-I${.CURDIR}
+
+MAN= gettext.3
+MLINKS= gettext.3 dgettext.3 gettext.3 dcgettext.3 \
+ gettext.3 ngettext.3 gettext.3 dngettext.3 \
+ gettext.3 dcngettext.3 gettext.3 textdomain.3 \
+ gettext.3 bindtextdomain.3 gettext.3 bind_textdomain_codeset.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libintl/gettext.3 b/lib/libintl/gettext.3
new file mode 100644
index 00000000000..12862c72859
--- /dev/null
+++ b/lib/libintl/gettext.3
@@ -0,0 +1,390 @@
+.\" $NetBSD: gettext.3,v 1.1.1.1 2000/10/31 10:45:05 itojun Exp $
+.\"
+.\" Copyright (c) 2000 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.
+.\"
+.Dd October 20, 2000
+.Dt GETTEXT 3
+.Os
+.Sh NAME
+.Nm gettext ,
+.Nm dgettext ,
+.Nm ngettext ,
+.Nm dngettext ,
+.Nm textdomain ,
+.Nm bindtextdomain ,
+.Nm bind_textdomain_codeset ,
+.Nm dcgettext ,
+.Nm dcngettext
+.Nd message handling functions
+.Sh LIBRARY
+libintl
+.Sh SYNOPSIS
+.Fd #include <libintl.h>
+.Ft char *
+.Fn gettext "const char *msgid"
+.Ft char *
+.Fn dgettext "const char *domainname" "const char *msgid"
+.Ft char *
+.Fn ngettext "const char *domainname" "const char *msgid" "unsigned long int n"
+.Ft char *
+.Fn dngettext "const char *domainname" "const char *msgid1" "const char *msgid2" "unsigned long int n"
+.Ft char *
+.Fn textdomain "const char *domainname"
+.Ft char *
+.Fn bindtextdomain "const char *domainname" "const char *dirname"
+.Ft char *
+.Fn bind_textdomain_codeset "const char *domainname" "const char *codeset"
+.Fd #include <libintl.h>
+.Fd #include <locale.h>
+.Ft char *
+.Fn dcgettext "const char *domainname" "const char *msgid" "int category"
+.Ft char *
+.Fn dcngettext "const char *domainname" "const char *msgid1" "const char *msgid2" "unsigned long int n" "int category"
+.Sh DESCRIPTION
+The
+.Fn gettext ,
+.Fn dgettext ,
+and
+.Fn dcgettext
+functions attempt to retrieve a
+target string based on the specified
+.Fa msgid
+argument within the context of a
+specific domain and the current locale.
+The length of strings returned by
+.Fn gettext ,
+.Fn dgettext ,
+and
+.fn dcgettext
+is undetermined until the function is
+called.
+The
+.Fa msgid
+argument is a null-terminated string.
+.Pp
+The
+.Fn ngettext ,
+.Fn dngettext
+and
+.Fn dcngettext
+functions are equivalent to
+.Fn gettext ,
+.Fn dgettext
+and
+.Fn dcgettext ,
+respectively, except for the handling of
+plural forms.
+The
+.Fn ngettext ,
+.Fn dngettext
+and
+.Fn dcngettext
+searches for the
+message string using the
+.Fa msgid1
+argument as the key, using the argument
+.Fa n
+to
+determine the plural form.
+If no message catalogs are found,
+.Fa msgid1
+is returned
+if
+.Fa n Li == 1 ,
+otherwise
+.Fa msgid2
+is returned.
+.Pp
+The
+.Dv LANGUAGE
+environment variable is examined first to determine the message
+catalogs to be used.
+The value of the
+.Dv LANGUAGE
+environment variable is a list
+of locale names separated by colon (:) character.
+If the
+.Dv LANGUAGE
+environment
+variable is defined, each locale name is tried in the specified order and if a
+message catalog containing the requested message is found, the message is
+returned.
+If the
+.Dv LANGUAGE
+environment variable is defined but failed to locate
+a message catalog, the
+.Fa msgid
+string will be returned.
+.Pp
+If the
+.Dv LANGUAGE
+environment variable is not defined,
+.Dv LC_ALL ,
+.Dv LC_xxx
+and
+.Dv LANG
+environment variables are examined to locate the message catalog, following the
+convention used by the
+.Xr setlocale 3
+function.
+.Pp
+The pathname used to locate the message catalog is
+.Pa dirname/locale/category/domainname.mo,
+where dirname is the directory specified by
+.Fn bindtextdomain ,
+locale is a locale name determined by the definition of environment variables,
+.Fa category
+is
+.Dv LC_MESSAGES
+if
+.Fn gettext ,
+.Fn ngettext ,
+.Fn dgettext
+or
+.Fn dngettext
+is
+called, otherwise
+.Dv LC_xxx
+where the name is the same as the locale category name
+specified by the
+.Fa category
+argument of
+.Fn dcgettext
+or
+.Fn dcngettext .
+.Fa domainname
+is the name of the domain specified by
+.Fn textdomain
+or the
+.Fa domainname
+argument of
+.Fn dgettext ,
+.Fn dngettext ,
+.Fn dcgettext
+or
+.Fn dcngettext .
+.Pp
+For
+.Fn gettext
+and
+.Fn ngettext ,
+the domain used is set by the last valid call to
+.Fn textdomain .
+If a valid call to
+.Fn textdomain
+has not been made, the default
+domain (called messages) is used.
+.Pp
+For
+.Fn dgettext ,
+.Fn dngettext ,
+.Fn dcgettext
+and
+.Fn dcngettext ,
+the domain used is
+specified by the
+.Fa domainname
+argument.
+The
+.Fa domainname
+argument is equivalent in
+syntax and meaning to the
+.Fa domainname
+argument to
+.Fn textdomain ,
+except that the
+selection of the domain is valid only for the duration of the
+.Fn dgettext ,
+.Fn dngettext ,
+.Fn dcgettext
+or
+.Fn dcngettext
+function call.
+.Pp
+The
+.Fn dcgettext
+and
+.Fn dcngettext
+functions require additional argument
+.Fa category
+for retrieving message string for other than
+.Dv LC_MESSAGES
+category.
+Available value for the
+.Fa category
+argument are
+.Dv LC_CTYPE ,
+.Dv LC_COLLATE ,
+.Dv LC_MESSAGES ,
+.Dv LC_MONETARY ,
+.Dv LC_NUMERIC
+and
+.Dv LC_TIME .
+The call of
+.Fn dcgettext "domainname" "msgid" "LC_MESSAGES"
+is equivalent to
+.Fn dgettext "domainname" "msgid" .
+Note that
+.Dv LC_ALL
+must not be used.
+.Pp
+The
+.Fn textdomain
+function sets or queries the name of the current domain of the
+active
+.Dv LC_MESSAGES
+locale category.
+The
+.Fa domainname
+argument is a
+null-terminated string that can contain only the characters allowed in legal
+filenames.
+.Pp
+The
+.Fa domainname
+argument is the unique name of a domain on the system.
+If there
+are multiple versions of the same domain on one system, namespace collisions
+can be avoided by using
+.Fn bindtextdomain .
+If
+.Fn textdomain
+is not called, a
+default domain is selected.
+The setting of domain made by the last valid call
+to
+.Fn textdomain
+remains valid across subsequent calls to
+.Xr setlocale 3 ,
+and
+.Fn gettext .
+.Pp
+The
+.Fa domainname
+argument is applied to the currently active LC_MESSAGES locale.
+.Pp
+The current setting of the domain can be queried without affecting the current
+state of the domain by calling
+.Fn textdomain
+with
+.Fa domainname
+set to the null pointer.
+Calling
+.Fn textdomain
+with a
+.Fa domainname
+argument of a null string sets
+the domain to the default domain
+.Pq messages .
+.Pp
+The
+.Fn bindtextdomain
+function binds the path predicate for a message domain
+.Fa domainname
+to the value contained in dirname.
+If
+.Fa domainname
+is a non-empty
+string and has not been bound previously,
+.Fn bindtextdomain
+binds
+.Fa domainname
+with
+.Fa dirname .
+.Pp
+If
+.Fa domainname
+is a non-empty string and has been bound previously,
+.Fn bindtextdomain
+replaces the old binding with dirname.
+The dirname argument
+can be an absolute or relative pathname being resolved when
+.Fn gettext ,
+.Fn ngettext ,
+.Fn dgettext ,
+.Fn dngettext ,
+.Fn dcgettext ,
+or
+.Fn dcngettext
+are called.
+If
+.Fa domainname
+is a null pointer or an empty string,
+.Fn bindtextdomain
+returns null pointer.
+If
+.Fn bindtextdomain
+is not called, implementation-defined default directory is used.
+.Pp
+The
+.Fn bind_textdomain_codeset
+function can be used to specify the output
+.Fa codeset
+for message catalogs for domain
+.Fa domainname .
+The
+.Fa codeset
+argument must
+be a valid codeset name which can be used for the
+.Xr iconv_open 3
+function.
+.Pp
+If the
+.Fa codeset
+argument is the null pointer,
+.Fn bind_textdomain_codeset
+returns the currently selected
+.Fa codeset
+for the domain with the name
+.Fa domainname .
+It returns null pointer if no
+.Fa codeset
+has yet been selected.
+.Pp
+The
+.Fn bind_textdomain_codeset
+function can be used several times.
+If used multiple times, with the same
+.Fa domainname
+argument,
+the later call overrides the
+settings made by the earlier one.
+.Pp
+The
+.Fn bind_textdomain_codeset
+function returns a pointer to a string containing
+the name of the selected
+.Fa codeset .
+.\".Sh "RETURN VALUES"
+.\".Sh EXAMPLES
+.Sh SEE ALSO
+.Xr setlocale 3
+.\".Sh STANDARDS
+.Sh HISTORY
+The functions are implemented by Citrus project,
+based on the documentations for GNU gettext.
+.Sh BUGS
+The text was ripped off from Annex C of
+.Dq LI18NUX 2000 Globalization Specification Version 1.0 .
diff --git a/lib/libintl/gettext.c b/lib/libintl/gettext.c
new file mode 100644
index 00000000000..57129b33b00
--- /dev/null
+++ b/lib/libintl/gettext.c
@@ -0,0 +1,589 @@
+/* $NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#if 0
+#include <util.h>
+#endif
+#include <libintl.h>
+#include <locale.h>
+#include "libintl_local.h"
+#include "pathnames.h"
+
+static struct mohandle mohandle;
+
+static const char *lookup_category __P((int));
+static const char *split_locale __P((const char *));
+static const char *lookup_mofile __P((char *, size_t, const char *,
+ char *, const char *, const char *));
+static u_int32_t flip __P((u_int32_t, u_int32_t));
+static int validate __P((void *));
+static int mapit __P((const char *));
+static int unmapit __P((void));
+static const char *lookup_hash __P((const char *));
+static const char *lookup_bsearch __P((const char *));
+static const char *lookup __P((const char *));
+
+/*
+ * shortcut functions. the main implementation resides in dcngettext().
+ */
+char *
+gettext(msgid)
+ const char *msgid;
+{
+
+ return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES);
+}
+
+char *
+dgettext(domainname, msgid)
+ const char *domainname;
+ const char *msgid;
+{
+
+ return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES);
+}
+
+char *
+dcgettext(domainname, msgid, category)
+ const char *domainname;
+ const char *msgid;
+ int category;
+{
+
+ return dcngettext(domainname, msgid, NULL, 1UL, category);
+}
+
+char *
+ngettext(msgid1, msgid2, n)
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+{
+
+ return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+char *
+dngettext(domainname, msgid1, msgid2, n)
+ const char *domainname;
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+{
+
+ return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+/*
+ * dcngettext() -
+ * lookup internationalized message on database locale/category/domainname
+ * (like ja_JP.eucJP/LC_MESSAGES/domainname).
+ * if n equals to 1, internationalized message will be looked up for msgid1.
+ * otherwise, message will be looked up for msgid2.
+ * if the lookup fails, the function will return msgid1 or msgid2 as is.
+ *
+ * Even though the return type is "char *", caller should not rewrite the
+ * region pointed to by the return value (should be "const char *", but can't
+ * change it for compatibility with other implementations).
+ *
+ * by default (if domainname == NULL), domainname is taken from the value set
+ * by textdomain(). usually name of the application (like "ls") is used as
+ * domainname. category is usually LC_MESSAGES.
+ *
+ * the code reads in *.mo files generated by GNU gettext. *.mo is a host-
+ * endian encoded file. both endians are supported here, as the files are in
+ * /usr/share/locale! (or we should move those files into /usr/libdata)
+ */
+
+static const char *
+lookup_category(category)
+ int category;
+{
+
+ switch (category) {
+ case LC_COLLATE: return "LC_COLLATE";
+ case LC_CTYPE: return "LC_CTYPE";
+ case LC_MONETARY: return "LC_MONETARY";
+ case LC_NUMERIC: return "LC_NUMERIC";
+ case LC_TIME: return "LC_TIME";
+ case LC_MESSAGES: return "LC_MESSAGES";
+ }
+ return NULL;
+}
+
+/*
+ * XPG syntax: language[_territory[.codeset]][@modifier]
+ * XXX boundary check on "result" is lacking
+ */
+static const char *
+split_locale(lname)
+ const char *lname;
+{
+ char buf[BUFSIZ], tmp[BUFSIZ];
+ char *l, *t, *c, *m;
+ static char result[BUFSIZ];
+
+ memset(result, 0, sizeof(result));
+
+ if (strlen(lname) + 1 > sizeof(buf)) {
+fail:
+ return lname;
+ }
+
+ strlcpy(buf, lname, sizeof(buf));
+ m = strrchr(buf, '@');
+ if (m)
+ *m++ = '\0';
+ c = strrchr(buf, '.');
+ if (c)
+ *c++ = '\0';
+ t = strrchr(buf, '_');
+ if (t)
+ *t++ = '\0';
+ l = buf;
+ if (strlen(l) == 0)
+ goto fail;
+ if (c && !t)
+ goto fail;
+
+ if (m) {
+ if (t) {
+ if (c) {
+ snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s",
+ l, t, c, m);
+ strlcat(result, tmp, sizeof(result));
+ strlcat(result, ":", sizeof(result));
+ }
+ snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m);
+ strlcat(result, tmp, sizeof(result));
+ strlcat(result, ":", sizeof(result));
+ }
+ snprintf(tmp, sizeof(tmp), "%s@%s", l, m);
+ strlcat(result, tmp, sizeof(result));
+ strlcat(result, ":", sizeof(result));
+ }
+ if (t) {
+ if (c) {
+ snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c);
+ strlcat(result, tmp, sizeof(result));
+ strlcat(result, ":", sizeof(result));
+ }
+ strlcat(result, tmp, sizeof(result));
+ strlcat(result, ":", sizeof(result));
+ }
+ strlcat(result, l, sizeof(result));
+
+ return result;
+}
+
+static const char *
+lookup_mofile(buf, len, dir, lpath, category, domainname)
+ char *buf;
+ size_t len;
+ const char *dir;
+ char *lpath; /* list of locales to be tried */
+ const char *category;
+ const char *domainname;
+{
+ struct stat st;
+ char *p, *q;
+
+ q = lpath;
+ while (1) {
+ p = strsep(&q, ":");
+ if (!p)
+ break;
+ if (!*p)
+ continue;
+
+ /* don't mess with default locales */
+ if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0)
+ return NULL;
+
+ /* validate pathname */
+ if (strchr(p, '/') || strchr(category, '/'))
+ continue;
+#if 1 /*?*/
+ if (strchr(domainname, '/'))
+ continue;
+#endif
+
+ snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p,
+ category, domainname);
+ if (stat(buf, &st) < 0)
+ continue;
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+ continue;
+
+ if (mapit(buf) == 0)
+ return buf;
+ }
+
+ return NULL;
+}
+
+static u_int32_t
+flip(v, magic)
+ u_int32_t v;
+ u_int32_t magic;
+{
+
+ if (magic == MO_MAGIC)
+ return v;
+ else if (magic == MO_MAGIC_SWAPPED) {
+ v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) |
+ ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000);
+ return v;
+ } else {
+ abort();
+ /*NOTREACHED*/
+ }
+}
+
+static int
+validate(arg)
+ void *arg;
+{
+ char *p;
+
+ p = (char *)arg;
+ if (p < (char *)mohandle.addr ||
+ p > (char *)mohandle.addr + mohandle.len)
+ return 0;
+ else
+ return 1;
+}
+
+int
+mapit(path)
+ const char *path;
+{
+ int fd;
+ struct stat st;
+ char *base;
+ u_int32_t magic, revision;
+ struct moentry *otable, *ttable;
+ struct moentry_h *p;
+ struct mo *mo;
+ size_t l;
+ int i;
+ char *v;
+
+ if (mohandle.addr && strcmp(path, mohandle.path) == 0)
+ return 0; /*already opened*/
+
+ unmapit();
+
+#if 0
+ if (secure_path(path) != 0)
+ goto fail;
+#endif
+ if (stat(path, &st) < 0)
+ goto fail;
+ if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX)
+ goto fail;
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto fail;
+ if (read(fd, &magic, sizeof(magic)) < 0 ||
+ (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) {
+ close(fd);
+ goto fail;
+ }
+ if (read(fd, &revision, sizeof(revision)) < 0 ||
+ flip(revision, magic) != MO_REVISION) {
+ close(fd);
+ goto fail;
+ }
+ mohandle.addr = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, (off_t)0);
+ if (!mohandle.addr) {
+ close(fd);
+ goto fail;
+ }
+ close(fd);
+ mohandle.len = st.st_size;
+ strlcpy(mohandle.path, path, sizeof(mohandle.path));
+
+ base = mohandle.addr;
+ mo = (struct mo *)mohandle.addr;
+
+ /* flip endian. do not flip magic number! */
+ mohandle.mo.mo_magic = mo->mo_magic;
+ mohandle.mo.mo_revision = flip(mo->mo_revision, magic);
+ mohandle.mo.mo_nstring = flip(mo->mo_nstring, magic);
+
+ /* validate otable/ttable */
+ otable = (struct moentry *)(base + flip(mo->mo_otable, magic));
+ ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic));
+ if (!validate(otable) || !validate(&otable[mohandle.mo.mo_nstring])) {
+ unmapit();
+ goto fail;
+ }
+ if (!validate(ttable) || !validate(&ttable[mohandle.mo.mo_nstring])) {
+ unmapit();
+ goto fail;
+ }
+
+ /* allocate [ot]table, and convert to normal pointer representation. */
+ l = sizeof(struct moentry_h) * mohandle.mo.mo_nstring;
+ mohandle.mo.mo_otable = (struct moentry_h *)malloc(l);
+ if (!mohandle.mo.mo_otable) {
+ unmapit();
+ goto fail;
+ }
+ mohandle.mo.mo_ttable = (struct moentry_h *)malloc(l);
+ if (!mohandle.mo.mo_ttable) {
+ unmapit();
+ goto fail;
+ }
+ p = mohandle.mo.mo_otable;
+ for (i = 0; i < mohandle.mo.mo_nstring; i++) {
+ p[i].len = flip(otable[i].len, magic);
+ p[i].off = base + flip(otable[i].off, magic);
+
+ if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) {
+ unmapit();
+ goto fail;
+ }
+ }
+ p = mohandle.mo.mo_ttable;
+ for (i = 0; i < mohandle.mo.mo_nstring; i++) {
+ p[i].len = flip(ttable[i].len, magic);
+ p[i].off = base + flip(ttable[i].off, magic);
+
+ if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) {
+ unmapit();
+ goto fail;
+ }
+ }
+
+ /* grab MIME-header and charset field */
+ mohandle.mo.mo_header = lookup("");
+ if (mohandle.mo.mo_header)
+ v = strstr(mohandle.mo.mo_header, "charset=");
+ else
+ v = NULL;
+ if (v) {
+ mohandle.mo.mo_charset = strdup(v + 8);
+ v = strchr(mohandle.mo.mo_charset, '\n');
+ if (v)
+ *v = '\0';
+ }
+
+ /*
+ * XXX check charset, reject it if we are unable to support the charset
+ * with the current locale.
+ * for example, if we are using euc-jp locale and we are looking at
+ * *.mo file encoded by euc-kr (charset=euc-kr), we should reject
+ * the *.mo file as we cannot support it.
+ */
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static int
+unmapit()
+{
+
+ /* unmap if there's already mapped region */
+ if (mohandle.addr)
+ munmap(mohandle.addr, mohandle.len);
+ mohandle.addr = NULL;
+ mohandle.path[0] = '\0';
+ if (mohandle.mo.mo_otable)
+ free(mohandle.mo.mo_otable);
+ if (mohandle.mo.mo_ttable)
+ free(mohandle.mo.mo_ttable);
+ if (mohandle.mo.mo_charset)
+ free(mohandle.mo.mo_charset);
+ memset(&mohandle.mo, 0, sizeof(mohandle.mo));
+ return 0;
+}
+
+static const char *
+lookup_hash(msgid)
+ const char *msgid;
+{
+
+ /*
+ * XXX should try a hashed lookup here, but to do so, we need to
+ * look inside the GPL'ed *.c and re-implement...
+ */
+ return NULL;
+}
+
+static const char *
+lookup_bsearch(msgid)
+ const char *msgid;
+{
+ size_t l;
+ int top, bottom, middle, omiddle;
+ int n;
+
+ l = strlen(msgid);
+
+ top = 0;
+ bottom = mohandle.mo.mo_nstring;
+ omiddle = -1;
+ while (1) {
+ if (top > bottom)
+ return NULL;
+ middle = (top + bottom) / 2;
+ /* avoid possible infinite loop, when the data is not sorted */
+ if (omiddle == middle)
+ return NULL;
+ if (middle < 0 || middle >= mohandle.mo.mo_nstring)
+ return NULL;
+
+ n = strcmp(msgid, mohandle.mo.mo_otable[middle].off);
+ if (n == 0)
+ return (const char *)mohandle.mo.mo_ttable[middle].off;
+ else if (n < 0)
+ bottom = middle;
+ else
+ top = middle;
+ omiddle = middle;
+ }
+
+ return NULL;
+}
+
+static const char *
+lookup(msgid)
+ const char *msgid;
+{
+ const char *v;
+
+ v = lookup_hash(msgid);
+ if (v)
+ return v;
+
+ return lookup_bsearch(msgid);
+}
+
+char *
+dcngettext(domainname, msgid1, msgid2, n, category)
+ const char *domainname;
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+ int category;
+{
+ const char *msgid;
+ char path[PATH_MAX];
+ static char lpath[PATH_MAX];
+ static char olpath[PATH_MAX];
+ const char *locale;
+ const char *language;
+ const char *cname;
+ const char *v;
+
+ msgid = (n == 1) ? msgid1 : msgid2;
+
+ if (!domainname)
+ domainname = __domainname;
+ cname = lookup_category(category);
+ if (!domainname || !cname)
+ goto fail;
+
+ language = getenv("LANGUAGE");
+ locale = setlocale(LC_MESSAGES, NULL); /*XXX*/
+ if (locale)
+ locale = split_locale(locale);
+ if (language && locale) {
+ if (strlen(language) + strlen(locale) + 2 > sizeof(lpath))
+ goto fail;
+ snprintf(lpath, sizeof(lpath), "%s:%s", language, locale);
+ } else if (language) {
+ if (strlen(language) + 1 > sizeof(lpath))
+ goto fail;
+ strlcpy(lpath, language, sizeof(lpath));
+ } else if (locale) {
+ if (strlen(locale) + 1 > sizeof(lpath))
+ goto fail;
+ strlcpy(lpath, locale, sizeof(lpath));
+ } else
+ goto fail;
+
+ /* don't bother looking it up if the values are the same */
+ if (strcmp(lpath, olpath) == 0)
+ goto found;
+
+ strlcpy(olpath, lpath, sizeof(olpath));
+
+ /* try to find appropriate file, from $LANGUAGE */
+ if (lookup_mofile(path, sizeof(path), __domainpath, lpath, cname,
+ domainname) != NULL)
+ goto found;
+
+ goto fail;
+
+found:
+ v = lookup(msgid);
+ if (v) {
+ /*
+ * XXX call iconv() here, if translated text is encoded
+ * differently from currently-selected encoding (locale).
+ * look at Content-type header in *.mo file, in string obtained
+ * by gettext("").
+ */
+
+ /*
+ * Given the amount of printf-format security issues, it may
+ * be a good idea to validate if the original msgid and the
+ * translated message format string carry the same printf-like
+ * format identifiers.
+ */
+
+ msgid = v;
+ }
+
+fail:
+ /* LINTED const cast */
+ return (char *)msgid;
+}
diff --git a/lib/libintl/libintl.h b/lib/libintl/libintl.h
new file mode 100644
index 00000000000..b792baa83a5
--- /dev/null
+++ b/lib/libintl/libintl.h
@@ -0,0 +1,47 @@
+/* $NetBSD: libintl.h,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+#ifndef __LIBINTL_H_DEFINED__
+#define __LIBINTL_H_DEFINED__
+
+#include <sys/cdefs.h>
+
+extern char *gettext __P((const char *));
+extern char *dgettext __P((const char *, const char *));
+extern char *dcgettext __P((const char *, const char *, int));
+extern char *ngettext __P((const char *, const char *, unsigned long int));
+extern char *dngettext __P((const char *, const char *, const char *,
+ unsigned long int));
+extern char *dcngettext __P((const char *, const char *, const char *,
+ unsigned long int, int));
+
+extern char *textdomain __P((const char *));
+extern char *bindtextdomain __P((const char *, const char *));
+extern char *bind_textdomain_codeset __P((const char *, const char *));
+
+#endif /*__LIBINTL_H_DEFINED__*/
diff --git a/lib/libintl/libintl_local.h b/lib/libintl/libintl_local.h
new file mode 100644
index 00000000000..a166c57744c
--- /dev/null
+++ b/lib/libintl/libintl_local.h
@@ -0,0 +1,75 @@
+/* $NetBSD: libintl_local.h,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+extern const char *__domainpath;
+extern const char *__domainname;
+
+#define MO_MAGIC 0x950412de
+#define MO_MAGIC_SWAPPED 0xde120495
+#define MO_REVISION 0
+
+#define GETTEXT_MMAP_MAX (64 * 1024) /*XXX*/
+
+/* *.mo file format */
+struct mo {
+ u_int32_t mo_magic; /* determines endian */
+ u_int32_t mo_revision; /* file format revision: 0 */
+ u_int32_t mo_nstring; /* N: number of strings */
+ u_int32_t mo_otable; /* O: original text table offset */
+ u_int32_t mo_ttable; /* T: translated text table offset */
+ u_int32_t mo_hsize; /* S: size of hashing table */
+ u_int32_t mo_hoffset; /* H: offset of hashing table */
+};
+
+struct moentry {
+ u_int32_t len; /* strlen(str), so region will be len + 1 */
+ u_int32_t off; /* offset of \0-terminated string */
+};
+
+/* libintl internal data format */
+struct moentry_h {
+ size_t len; /* strlen(str), so region will be len + 1 */
+ char *off; /* offset of \0-terminated string */
+};
+
+struct mo_h {
+ u_int32_t mo_magic; /* determines endian */
+ u_int32_t mo_revision; /* file format revision: 0 */
+ u_int32_t mo_nstring; /* N: number of strings */
+ struct moentry_h *mo_otable; /* O: original text table offset */
+ struct moentry_h *mo_ttable; /* T: translated text table offset */
+ const char *mo_header;
+ char *mo_charset;
+};
+
+struct mohandle {
+ char path[PATH_MAX];
+ void *addr; /* mmap'ed region */
+ size_t len;
+ struct mo_h mo; /* endian-flipped mo file header */
+};
diff --git a/lib/libintl/pathnames.h b/lib/libintl/pathnames.h
new file mode 100644
index 00000000000..ce6bb408115
--- /dev/null
+++ b/lib/libintl/pathnames.h
@@ -0,0 +1,29 @@
+/* $NetBSD: pathnames.h,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+#define _PATH_TEXTDOMAIN "/usr/share/locale"
diff --git a/lib/libintl/shlib_version b/lib/libintl/shlib_version
new file mode 100644
index 00000000000..5c159d4644d
--- /dev/null
+++ b/lib/libintl/shlib_version
@@ -0,0 +1,5 @@
+# $NetBSD: shlib_version,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $
+# Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=0
+minor=0
diff --git a/lib/libintl/textdomain.c b/lib/libintl/textdomain.c
new file mode 100644
index 00000000000..83d612bcdc8
--- /dev/null
+++ b/lib/libintl/textdomain.c
@@ -0,0 +1,91 @@
+/* $NetBSD: textdomain.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $ */
+
+/*-
+ * Copyright (c) 2000 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.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: textdomain.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <libintl.h>
+#include "libintl_local.h"
+#include "pathnames.h"
+
+static char dpath[PATH_MAX];
+static char dname[PATH_MAX]; /*XXX*/
+const char *__domainpath = _PATH_TEXTDOMAIN;
+const char *__domainname;
+
+/*
+ * set the default domainname for dcngettext() and friends.
+ */
+char *
+textdomain(domainname)
+ const char *domainname;
+{
+
+ return bindtextdomain(domainname, _PATH_TEXTDOMAIN);
+}
+
+char *
+bindtextdomain(domainname, dirname)
+ const char *domainname;
+ const char *dirname;
+{
+
+ if (strlen(dirname) + 1 > sizeof(dpath))
+ return NULL;
+ /* disallow relative path */
+ if (dirname[0] != '/')
+ return NULL;
+
+ if (strlen(domainname) + 1 > sizeof(dname))
+ return NULL;
+
+ strlcpy(dpath, dirname, sizeof(dpath));
+ __domainpath = dpath;
+ strlcpy(dname, domainname, sizeof(dname));
+ __domainname = dname;
+
+ /* LINTED const cast */
+ return (char *)domainname;
+}
+
+char *
+bind_textdomain_codeset(domainname, codeset)
+ const char *domainname;
+ const char *codeset;
+{
+
+ /* yet to be done - always fail */
+ return NULL;
+}