gettext_gnu.c revision f13ac6397da869bb7b7bc8a9f0b2e8fff530c346
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "synonyms.h"
#include "mtlib.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <thread.h>
#include <synch.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <inttypes.h>
#include "libc.h"
#include "msgfmt.h"
#include "nlspath_checks.h"
#include "gettext.h"
#ifdef DEBUG
#include <assert.h>
#endif
/* The following symbols are just for GNU binary compatibility */
int _nl_msg_cat_cntr;
int *_nl_domain_bindings;
static const char *nullstr = "";
#define CHARSET_MOD "charset="
#define NPLURALS_MOD "nplurals="
#define PLURAL_MOD "plural="
/*
* free_conv_msgstr
*
* release the memory allocated for storing code-converted messages
*
* f
* 0: do not free gmnp->conv_msgstr
* 1: free gmnp->conv_msgstr
*/
static void
{
uint32_t i, num_of_conv;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** free_conv_msgstr(0x%p, %d)\n",
(void *)gmnp, f);
#endif
for (i = 0; i < num_of_conv; i++) {
if (gmnp->conv_msgstr[i]) {
}
}
if (f) {
}
}
/*
* dfltmsgstr
*
* choose an appropriate message by evaluating the plural expression,
* and return it.
*/
static char *
{
unsigned int pindex;
const char *p;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** dfltmsgstr(0x%p, \"%s\", %u, 0x%p)\n",
(void *)gmnp,
#endif
} else {
/*
* This mo does not have plural information.
* Using the English form.
*/
if (mp->n == 1)
pindex = 0;
else
pindex = 1;
}
#ifdef GETTEXT_DEBUG
#endif
/* should never happen */
pindex = 0;
}
p = msgstr;
if (p == NULL) {
/*
* null byte not found
* this should never happen
*/
char *result;
return (result);
}
p++; /* skip */
}
return ((char *)p);
}
return ((char *)msgstr);
}
/*
* parse_header
*
* parse the header entry of the GNU MO file and
* extract the src encoding and the plural information of the MO file
*/
static int
{
char *charset_str;
char *nplurals_str, *plural_str;
char *p, *q;
unsigned int nplurals;
int ret;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** parse_header(\"%s\", 0x%p)\n",
#endif
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** exiting parse_header\n");
gprintf(0, "no header\n");
#endif
return (0);
}
if (charset_str == NULL) {
} else {
p = charset_str + CHARSET_LEN;
q = p;
while ((*q != ' ') && (*q != '\t') &&
(*q != '\n')) {
q++;
}
len = q - p;
if (len > 0) {
return (-1);
}
} else {
}
}
/* no valid plural specification */
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** exiting parse_header\n");
gprintf(0, "no plural entry\n");
#endif
return (0);
} else {
p = nplurals_str + NPLURALS_LEN;
while (*p && isspace((unsigned char)*p)) {
p++;
}
if (p != q) {
} else {
}
p = plural_str + PLURAL_LEN;
#ifdef GETTEXT_DEBUG
gprintf(0, "plural_str: \"%s\"\n", p);
#endif
if (ret == 0) {
/* parse succeeded */
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** exiting parse_header\n");
gprintf(0, "charset: \"%s\"\n",
#endif
return (0);
} else if (ret == 1) {
/* parse error */
return (0);
} else {
/* fatal error */
if (charset)
return (-1);
}
}
/* NOTREACHED */
}
/*
* handle_lang
*
* take care of the LANGUAGE specification
*/
char *
{
const char *p, *op, *q;
char *result;
char locale[MAXPATHLEN];
#ifdef GETTEXT_DEBUG
#endif
while (*p) {
op = p;
q = strchr(p, ':');
if (q == NULL) {
locale_len = strlen(p);
p += locale_len;
} else {
locale_len = q - p;
p += locale_len + 1;
}
/* illegal locale name */
continue;
}
#ifdef GETTEXT_DEBUG
#endif
/* illegal locale name */
continue;
}
return (result);
break;
}
/*
* no valid locale found, Sun MO found, or
* GNU MO found but no valid msg found there.
*/
/*
* GNU MO found but no valid msg found there.
* returning DFLTMSG.
*/
return (result);
}
return (NULL);
}
/*
* gnu_msgsearch
*
* Searchs the translation message for the specified msgid1.
* Hash algorithm used in this function is Open Addressing
* with Double Hashing:
* H(k, i) = (H1(k) + i * H2(k)) mod M
* H1(k) = hashvalue % M
* H2(k) = 1 + (hashvalue % (M - 2))
*
* Ref: The Art of Computer Programming Volume 3
* Sorting and Searching, second edition
* Donald E Knuth
*/
static char *
{
char *base;
char *msg;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** gnu_msgsearch(0x%p, \"%s\", "
"0x%p, 0x%p)\n",
#endif
/*
* Revision 0 and
* No hash table exists or
* hash size is enough small.
*/
char *msg_id_str;
int val;
top = 0;
bottom = num_of_str;
msg_id_str = base +
if (val < 0) {
} else if (val > 0) {
} else {
*msgstrlen = (unsigned int)
return (base +
}
}
/* not found */
return ((char *)msgid1);
}
/* use hash table */
for (;;) {
if (hash_val == 0) {
/* not found */
return ((char *)msgid1);
}
if (hash_val <= num_of_str) {
/* static message */
} else {
/* rev 0 does not have dynamic message */
return ((char *)msgid1);
}
/* dynamic message */
}
/* found */
break;
}
}
/* msgstrlen should include a null termination */
if (hash_val <= num_of_str) {
} else {
}
return (msg);
}
/*
* do_conv
*
* Converts the specified string from the src encoding
* to the dst encoding by calling iconv()
*/
static uint32_t *
{
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** do_conv("
"0x%p, \"%s\", %d)\n",
#endif
return (NULL);
}
for (;;) {
errno = 0;
#ifdef GETTEXT_DEBUG
gprintf(0, "******* calling iconv()\n");
#endif
#ifdef GETTEXT_DEBUG
gprintf(0, "******* iconv detected E2BIG\n");
#endif
return (NULL);
}
#ifdef GETTEXT_DEBUG
#endif
continue;
} else {
break;
}
}
break;
}
/* shrink the buffer */
return (NULL);
}
}
#ifdef GETTEXT_DEBUG
gprintf(0, "******* exiting do_conv()\n");
#endif
return (ptr);
}
/*
* conv_msg
*/
static char *
{
char *conv_msgstr, *result;
gmnp->conv_msgstr =
/* malloc failed */
return (result);
}
}
return (result);
}
return (result);
}
/*
* gnu_key_2_text
*
* Extracts msgstr from the GNU MO file
*/
char *
{
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** gnu_key_2_text("
"0x%p, \"%s\", 0x%p)\n",
#endif
/* first checks if header entry has been processed */
char *msg_header;
if (ret == -1) {
/* fatal error */
return (result);
}
}
/* not found */
return (result);
}
#ifdef GETTEXT_DEBUG
#endif
/*
* destination encoding has not been set.
*/
if (dupcodeset == NULL) {
/* strdup failed */
return (result);
}
/*
* target encoding and src encoding
* are the same.
* No conversion required.
*/
conversion = 0;
} else {
/*
* target encoding is different from
* src encoding.
* New conversion required.
*/
/* sanity check */
}
if (gmnp->conv_msgstr)
free_conv_msgstr(gmnp, 0);
conversion = 1;
new_encoding = 1;
}
} else {
/*
* dst encoding has been already set.
*/
/*
* dst encoding and target encoding are the same.
*/
== 0) {
/*
* dst encoding and src encoding are the same.
* No conversion required.
*/
conversion = 0;
} else {
/*
* dst encoding is different from src encoding.
* current conversion is valid.
*/
conversion = 1;
new_encoding = 0;
/* checks if iconv_open has succeeded before */
/*
* iconv_open should have failed before
* Assume this conversion is invalid
*/
conversion = 0;
} else {
/*
* memory allocation for
* conv_msgstr should
* have failed before.
*/
new_encoding = 1;
(void) iconv_close(
}
}
}
} else {
/*
* dst encoding is different from target encoding.
* It has changed since before.
*/
if (dupcodeset == NULL) {
msgstr_len, mp);
return (result);
}
== 0) {
/*
* dst encoding and src encoding are the same.
* now, no conversion required.
*/
conversion = 0;
if (gmnp->conv_msgstr)
} else {
/*
* dst encoding is different from src encoding.
* new conversion required.
*/
conversion = 1;
new_encoding = 1;
if (gmnp->conv_msgstr)
free_conv_msgstr(gmnp, 0);
}
}
}
}
}
if (conversion == 0) {
/* no conversion */
return (result);
}
/* conversion required */
if (new_encoding == 0) {
/* dst codeset hasn't been changed since before */
char *conv_msgstr;
/* this msgstr hasn't been converted yet */
return (result);
}
/* this msgstr is in the conversion cache */
conv_msgstr_len = *cmsg;
return (result);
}
/* new conversion */
#ifdef GETTEXT_DEBUG
gprintf(0, "******* calling iconv_open()\n");
gprintf(0, " dst: \"%s\", src: \"%s\"\n",
#endif
/*
* iconv_open() failed.
* no conversion
*/
return (result);
}
return (result);
}
#define PRIS(P, x) {\
/* x/N/ */ P(x, 8), P(x, 16), P(x, 32), P(x, 64), \
}
#define PRI_BIAS_LEAST 4
#define PRI_BIAS_FAST 8
#define PRI_BIAS_MAX 12
#define PRI_BIAS_PTR 13
static struct {
const char type;
const char **str_table;
const char *len_table;
} pri_table[] = {
};
static struct {
const char *name;
const char nlen;
const char want_digits;
const char bias;
} special_table[] = {
};
/*
* conv_macro() returns the conversion specifier corresponding
* to the macro name specified in 'name'. 'len' contains the
* length of the macro name including the null termination.
* '*elen' will be set to the length of the returning conversion
* specifier without the null termination.
*/
static const char *
{
const char **tbl;
const char *ltbl;
char *next;
if (len == 2) {
if (*str == 'I') {
/* Solaris does not support %I */
*lenp = 0;
return ("");
}
return (NULL);
}
return (NULL);
str += 3;
for (i = 0; i < n; i++) {
break;
}
if (i == n)
return (NULL);
str++;
idx = want_digits = 0;
/* PRIx/N/ */
bias = 0;
want_digits = 1;
} else {
n = sizeof (special_table) / sizeof (special_table[0]);
for (i = 0; i < n; i++) {
break;
}
}
if (i == n)
return (NULL);
}
if (want_digits) {
return (NULL);
/* see if it is 8/16/32/64 */
if (n == num)
break;
}
if (idx == 4)
return (NULL);
}
if (*str != '\0') {
/* unknow format */
return (NULL);
}
}
static gnu_d_macro_t *
{
char *base = (char *)p->msg_file_info;
struct gnu_msg_ent *d_macro_tbl;
const char *e_macname;
char *macname;
/* number of the dynamic macros */
return (NULL);
/* pointer to the dynamic strings table */
for (i = 0; i < num_of_d_macro; i++) {
/*
* sanity check
* maclen includes a null termination.
*/
return (NULL);
}
return (NULL);
}
}
return (d_macro);
}
static char *
{
char *base = (char *)p->msg_file_info;
struct gnu_dynamic_tbl *d_info;
struct gnu_dynamic_ent *entry;
#define MEM_INCR (1024)
d_macro = expand_macros(p);
return (NULL);
/* number of dynamic messages */
num_of_d_str = p->num_of_d_str;
mchunk_size = 0; /* size of the allocated memory in mchunk */
used = 0; /* size of the used memory in mchunk */
for (j = 0; j < num_of_d_str; j++) {
for (;;) {
if (need >= mchunk_size) {
char *t;
size_t n = mchunk_size;
do {
n += MEM_INCR;
} while (n <= need);
if (t == NULL) {
return (NULL);
}
mchunk = t;
mchunk_size = n;
}
if (didx == NOMORE_DYNAMIC_MACRO) {
/*
* Last segment of a static
* msg string contains a null
* termination, so an explicit
* null termination is not required
* here.
*/
break;
}
entry++; /* to next entry */
}
/*
* e_msgs[][].len does not include a null termination
*/
}
}
/* shrink mchunk to 'used' */
{
char *t;
if (t == NULL) {
return (NULL);
}
mchunk = t;
}
return (mchunk);
}
static int
{
int i;
#ifdef GETTEXT_DEBUG
gprintf(0, "******* entering build_rev1_info(0x%p)\n", p);
printgnumsg(p, 1);
#endif
if (p->hash_table == NULL) {
/* Revision 1 always requires the hash table */
return (-1);
}
num_of_str = p->num_of_str;
num_of_d_str = p->num_of_d_str;
return (-1);
}
(chunk + hash_mem_size);
return (-1);
}
/* copy the original hash table into the dynamic hash table */
for (i = 0; i < hash_size; i++) {
}
/* fill in the dynamic hash table with dynamic messages */
for (i = 0; i < num_of_d_str; i++) {
NULL);
}
p->hash_table = d_hash;
#ifdef GETTEXT_DEBUG
print_rev1_info(p);
gprintf(0, "******* exiting build_rev1_info()\n");
printgnumsg(p, 1);
#endif
return (0);
}
/*
* gnu_setmsg
*
* INPUT
* mnp - message node
* addr - address to the mmapped file
* size - size of the file
*
* RETURN
* 0 - either T_GNU_MO or T_ILL_MO has been set
* -1 - failed
*/
int
{
struct gnu_msg_info *gnu_header;
Msg_g_node *p;
#ifdef GETTEXT_DEBUG
gprintf(0, "******** entering gnu_setmsg(0x%p, 0x%p, %lu)\n",
#endif
/* checks the GNU MAGIC number */
if (size < sizeof (struct gnu_msg_info)) {
/* invalid mo file */
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting gnu_setmsg\n");
#endif
return (0);
}
if (p == NULL) {
return (-1);
}
p->msg_file_info = gnu_header;
switch (gnu_header->revision) {
case GNU_REVISION_0_1:
case GNU_REVISION_1_1:
break;
}
switch (gnu_header->revision) {
case GNU_REVISION_0_1_SWAPPED:
case GNU_REVISION_1_1_SWAPPED:
break;
}
} else {
/* invalid mo file */
free(p);
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting gnu_setmsg\n");
#endif
return (0);
}
/* Revision 1 */
struct gnu_msg_rev1_info *rev1_header;
rev1_header = (struct gnu_msg_rev1_info *)
p->rev1_header = rev1_header;
if (build_rev1_info(p) == -1) {
free(p);
#ifdef GETTEXT_DEBUG
gprintf(0, "******** exiting gnu_setmsg: "
"build_rev1_info() failed\n");
#endif
return (-1);
}
}
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting gnu_setmsg\n");
#endif
return (0);
}
/*
* get_hash_index
*
* Returns the index to an empty slot in the hash table
* for the specified hash_value.
*/
static uint32_t
{
for (;;) {
/* found an empty slot */
return (idx);
}
}
/* NOTREACHED */
}