Logo Search packages:      
Sourcecode: ksh version File versions  Download package

translate.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2010 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              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     */    
      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    */
      const char* debug;            /* debug locale name          */
      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(path, locale, catalog, LC_MESSAGES, 0) || (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;

      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;
            }
            if (streq(loc, "debug"))
                  state.debug = loc;
      }

      /*
       * 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
            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 (cp->locale != loc)
      {
            cp->locale = loc;
            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);
            }
            goto done;
      }

      /*
       * get the translated message
       */

      r = catgets(cp->cat, mp->set, mp->seq, msg);
      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);
      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);
      }
 done:
      if (r == (char*)msg && loc == state.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;
}

Generated by  Doxygen 1.6.0   Back to index