diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/misc/translate.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libast/misc/translate.c')
-rw-r--r-- | src/lib/libast/misc/translate.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/lib/libast/misc/translate.c b/src/lib/libast/misc/translate.c new file mode 100644 index 0000000..0c9fbe3 --- /dev/null +++ b/src/lib/libast/misc/translate.c @@ -0,0 +1,437 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * AT&T Research and SCO + * ast l10n message translation + */ + +#include "lclib.h" + +#include <cdt.h> +#include <error.h> +#include <mc.h> +#include <nl_types.h> + +#ifndef DEBUG_trace +#define DEBUG_trace 0 +#endif + +#define NOCAT ((nl_catd)-1) +#define GAP 100 + +typedef struct +{ + Dtlink_t link; /* dictionary link */ + Dt_t* messages; /* message dictionary handle */ + nl_catd cat; /* message catalog handle */ + int debug; /* special debug locale */ + const char* locale; /* message catalog locale */ + const char* nlspath; /* message catalog NLSPATH */ + char name[1]; /* catalog name */ +} Catalog_t; + +typedef struct +{ + Dtlink_t link; /* dictionary link */ + Catalog_t* cat; /* current catalog pointer */ + int set; /* set number */ + int seq; /* sequence number */ + char text[1]; /* message text */ +} Message_t; + +typedef struct +{ + Sfio_t* sp; /* temp string stream */ + int off; /* string base offset */ +} Temp_t; + +typedef struct +{ + Dtdisc_t message_disc; /* message dict discipline */ + Dtdisc_t catalog_disc; /* catalog dict discipline */ + Dt_t* catalogs; /* catalog dictionary handle */ + Sfio_t* tmp; /* temporary string stream */ + int error; /* no dictionaries! */ + char null[1]; /* null string */ +} State_t; + +static State_t state = +{ + { offsetof(Message_t, text), 0, 0 }, + { offsetof(Catalog_t, name), 0, 0 }, +}; + +static int +tempget(Sfio_t* sp) +{ + if (sfstrtell(sp) > sfstrsize(sp) / 2) + sfstrseek(sp, 0, SEEK_SET); + return sfstrtell(sp); +} + +static char* +tempuse(Sfio_t* sp, int off) +{ + sfputc(sp, 0); + return sfstrbase(sp) + off; +} + +/* + * add msg to dict + */ + +static int +entry(Dt_t* dict, int set, int seq, const char* msg) +{ + Message_t* mp; + + if (!(mp = newof(0, Message_t, 1, strlen(msg)))) + return 0; + strcpy(mp->text, msg); + mp->set = set; + mp->seq = seq; + if (!dtinsert(dict, mp)) + { + free(mp); + return 0; + } +#if DEBUG_trace > 1 +sfprintf(sfstderr, "AHA#%d:%s set %d seq %d msg `%s'\n", __LINE__, __FILE__, set, seq, msg); +#endif + return 1; +} + +/* + * find catalog in locale and return catopen() descriptor + */ + +static nl_catd +find(const char* locale, const char* catalog) +{ + char* o; + nl_catd d; + char path[PATH_MAX]; + + if (!mcfind(locale, catalog, LC_MESSAGES, 0, path, sizeof(path)) || (d = catopen(path, NL_CAT_LOCALE)) == NOCAT) + { + if (locale == (const char*)lc_categories[AST_LC_MESSAGES].prev) + o = 0; + else if (o = setlocale(LC_MESSAGES, NiL)) + { + ast.locale.set |= AST_LC_internal; + setlocale(LC_MESSAGES, locale); + } + d = catopen(catalog, NL_CAT_LOCALE); + if (o) + { + setlocale(LC_MESSAGES, o); + ast.locale.set &= ~AST_LC_internal; + } + } + return d; +} + +/* + * initialize the catalog s by loading in the default locale messages + */ + +static Catalog_t* +init(register char* s) +{ + register Catalog_t* cp; + register char* u; + register int n; + register int m; + register int set; + nl_catd d; + + static const int sets[] = { AST_MESSAGE_SET, 1 }; + + /* + * insert into the catalog dictionary + */ + + if (!(cp = newof(0, Catalog_t, 1, strlen(s)))) + return 0; + strcpy(cp->name, s); + if (!dtinsert(state.catalogs, cp)) + { + free(cp); + return 0; + } + cp->cat = NOCAT; + + /* + * locate the default locale catalog + */ + + if ((d = find("C", s)) != NOCAT) + { + /* + * load the default locale messages + * this assumes one mesage set for ast (AST_MESSAGE_SET or fallback to 1) + * different packages can share the same message catalog + * name by using different message set numbers + * see <mc.h> mcindex() + * + * this method requires a scan of each catalog, and the + * catalogs do not advertise the max message number, so + * we assume there are no messages after a gap of GAP + * missing messages + */ + + if (cp->messages = dtopen(&state.message_disc, Dtset)) + { + n = m = 0; + for (;;) + { + n++; + if (((s = catgets(d, set = AST_MESSAGE_SET, n, state.null)) && *s || (s = catgets(d, set = 1, n, state.null)) && *s) && entry(cp->messages, set, n, s)) + m = n; + else if ((n - m) > GAP) + break; + } + if (!m) + { + dtclose(cp->messages); + cp->messages = 0; + } + } + catclose(d); + } + return cp; +} + +/* + * return the C locale message pointer for msg in cat + * cat may be a : separated list of candidate names + */ + +static Message_t* +match(const char* cat, const char* msg) +{ + register char* s; + register char* t; + Catalog_t* cp; + Message_t* mp; + size_t n; + + char buf[1024]; + + s = (char*)cat; + for (;;) + { + if (t = strchr(s, ':')) + { + if (s == (char*)cat) + { + if ((n = strlen(s)) >= sizeof(buf)) + n = sizeof(buf) - 1; + s = (char*)memcpy(buf, s, n); + s[n] = 0; + t = strchr(s, ':'); + } + *t = 0; + } + if (*s && ((cp = (Catalog_t*)dtmatch(state.catalogs, s)) || (cp = init(s))) && cp->messages && (mp = (Message_t*)dtmatch(cp->messages, msg))) + { + mp->cat = cp; + return mp; + } + if (!t) + break; + s = t + 1; + } + return 0; +} + +/* + * translate() is called with four arguments: + * + * loc the LC_MESSAGES locale name + * cmd the calling command name + * cat the catalog name, possibly a : separated list + * "libFOO" FOO library messages + * "libshell" ksh command messages + * "SCRIPT" script SCRIPT application messages + * msg message text to be translated + * + * the translated message text is returned on success + * otherwise the original msg is returned + * + * The first time translate() is called (for a non-C locale) + * it creates the state.catalogs dictionary. A dictionary entry + * (Catalog_t) is made each time translate() is called with a new + * cmd:cat argument. + * + * The X/Open interface catgets() is used to obtain a translated + * message. Its arguments include the message catalog name + * and the set/sequence numbers within the catalog. An additional + * dictionary, with entries of type Message_t, is needed for + * mapping untranslated message strings to the set/sequence numbers + * needed by catgets(). A separate Message_t dictionary is maintained + * for each Catalog_t. + */ + +char* +translate(const char* loc, const char* cmd, const char* cat, const char* msg) +{ + register char* r; + char* t; + int p; + int oerrno; + Catalog_t* cp; + Message_t* mp; + + static uint32_t serial; + static char* nlspath; + + oerrno = errno; + r = (char*)msg; + + /* + * quick out + */ + + if (!cmd && !cat) + goto done; + if (cmd && (t = strrchr(cmd, '/'))) + cmd = (const char*)(t + 1); + + /* + * initialize the catalogs dictionary + */ + + if (!state.catalogs) + { + if (state.error) + goto done; + if (!(state.tmp = sfstropen())) + { + state.error = 1; + goto done; + } + if (!(state.catalogs = dtopen(&state.catalog_disc, Dtset))) + { + sfclose(state.tmp); + state.error = 1; + goto done; + } + } + + /* + * get the message + * or do we have to spell it out for you + */ + + if ((!cmd || !(mp = match(cmd, msg))) && + (!cat || !(mp = match(cat, msg))) && + (!error_info.catalog || !(mp = match(error_info.catalog, msg))) && + (!ast.id || !(mp = match(ast.id, msg))) || + !(cp = mp->cat)) + { +#if DEBUG_trace > 1 +sfprintf(sfstderr, "AHA#%d:%s cmd %s cat %s:%s id %s msg `%s'\n", __LINE__, __FILE__, cmd, cat, error_info.catalog, ast.id, msg); +#endif + cp = 0; + goto done; + } + + /* + * adjust for the current locale + */ + +#if DEBUG_trace +sfprintf(sfstderr, "AHA#%d:%s cp->locale `%s' %p loc `%s' %p\n", __LINE__, __FILE__, cp->locale, cp->locale, loc, loc); +#endif + if (serial != ast.env_serial) + { + serial = ast.env_serial; + nlspath = getenv("NLSPATH"); + } + if (cp->locale != loc || cp->nlspath != nlspath) + { + cp->locale = loc; + cp->nlspath = nlspath; + if (cp->cat != NOCAT) + catclose(cp->cat); + if ((cp->cat = find(cp->locale, cp->name)) == NOCAT) + cp->debug = streq(cp->locale, "debug"); + else + cp->debug = 0; +#if DEBUG_trace +sfprintf(sfstderr, "AHA#%d:%s cp->cat %p cp->debug %d NOCAT %p\n", __LINE__, __FILE__, cp->cat, cp->debug, NOCAT); +#endif + } + if (cp->cat == NOCAT) + { + if (cp->debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)", cp->name, mp->set, mp->seq); + r = tempuse(state.tmp, p); + } + else if (ast.locale.set & AST_LC_debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r); + r = tempuse(state.tmp, p); + } + } + else + { + /* + * get the translated message + */ + + r = catgets(cp->cat, mp->set, mp->seq, msg); + if (r != (char*)msg) + { + if (streq(r, (char*)msg)) + r = (char*)msg; + else if (strcmp(fmtfmt(r), fmtfmt(msg))) + { + sfprintf(sfstderr, "locale %s catalog %s message %d.%d \"%s\" does not match \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, r, msg); + r = (char*)msg; + } + } + if (ast.locale.set & AST_LC_debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r); + r = tempuse(state.tmp, p); + } + } + if (ast.locale.set & AST_LC_translate) + sfprintf(sfstderr, "translate locale=%s catalog=%s set=%d seq=%d \"%s\" => \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, msg, r == (char*)msg ? "NOPE" : r); + done: + if (r == (char*)msg && (!cp && streq(loc, "debug") || cp && cp->debug)) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%s,%s,%s)", loc, cmd, cat, r); + r = tempuse(state.tmp, p); + } + errno = oerrno; + return r; +} |