static char * handle_type_mo(Msg_node *mnp, struct msg_pack *mp) { char *result; switch (mnp->type) { case T_ILL_MO: /* invalid MO */ return (NULL); case T_SUN_MO: /* Sun MO found */ mp->status |= ST_SUN_MO_FOUND; if (mp->plural) { /* * *ngettext is called against * Sun MO file */ int exp = (mp->n == 1); result = (char *)mp->msgid1; if (!exp) result = (char *)mp->msgid2; return (result); } result = key_2_text(mnp->msg.sunmsg, mp->msgid1); if (!mnp->trusted) { result = check_format(mp->msgid1, result, 0); } return (result); case T_GNU_MO: /* GNU MO found */ mp->status |= ST_GNU_MO_FOUND; result = gnu_key_2_text(mnp->msg.gnumsg, get_codeset(mp->domain), mp); if (result == mp->msgid1 || result == mp->msgid2) { /* no valid msg found */ return (result); } /* valid msg found */ mp->status |= ST_GNU_MSG_FOUND; if (!mnp->trusted) { result = check_format(mp->msgid1, result, 0); if (result == mp->msgid1) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); } } return (result); default: /* this should never happen */ DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } /* NOTREACHED */ }
static char * handle_gnu_mo(struct cache_pack *cp, struct msg_pack *mp, Gettext_t *gt) { char *result; char *codeset = get_codeset(mp->domain); result = gnu_key_2_text(cp->mnp->msg.gnumsg, codeset, mp); if (mp->plural) { if (((result == mp->msgid1) && (mp->n == 1)) || ((result == mp->msgid2) && (mp->n != 1))) { return (NULL); } } else { if (result == mp->msgid1) { return (NULL); } } gt->c_m_node = cp->mnp; if (!cp->mnp->trusted) { result = check_format(mp->msgid1, result, 0); if (result == mp->msgid1) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); } } return (result); }
/* * dfltmsgstr * * choose an appropriate message by evaluating the plural expression, * and return it. */ static char * dfltmsgstr(Msg_g_node *gmnp, const char *msgstr, size_t msgstr_len, struct msg_pack *mp) { unsigned int pindex; size_t len; const char *p; #ifdef GETTEXT_DEBUG (void) printf("*************** dfltmsgstr(0x%p, \"%s\", %d, 0x%p)\n", (void *)gmnp, msgstr ? msgstr : "(null)", msgstr_len, (void *)mp); printgnumsg(gmnp, 0); printmp(mp, 0); #endif if (mp->plural) { if (gmnp->plural) { pindex = plural_eval(gmnp->plural, mp->n); } else { /* * This mo does not have plural information. * Using the English form. */ if (mp->n == 1) pindex = 0; else pindex = 1; } #ifdef GETTEXT_DEBUG (void) printf("plural_eval returned: %d\n", pindex); #endif if (pindex >= gmnp->nplurals) { /* should never happen */ pindex = 0; } p = msgstr; for (; pindex != 0; pindex--) { len = msgstr_len - (p - msgstr); p = memchr(p, '\0', len); if (!p) { /* * null byte not found * this should never happen */ char *result; DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } p++; /* skip */ } return ((char *)p); } return ((char *)msgstr); }
char * _real_gettext_u(const char *domain, const char *msgid1, const char *msgid2, unsigned long int ln, int category, int plural) { char msgfile[MAXPATHLEN]; /* 1024 */ char mydomain[TEXTDOMAINMAX + 1]; /* 256 + 1 */ char *cur_binding; /* points to current binding in list */ char *cur_locale, *cur_domain, *result, *nlspath; char *msgloc, *cb, *cur_domain_binding; char *language; unsigned int n = (unsigned int)ln; /* we don't need long for n */ uint32_t cur_domain_len, cblen; uint32_t hash_domain; struct msg_pack *mp, omp; #ifdef GETTEXT_DEBUG gprintf(0, "*************** _real_gettext_u(\"%s\", \"%s\", " "\"%s\", %d, %d, %d)\n", domain ? domain : "NULL", msgid1 ? msgid1 : "NULL", msgid2 ? msgid2 : "NULL", n, category, plural); gprintf(0, "***************** global_gt: 0x%p\n", global_gt); printgt(global_gt, 1); #endif if (msgid1 == NULL) return (NULL); mp = memset(&omp, 0, sizeof (omp)); /* msg pack */ /* * category may be LC_MESSAGES or LC_TIME * locale contains the value of 'category' */ cur_locale = setlocale(category, NULL); language = getenv("LANGUAGE"); /* for GNU */ if (language) { if (!*language || strchr(language, '/') != NULL) { /* * LANGUAGE is an empty string or * LANGUAGE contains '/'. * Ignore it. */ language = NULL; } } /* * Query the current domain if domain argument is NULL pointer */ mydomain[0] = '\0'; if (domain == NULL) { /* * if NULL is specified for domainname, * use the currently bound domain. */ cur_domain = _textdomain_u(NULL, mydomain); } else if (!*domain) { /* * if an empty string is specified */ cur_domain = DEFAULT_DOMAIN; } else { cur_domain = (char *)domain; } hash_domain = get_hashid(cur_domain, &cur_domain_len); if (cur_domain_len > TEXTDOMAINMAX) { /* domain is invalid, return msg_id */ DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } nlspath = getenv("NLSPATH"); /* get the content of NLSPATH */ if (nlspath == NULL || !*nlspath) { /* no NLSPATH is defined in the environ */ if ((*cur_locale == 'C') && (*(cur_locale + 1) == '\0')) { /* * If C locale, * return the original msgid immediately. */ DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } nlspath = NULL; } else { /* NLSPATH is set */ int ret; msgloc = setlocale(LC_MESSAGES, NULL); ret = process_nlspath(cur_domain, msgloc, (const char *)nlspath, &cur_binding); if (ret == -1) { /* error occurred */ DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } else if (ret == 0) { nlspath = NULL; } } cur_domain_binding = _real_bindtextdomain_u(cur_domain, NULL, TP_BINDING); if (cur_domain_binding == NULL) { DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } mp->msgid1 = msgid1; mp->msgid2 = msgid2; mp->msgfile = msgfile; mp->domain = cur_domain; mp->binding = cur_domain_binding; mp->locale = cur_locale; mp->language = language; mp->domain_len = cur_domain_len; mp->n = n; mp->category = category; mp->plural = plural; mp->hash_domain = hash_domain; /* * Spec1170 requires that we use NLSPATH if it's defined, to * override any system default variables. If NLSPATH is not * defined or if a message catalog is not found in any of the * components (bindings) specified by NLSPATH, dcgettext_u() will * search for the message catalog in either a) the binding path set * by any previous application calls to bindtextdomain() or * b) the default binding path (/usr/lib/locale). Save the original * binding path so that we can search it if the message catalog * is not found via NLSPATH. The original binding is restored before * returning from this routine because the gettext routines should * not change the binding set by the application. This allows * bindtextdomain() to be called once for all gettext() calls in the * application. */ /* * First, examine NLSPATH */ if (nlspath) { /* * NLSPATH binding has been successfully built */ #ifdef GETTEXT_DEBUG gprintf(0, "************************** examining NLSPATH\n"); gprintf(0, " cur_binding: \"%s\"\n", cur_binding ? cur_binding : "(null)"); #endif mp->nlsp = 1; /* * cur_binding always ends with ':' before a null * termination. */ while (*cur_binding) { cb = cur_binding; while (*cur_binding != ':') cur_binding++; cblen = cur_binding - cb; cur_binding++; if (cblen >= MAXPATHLEN) { /* cur_binding too long */ DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } (void) memcpy(mp->msgfile, cb, cblen); *(mp->msgfile + cblen) = '\0'; #ifdef GETTEXT_DEBUG gprintf(0, "*******************" "********************* \n"); gprintf(0, " msgfile: \"%s\"\n", msgfile ? msgfile : "(null)"); gprintf(0, "*******************" "********************* \n"); #endif result = handle_mo(mp); if (result) { return (result); } } } mp->nlsp = 0; mp->binding = cur_domain_binding; /* * Next, examine LANGUAGE */ if (language) { char *ret_msg; ret_msg = handle_lang(mp); if (ret_msg != NULL) { /* valid msg found in GNU MO */ return (ret_msg); } /* * handle_lang() may have overridden locale */ mp->locale = cur_locale; mp->status = 0; } /* * Finally, handle a single binding */ #ifdef GETTEXT_DEBUG *mp->msgfile = '\0'; #endif if (mk_msgfile(mp) == NULL) { DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } result = handle_mo(mp); if (result) { return (result); } DFLTMSG(result, msgid1, msgid2, n, plural); return (result); } /* _real_gettext_u */
/* * handle_mo() returns NULL if invalid MO found. */ char * handle_mo(struct msg_pack *mp) { int fd; char *result; struct stat64 statbuf; Msg_node *mnp; Gettext_t *gt = global_gt; #define CONNECT_ENTRY \ mnp->next = gt->m_node; \ gt->m_node = mnp; \ gt->c_m_node = mnp #ifdef GETTEXT_DEBUG gprintf(0, "*************** handle_mo(0x%p)\n", (void *)mp); printmp(mp, 1); #endif mnp = check_cache(mp); if (mnp != NULL) { /* cache found */ return (handle_type_mo(mnp, mp)); } /* * Valid entry not found in the cache */ mnp = calloc(1, sizeof (Msg_node)); if (mnp == NULL) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } mnp->hashid = mp->hash_domain; mnp->path = strdup(mp->msgfile); if (mnp->path == NULL) { free(mnp); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, !mp->nlsp); if ((fd == -1) || (statbuf.st_size > LONG_MAX)) { if (fd != -1) (void) close(fd); mnp->type = T_ILL_MO; CONNECT_ENTRY; return (NULL); } mp->fsz = (size_t)statbuf.st_size; mp->addr = mmap(NULL, mp->fsz, PROT_READ, MAP_SHARED, fd, 0); (void) close(fd); if (mp->addr == MAP_FAILED) { free(mnp->path); free(mnp); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } if (setmsg(mnp, (char *)mp->addr, mp->fsz) == -1) { free(mnp->path); free(mnp); (void) munmap(mp->addr, mp->fsz); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } mnp->trusted = mp->trusted; CONNECT_ENTRY; return (handle_type_mo(mnp, mp)); }
/* * gnu_key_2_text * * Extracts msgstr from the GNU MO file */ char * gnu_key_2_text(Msg_g_node *gmnp, const char *codeset, struct msg_pack *mp) { char *result, *msgstr; size_t msgstr_len; unsigned int midx; int ret; char *conv_msgstr, *conv_dst; size_t *p; size_t conv_msgstr_len, buflen; iconv_t fd; int conversion, new_encoding; unsigned int num_of_str; #ifdef GETTEXT_DEBUG (void) printf("*************** gnu_key_2_text(" "0x%p, \"%s\", 0x%p)\n", (void *)gmnp, codeset ? codeset : "(null)", (void *)mp); printgnumsg(gmnp, 0); printmp(mp, 0); #endif /* first checks if header entry has been processed */ if (!(gmnp->flag & ST_CHK)) { char *msg_header; msg_header = gnu_msgsearch(gmnp, "", NULL, NULL); ret = parse_header((const char *)msg_header, gmnp); if (ret == -1) { /* fatal error */ DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } gmnp->flag |= ST_CHK; } msgstr = gnu_msgsearch(gmnp, mp->msgid1, &msgstr_len, &midx); if (msgstr == mp->msgid1) { /* not found */ DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } #ifdef GETTEXT_DEBUG printgnumsg(gmnp, 0); #endif if (!gmnp->dst_encoding) { /* * destination encoding has not been set. */ char *dupcodeset = strdup(codeset); if (!dupcodeset) { /* strdup failed */ result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } gmnp->dst_encoding = dupcodeset; if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 0) { /* * 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->fd && (gmnp->fd != (iconv_t)-1)) { (void) iconv_close(gmnp->fd); gmnp->fd = (iconv_t)-1; } if (gmnp->conv_msgstr) free_conv_msgstr(gmnp); conversion = 1; new_encoding = 1; } } else { /* * dst encoding has been already set. */ if (strcmp(gmnp->dst_encoding, codeset) == 0) { /* * dst encoding and target encoding are the same. */ if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 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 */ if (gmnp->fd == (iconv_t)-1) { /* * iconv_open should have failed before * Assume this conversion is invalid */ conversion = 0; } else { if (!gmnp->conv_msgstr) { /* * memory allocation for * conv_msgstr should * have failed before. */ new_encoding = 1; if (gmnp->fd) (void) iconv_close( gmnp->fd); gmnp->fd = (iconv_t)-1; } } } } else { /* * dst encoding is different from target encoding. * It has changed since before. */ char *dupcodeset = strdup(codeset); if (!dupcodeset) { result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } free(gmnp->dst_encoding); gmnp->dst_encoding = dupcodeset; if (strcmp(gmnp->dst_encoding, gmnp->src_encoding) == 0) { /* * dst encoding and src encoding are the same. * now, no conversion required. */ conversion = 0; } else { /* * dst encoding is different from src encoding. * new conversion required. */ conversion = 1; new_encoding = 1; } if (gmnp->fd && (gmnp->fd != (iconv_t)-1)) { (void) iconv_close(gmnp->fd); } if (gmnp->fd != (iconv_t)-1) { gmnp->fd = (iconv_t)-1; } if (gmnp->conv_msgstr) free_conv_msgstr(gmnp); } } if (conversion == 0) { /* no conversion */ result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } /* conversion required */ if (new_encoding == 0) { /* dst codeset hasn't been changed since before */ if (!gmnp->conv_msgstr[midx]) { /* this msgstr hasn't been converted yet */ conv_msgstr_len = do_conv(gmnp->fd, &conv_dst, (const char *)msgstr, msgstr_len); if (conv_msgstr_len == (size_t)-1) { result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } buflen = (conv_msgstr_len + sizeof (size_t)); /* allign to sizeof (size_t) */ if (buflen % sizeof (size_t)) buflen += (sizeof (size_t) - (buflen % sizeof (size_t))); p = (size_t *)malloc(buflen); if (!p) { free(conv_dst); result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } *p = conv_msgstr_len; (void) memcpy(p + 1, conv_dst, conv_msgstr_len); free(conv_dst); gmnp->conv_msgstr[midx] = (char *)p; conv_msgstr = (char *)(p + 1); } else { /* this msgstr is in the conversion cache */ /* LINTED */ size_t *cmsg = (size_t *)gmnp->conv_msgstr[midx]; conv_msgstr_len = *cmsg; conv_msgstr = (char *)(cmsg + 1); } result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp); return (result); } /* new conversion */ #ifdef GETTEXT_DEBUG (void) printf("******* calling iconv_open()\n"); (void) printf(" dst: \"%s\", src: \"%s\"\n", gmnp->dst_encoding, gmnp->src_encoding); #endif fd = iconv_open(gmnp->dst_encoding, gmnp->src_encoding); gmnp->fd = fd; if (fd == (iconv_t)-1) { /* * iconv_open() failed. * no conversion */ result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } num_of_str = SWAP(gmnp, gmnp->msg_file_info->num_of_str); gmnp->conv_msgstr = (char **)calloc((size_t)num_of_str, sizeof (char *)); if (!gmnp->conv_msgstr) { /* malloc failed */ result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } conv_msgstr_len = do_conv(gmnp->fd, &conv_dst, (const char *)msgstr, msgstr_len); if (conv_msgstr_len == (size_t)-1) { free_conv_msgstr(gmnp); result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } buflen = (conv_msgstr_len + sizeof (size_t)); /* allign to sizeof (size_t) */ if (buflen % sizeof (size_t)) buflen += (sizeof (size_t) - (buflen % sizeof (size_t))); p = (size_t *)malloc(buflen); if (!p) { free(conv_dst); free_conv_msgstr(gmnp); result = dfltmsgstr(gmnp, msgstr, msgstr_len, mp); return (result); } *p = conv_msgstr_len; (void) memcpy(p + 1, conv_dst, conv_msgstr_len); free(conv_dst); gmnp->conv_msgstr[midx] = (char *)p; conv_msgstr = (char *)(p + 1); result = dfltmsgstr(gmnp, conv_msgstr, conv_msgstr_len, mp); return (result); }
/* * handle_lang * * take care of the LANGUAGE specification */ char * handle_lang(struct cache_pack *cp, struct msg_pack *mp) { Gettext_t *gt = global_gt; struct stat64 statbuf; const char *p, *op, *q; char *locale = NULL, *olocale, *result; unsigned int hash_locale; size_t locale_len, olocale_len = 0; int gnu_mo_found = 0; int fd; int ret; #ifdef GETTEXT_DEBUG (void) printf("*************** handle_lang(0x%p, 0x%p)\n", (void *)cp, (void *)mp); printcp(cp, 0); printmp(mp, 0); #endif p = mp->language; while (*p) { op = p; q = strchr(p, ':'); if (!q) { locale_len = strlen(p); p += locale_len; } else { locale_len = q - p; p += locale_len + 1; } if ((locale_len >= MAXPATHLEN) || (locale_len == 0)) { /* illegal locale name */ continue; } if (olocale_len < locale_len) { olocale = locale; locale = (char *)realloc(locale, locale_len + 1); if (!locale) { if (olocale) free(olocale); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } olocale_len = locale_len; } (void) memcpy(locale, op, locale_len); locale[locale_len] = '\0'; hash_locale = get_hashid(locale, NULL); mp->locale = locale; mp->hash_locale = hash_locale; mp->locale_len = locale_len; #ifdef GETTEXT_DEBUG *mp->msgfile = '\0'; #endif if (mk_msgfile(mp) == NULL) { /* illegal locale name */ continue; } cp->node_hash = NULL; ret = check_cache(cp, mp); if (ret) { /* * found in cache */ switch (cp->mnp->type) { case T_ILL_MO: /* invalid MO */ continue; case T_SUN_MO: /* Solaris MO */ goto out_loop; case T_GNU_MO: /* GNU MO */ gnu_mo_found = 1; result = handle_gnu_mo(cp, mp, gt); if (result) { free(locale); return (result); } continue; } /* NOTREACHED */ } /* * not found in cache */ fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, 1); if ((fd == -1) || (statbuf.st_size > LONG_MAX)) { if (connect_invalid_entry(cp, mp) == -1) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); free(locale); return (result); } continue; } mp->fsz = (size_t)statbuf.st_size; mp->addr = mmap(0, mp->fsz, PROT_READ, MAP_SHARED, fd, 0); (void) close(fd); if (mp->addr == (caddr_t)-1) { if (connect_invalid_entry(cp, mp) == -1) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); free(locale); return (result); } continue; } cp->mnp = create_mnp(mp); if (!cp->mnp) { free(locale); free_mnp_mp(cp->mnp, mp); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } if (setmsg(cp->mnp, (char *)mp->addr, mp->fsz) == -1) { free(locale); free_mnp_mp(cp->mnp, mp); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } if (!cp->cacheline) { cp->cnp = create_cnp(cp->mnp, mp); if (!cp->cnp) { free(locale); free_mnp_mp(cp->mnp, mp); DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); return (result); } } cp->mnp->trusted = mp->trusted; connect_entry(cp); switch (cp->mnp->type) { case T_ILL_MO: /* invalid MO */ continue; case T_SUN_MO: /* Solaris MO */ goto out_loop; case T_GNU_MO: /* GNU MO */ gnu_mo_found = 1; result = handle_gnu_mo(cp, mp, gt); if (result) { free(locale); return (result); } continue; } /* NOTREACHED */ } out_loop: if (gnu_mo_found) { DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); free(locale); return (result); } if (locale) free(locale); return (NULL); }