14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin
244887Schin /*
254887Schin * AT&T Research and SCO
2610898Sroland.mainz@nrubsig.org * ast l10n message translation
274887Schin */
284887Schin
294887Schin #include "lclib.h"
304887Schin
314887Schin #include <cdt.h>
324887Schin #include <error.h>
334887Schin #include <mc.h>
344887Schin #include <nl_types.h>
354887Schin
364887Schin #ifndef DEBUG_trace
374887Schin #define DEBUG_trace 0
384887Schin #endif
394887Schin
404887Schin #define NOCAT ((nl_catd)-1)
414887Schin #define GAP 100
424887Schin
434887Schin typedef struct
444887Schin {
454887Schin Dtlink_t link; /* dictionary link */
464887Schin Dt_t* messages; /* message dictionary handle */
474887Schin nl_catd cat; /* message catalog handle */
484887Schin int debug; /* special debug locale */
494887Schin const char* locale; /* message catalog locale */
504887Schin char name[1]; /* catalog name */
514887Schin } Catalog_t;
524887Schin
534887Schin typedef struct
544887Schin {
554887Schin Dtlink_t link; /* dictionary link */
564887Schin Catalog_t* cat; /* current catalog pointer */
574887Schin int set; /* set number */
584887Schin int seq; /* sequence number */
594887Schin char text[1]; /* message text */
604887Schin } Message_t;
614887Schin
624887Schin typedef struct
634887Schin {
644887Schin Sfio_t* sp; /* temp string stream */
654887Schin int off; /* string base offset */
664887Schin } Temp_t;
674887Schin
684887Schin typedef struct
694887Schin {
704887Schin Dtdisc_t message_disc; /* message dict discipline */
714887Schin Dtdisc_t catalog_disc; /* catalog dict discipline */
724887Schin Dt_t* catalogs; /* catalog dictionary handle */
734887Schin Sfio_t* tmp; /* temporary string stream */
744887Schin const char* debug; /* debug locale name */
754887Schin int error; /* no dictionaries! */
764887Schin char null[1]; /* null string */
774887Schin } State_t;
784887Schin
794887Schin static State_t state =
804887Schin {
814887Schin { offsetof(Message_t, text), 0, 0 },
824887Schin { offsetof(Catalog_t, name), 0, 0 },
834887Schin };
844887Schin
854887Schin static int
tempget(Sfio_t * sp)864887Schin tempget(Sfio_t* sp)
874887Schin {
884887Schin if (sfstrtell(sp) > sfstrsize(sp) / 2)
894887Schin sfstrseek(sp, 0, SEEK_SET);
904887Schin return sfstrtell(sp);
914887Schin }
924887Schin
934887Schin static char*
tempuse(Sfio_t * sp,int off)944887Schin tempuse(Sfio_t* sp, int off)
954887Schin {
964887Schin sfputc(sp, 0);
974887Schin return sfstrbase(sp) + off;
984887Schin }
994887Schin
1004887Schin /*
1014887Schin * add msg to dict
1024887Schin */
1034887Schin
1044887Schin static int
entry(Dt_t * dict,int set,int seq,const char * msg)1054887Schin entry(Dt_t* dict, int set, int seq, const char* msg)
1064887Schin {
1074887Schin Message_t* mp;
1084887Schin
1094887Schin if (!(mp = newof(0, Message_t, 1, strlen(msg))))
1104887Schin return 0;
1114887Schin strcpy(mp->text, msg);
1124887Schin mp->set = set;
1134887Schin mp->seq = seq;
1144887Schin if (!dtinsert(dict, mp))
1154887Schin {
1164887Schin free(mp);
1174887Schin return 0;
1184887Schin }
1194887Schin #if DEBUG_trace > 1
1204887Schin sfprintf(sfstderr, "AHA#%d:%s set %d seq %d msg `%s'\n", __LINE__, __FILE__, set, seq, msg);
1214887Schin #endif
1224887Schin return 1;
1234887Schin }
1244887Schin
1254887Schin /*
1264887Schin * find catalog in locale and return catopen() descriptor
1274887Schin */
1284887Schin
1294887Schin static nl_catd
find(const char * locale,const char * catalog)1304887Schin find(const char* locale, const char* catalog)
1314887Schin {
1324887Schin char path[PATH_MAX];
1334887Schin #if DEBUG_trace
1344887Schin const char* ocatalog = catalog;
1354887Schin #endif
1364887Schin
1374887Schin if (mcfind(path, locale, catalog, LC_MESSAGES, 0))
1384887Schin catalog = (const char*)path;
1394887Schin #if DEBUG_trace
1404887Schin sfprintf(sfstderr, "AHA#%d:%s %s %s %s\n", __LINE__, __FILE__, locale, ocatalog, catalog);
1414887Schin #endif
1424887Schin return catopen(catalog, NL_CAT_LOCALE);
1434887Schin }
1444887Schin
1454887Schin /*
1464887Schin * initialize the catalog s by loading in the default locale messages
1474887Schin */
1484887Schin
1494887Schin static Catalog_t*
init(register char * s)1504887Schin init(register char* s)
1514887Schin {
1524887Schin register Catalog_t* cp;
1534887Schin register char* u;
1544887Schin register int n;
1554887Schin register int m;
1564887Schin nl_catd d;
1574887Schin
1584887Schin /*
1594887Schin * insert into the catalog dictionary
1604887Schin */
1614887Schin
1624887Schin if (!(cp = newof(0, Catalog_t, 1, strlen(s))))
1634887Schin return 0;
1644887Schin strcpy(cp->name, s);
1654887Schin if (!dtinsert(state.catalogs, cp))
1664887Schin {
1674887Schin free(cp);
1684887Schin return 0;
1694887Schin }
1704887Schin cp->cat = NOCAT;
1714887Schin
1724887Schin /*
1734887Schin * locate the default locale catalog
1744887Schin */
1754887Schin
176*12068SRoger.Faulkner@Oracle.COM ast.locale.set |= AST_LC_internal;
1774887Schin u = setlocale(LC_MESSAGES, NiL);
1784887Schin setlocale(LC_MESSAGES, "C");
1794887Schin if ((d = find("C", s)) != NOCAT)
1804887Schin {
1814887Schin /*
1824887Schin * load the default locale messages
1834887Schin * this assumes one mesage set for ast (AST_MESSAGE_SET)
1844887Schin * different packages can share the same message catalog
1854887Schin * name by using different message set numbers
1864887Schin * see <mc.h> mcindex()
1874887Schin *
1884887Schin * this method requires a scan of each catalog, and the
1894887Schin * catalogs do not advertize the max message number, so
1904887Schin * we assume there are no messages after a gap of GAP
1914887Schin * missing messages
1924887Schin */
1934887Schin
1944887Schin if (cp->messages = dtopen(&state.message_disc, Dtset))
1954887Schin {
1964887Schin n = m = 0;
1974887Schin for (;;)
1984887Schin {
1994887Schin n++;
2004887Schin if ((s = catgets(d, AST_MESSAGE_SET, n, state.null)) != state.null && entry(cp->messages, AST_MESSAGE_SET, n, s))
2014887Schin m = n;
2024887Schin else if ((n - m) > GAP)
2034887Schin break;
2044887Schin }
2054887Schin if (!m)
2064887Schin {
2074887Schin dtclose(cp->messages);
2084887Schin cp->messages = 0;
2094887Schin }
2104887Schin }
2114887Schin catclose(d);
2124887Schin }
2134887Schin setlocale(LC_MESSAGES, u);
214*12068SRoger.Faulkner@Oracle.COM ast.locale.set &= ~AST_LC_internal;
2154887Schin return cp;
2164887Schin }
2174887Schin
2184887Schin /*
2194887Schin * return the C locale message pointer for msg in cat
2204887Schin * cat may be a : separated list of candidate names
2214887Schin */
2224887Schin
2234887Schin static Message_t*
match(const char * cat,const char * msg)2244887Schin match(const char* cat, const char* msg)
2254887Schin {
2264887Schin register char* s;
2274887Schin register char* t;
2284887Schin Catalog_t* cp;
2294887Schin Message_t* mp;
2304887Schin size_t n;
2314887Schin
2324887Schin char buf[1024];
2334887Schin
2344887Schin s = (char*)cat;
2354887Schin for (;;)
2364887Schin {
2374887Schin if (t = strchr(s, ':'))
2384887Schin {
2394887Schin if (s == (char*)cat)
2404887Schin {
2414887Schin if ((n = strlen(s)) >= sizeof(buf))
2424887Schin n = sizeof(buf) - 1;
2434887Schin s = (char*)memcpy(buf, s, n);
2444887Schin s[n] = 0;
2454887Schin t = strchr(s, ':');
2464887Schin }
2474887Schin *t = 0;
2484887Schin }
2494887Schin if (*s && ((cp = (Catalog_t*)dtmatch(state.catalogs, s)) || (cp = init(s))) && cp->messages && (mp = (Message_t*)dtmatch(cp->messages, msg)))
2504887Schin {
2514887Schin mp->cat = cp;
2524887Schin return mp;
2534887Schin }
2544887Schin if (!t)
2554887Schin break;
2564887Schin s = t + 1;
2574887Schin }
2584887Schin return 0;
2594887Schin }
2604887Schin
2614887Schin /*
2624887Schin * translate() is called with four arguments:
2634887Schin *
2644887Schin * loc the LC_MESSAGES locale name
2654887Schin * cmd the calling command name
2664887Schin * cat the catalog name, possibly a : separated list
2674887Schin * "libFOO" FOO library messages
2684887Schin * "libshell" ksh command messages
2694887Schin * "SCRIPT" script SCRIPT application messages
2704887Schin * msg message text to be translated
2714887Schin *
2724887Schin * the translated message text is returned on success
2734887Schin * otherwise the original msg is returned
2744887Schin *
2754887Schin * The first time translate() is called (for a non-C locale)
2764887Schin * it creates the state.catalogs dictionary. A dictionary entry
2774887Schin * (Catalog_t) is made each time translate() is called with a new
2784887Schin * cmd:cat argument.
2794887Schin *
2804887Schin * The X/Open interface catgets() is used to obtain a translated
2814887Schin * message. Its arguments include the message catalog name
2824887Schin * and the set/sequence numbers within the catalog. An additional
2834887Schin * dictionary, with entries of type Message_t, is needed for
2844887Schin * mapping untranslated message strings to the set/sequence numbers
2854887Schin * needed by catgets(). A separate Message_t dictionary is maintained
2864887Schin * for each Catalog_t.
2874887Schin */
2884887Schin
2894887Schin char*
translate(const char * loc,const char * cmd,const char * cat,const char * msg)2904887Schin translate(const char* loc, const char* cmd, const char* cat, const char* msg)
2914887Schin {
2924887Schin register char* r;
2934887Schin char* t;
2944887Schin int p;
2954887Schin int oerrno;
2964887Schin Catalog_t* cp;
2974887Schin Message_t* mp;
2984887Schin
2994887Schin oerrno = errno;
3004887Schin r = (char*)msg;
3014887Schin
3024887Schin /*
3034887Schin * quick out
3044887Schin */
3054887Schin
3064887Schin if (!cmd && !cat)
3074887Schin goto done;
3084887Schin if (cmd && (t = strrchr(cmd, '/')))
3094887Schin cmd = (const char*)(t + 1);
3104887Schin
3114887Schin /*
3124887Schin * initialize the catalogs dictionary
3134887Schin */
3144887Schin
3154887Schin if (!state.catalogs)
3164887Schin {
3174887Schin if (state.error)
3184887Schin goto done;
3194887Schin if (!(state.tmp = sfstropen()))
3204887Schin {
3214887Schin state.error = 1;
3224887Schin goto done;
3234887Schin }
3244887Schin if (!(state.catalogs = dtopen(&state.catalog_disc, Dtset)))
3254887Schin {
3264887Schin sfclose(state.tmp);
3274887Schin state.error = 1;
3284887Schin goto done;
3294887Schin }
3304887Schin if (streq(loc, "debug"))
3314887Schin state.debug = loc;
3324887Schin }
3334887Schin
3344887Schin /*
3354887Schin * get the message
3364887Schin * or do we have to spell it out for you
3374887Schin */
3384887Schin
3394887Schin if ((!cmd || !(mp = match(cmd, msg))) &&
3404887Schin (!cat || !(mp = match(cat, msg))) &&
3414887Schin (!error_info.catalog || !(mp = match(error_info.catalog, msg))) &&
3424887Schin (!ast.id || !(mp = match(ast.id, msg))) ||
3434887Schin !(cp = mp->cat))
3444887Schin {
3454887Schin #if DEBUG_trace > 1
3464887Schin 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);
3474887Schin #endif
3484887Schin goto done;
3494887Schin }
3504887Schin
3514887Schin /*
3524887Schin * adjust for the current locale
3534887Schin */
3544887Schin
3554887Schin #if DEBUG_trace
3564887Schin sfprintf(sfstderr, "AHA#%d:%s cp->locale `%s' %p loc `%s' %p\n", __LINE__, __FILE__, cp->locale, cp->locale, loc, loc);
3574887Schin #endif
3584887Schin if (cp->locale != loc)
3594887Schin {
3604887Schin cp->locale = loc;
3614887Schin if (cp->cat != NOCAT)
3624887Schin catclose(cp->cat);
3634887Schin if ((cp->cat = find(cp->locale, cp->name)) == NOCAT)
3644887Schin cp->debug = streq(cp->locale, "debug");
3654887Schin else
3664887Schin cp->debug = 0;
3674887Schin #if DEBUG_trace
3684887Schin sfprintf(sfstderr, "AHA#%d:%s cp->cat %p cp->debug %d NOCAT %p\n", __LINE__, __FILE__, cp->cat, cp->debug, NOCAT);
3694887Schin #endif
3704887Schin }
3714887Schin if (cp->cat == NOCAT)
3724887Schin {
3734887Schin if (cp->debug)
3744887Schin {
3754887Schin p = tempget(state.tmp);
3764887Schin sfprintf(state.tmp, "(%s,%d,%d)", cp->name, mp->set, mp->seq);
3774887Schin r = tempuse(state.tmp, p);
3784887Schin }
3794887Schin else if (ast.locale.set & AST_LC_debug)
3804887Schin {
3814887Schin p = tempget(state.tmp);
3824887Schin sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r);
3834887Schin r = tempuse(state.tmp, p);
3844887Schin }
3854887Schin goto done;
3864887Schin }
3874887Schin
3884887Schin /*
3894887Schin * get the translated message
3904887Schin */
3914887Schin
3924887Schin r = catgets(cp->cat, mp->set, mp->seq, msg);
3934887Schin if (ast.locale.set & AST_LC_translate)
3944887Schin 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);
3954887Schin if (r != (char*)msg)
3964887Schin {
3974887Schin if (streq(r, (char*)msg))
3984887Schin r = (char*)msg;
3994887Schin else if (strcmp(fmtfmt(r), fmtfmt(msg)))
4004887Schin {
4014887Schin 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);
4024887Schin r = (char*)msg;
4034887Schin }
4044887Schin }
4054887Schin if (ast.locale.set & AST_LC_debug)
4064887Schin {
4074887Schin p = tempget(state.tmp);
4084887Schin sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r);
4094887Schin r = tempuse(state.tmp, p);
4104887Schin }
4114887Schin done:
4124887Schin if (r == (char*)msg && loc == state.debug)
4134887Schin {
4144887Schin p = tempget(state.tmp);
4154887Schin sfprintf(state.tmp, "(%s,%s,%s,\"%s\")", loc, cmd, cat, r);
4164887Schin r = tempuse(state.tmp, p);
4174887Schin }
4184887Schin errno = oerrno;
4194887Schin return r;
4204887Schin }
421