Logo Search packages:      
Sourcecode: ksh version File versions

msggen.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 2000-2007 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                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>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 */

static const char usage[] =
"[-?\n@(#)$Id: msggen (AT&T Research) 2002-03-11 $\n]"
USAGE_LICENSE
"[+NAME?msggen - generate a machine independent formatted message catalog]"
"[+DESCRIPTION?\bmsggen\b merges the message text source files \amsgfile\a"
"     into a machine independent formatted message catalog \acatfile\a."
"     The file \acatfile\a will be created if it does not already exist."
"     If \acatfile\a does exist, its messages will be included in the new"
"     \acatfile\a. If set and message numbers collide, the new message"
"     text defined in \amsgfile\a will replace the old message text"
"     currently contained in \acatfile\a. Non-ASCII characters must be"
"     UTF-8 encoded. \biconv\b(1) can be used to convert to/from UTF-8.]"
"[f:format?List the \bprintf\b(3) format signature for each message in"
"     \acatfile\a. A format signature is one line containing one character"
"     per format specification:]{"
"           [c?char]"
"           [d?double]"
"           [D?long double]"
"           [f?float]"
"           [h?short]"
"           [i?int]"
"           [j?long long]"
"           [l?long]"
"           [p?void*]"
"           [s?string]"
"           [t?ptrdiff_t]"
"           [z?size_t]"
"           [???unknown]"
"}"
"[l:list?List \acatfile\a in UTF-8 \amsgfile\a form.]"
"[s:set?Convert the \acatfile\a operand to a message set number and"
"     print the number on the standard output.]"
"[+EXTENDED DESCRIPTION?Message text source files are in \bgencat\b(1)"
"     format, defined as follows. Note that the fields of a message text"
"     source line are separated by a single blank character. Any other"
"     blank characters are considered as being part of the subsequent"
"     field. The \bNL_*\b constants are defined in one or both of"
"     \b<limits.h>\b and \b<nl_types.h>\b.]{"
"           [+$ \acomment\a?A line beginning with \b$\b followed by a"
"                 blank character is treated as a comment.]"
"           [+$delset \an\a \acomment\a?This line deletes message set"
"                 \an\a from an existing message catalog. \an\a"
"                 denotes the set number [1, \bNL_SETMAX\b]]. Any"
"                 text following the set number is treated as a"
"                 comment.]"
"           [+$quote \ac\a?This line specifies an optional quote"
"                 character \ac\a, which can be used to surround"
"                 \amessage-text\a so that trailing spaces or"
"                 empty messages are visible in a message source"
"                 line. By default, or if an empty \b$quote\b"
"                 directive is supplied, no quoting of \amessage-text\a"
"                 will be recognized.]"
"           [+$set \an\a \acomment\a?This line specifies the set"
"                 identifier of the following messages until the next"
"                 \b$set\b or end-of-file appears. \an\a denotes the set"
"                 identifier, which is defined as a number in the range"
"                 [1, \bNL_SETMAX\b]]. Set numbers need not be"
"                 contiguous. Any text following the set identifier is"
"                 treated as a comment. If no \b$set\b directive is"
"                 specified in a    message text source file, all messages"
"                 will be located in message set \b1\b.]"
"           [+$translation \aidentification\a \aYYYY-MM-DD\a[,...]]?Append"
"                 translation info to the message catalog header. Only"
"                 the newest date for a given \aidentification\a"
"                 is retained in the catalog. Multiple translation lines"
"                 are combined into a single \b,\b separated list.]"
"           [+\am\a \amessage-text\a?\am\a denotes the message identifier,"
"                 which is defined as a number in the range"
"                 [1, \bNL_MSGMAX\b]]. The message-text is stored in the"
"                 message catalogue with the set identifier specified by"
"                 the last \b$set\b directive, and with message"
"                 identifier \am\a. If the \amessage-text\a is empty,"
"                 and a blank character field separator is present, an"
"                 empty string is stored in the message catalogue. If a"
"                 message source line has a message number, but neither"
"                 a field separator nor \amessage-text\a, the existing"
"                 message with that number (if any) is deleted from the"
"                 catalogue. Message identifiers need not be contiguous."
"                 There are no \amessage-text\a length restrictions.]"
"}"

"\n"
"\ncatfile [ msgfile ]\n"
"\n"

"[+SEE ALSO?\bgencat\b(1), \biconv\b(1), \bmsgcc\b(1), \btranslate\b(1),"
"     \bfmtfmt\b(3)]"
;

#include <ast.h>
#include <ctype.h>
#include <ccode.h>
#include <error.h>
#include <mc.h>

typedef struct Xl_s
{
      struct Xl_s*      next;
      char*       date;
      char        name[1];
} Xl_t;

/*
 * append s to the translation list
 */

static Xl_t*
translation(Xl_t* xp, register char* s)
{
      register Xl_t*    px;
      register char*    t;
      char*       d;
      char*       e;

      do
      {
            for (; isspace(*s); s++);
            for (d = e = 0, t = s; *t; t++)
                  if (*t == ',')
                  {
                        e = t;
                        *e++ = 0;
                        break;
                  }
                  else if (isspace(*t))
                        d = t;
            if (d)
            {
                  *d++ = 0;
                  for (px = xp; px; px = px->next)
                        if (streq(px->name, s))
                        {
                              if (strcoll(px->date, d) < 0)
                              {
                                    free(px->date);
                                    if (!(px->date = strdup(d)))
                                          error(ERROR_SYSTEM|3, "out of space [translation]");
                              }
                              break;
                        }
                  if (!px)
                  {
                        if (!(px = newof(0, Xl_t, 1, strlen(s))) || !(px->date = strdup(d)))
                              error(ERROR_SYSTEM|3, "out of space [translation]");
                        strcpy(px->name, s);
                        px->next = xp;
                        xp = px;
                  }
            }
      } while (s = e);
      return xp;
}

/*
 * sfprintf() with ccmaps(from,to)
 */

static int
ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...)
{
      va_list           ap;
      Sfio_t*           tp;
      char*       s;
      int         n;

      va_start(ap, format);
      if (from == to)
            n = sfvprintf(sp, format, ap);
      else if (tp = sfstropen())
      {
            n = sfvprintf(tp, format, ap);
            s = sfstrbase(tp);
            ccmaps(s, n, from, to);
            n = sfwrite(sp, s, n);
            sfstrclose(tp);
      }
      else
            n = -1;
      return n;
}

int
main(int argc, char** argv)
{
      register Mc_t*    mc;
      register char*    s;
      register char*    t;
      register int      c;
      register int      q;
      register int      i;
      int         num;
      char*       b;
      char*       e;
      char*       catfile;
      char*       msgfile;
      Sfio_t*           sp;
      Sfio_t*           mp;
      Sfio_t*           tp;
      Xl_t*       px;
      Xl_t*       bp;

      Xl_t*       xp = 0;
      int         format = 0;
      int         list = 0;
      int         set = 0;

      NoP(argc);
      error_info.id = "msggen";
      for (;;)
      {
            switch (optget(argv, usage))
            {
            case 'f':
                  format = list = 1;
                  continue;
            case 'l':
                  list = 1;
                  continue;
            case 's':
                  set = 1;
                  continue;
            case '?':
                  error(ERROR_USAGE|4, "%s", opt_info.arg);
                  continue;
            case ':':
                  error(2, "%s", opt_info.arg);
                  continue;
            }
            break;
      }
      argv += opt_info.index;
      if (error_info.errors || !(catfile = *argv++))
            error(ERROR_USAGE|4, "%s", optusage(NiL));

      /*
       * set and list only need catfile
       */

      if (set)
      {
            sfprintf(sfstdout, "%d\n", mcindex(catfile, NiL, NiL, NiL));
            return error_info.errors != 0;
      }
      else if (list)
      {
            if (!(sp = sfopen(NiL, catfile, "r")))
                  error(ERROR_SYSTEM|3, "%s: cannot read catalog", catfile);
            if (!(mc = mcopen(sp)))
                  error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
            sfclose(sp);
            if (format)
            {
                  for (set = 1; set <= mc->num; set++)
                        if (mc->set[set].num)
                        {
                              sfprintf(sfstdout, "$set %d\n", set);
                              for (num = 1; num <= mc->set[set].num; num++)
                                    if (s = mc->set[set].msg[num])
                                          sfprintf(sfstdout, "%d \"%s\"\n", num, fmtfmt(s));
                        }
            }
            else
            {
                  if (*mc->translation)
                  {
                        ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$translation ");
                        sfprintf(sfstdout, "%s", mc->translation);
                        ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\n");
                  }
                  ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$quote \"\n");
                  for (set = 1; set <= mc->num; set++)
                        if (mc->set[set].num)
                        {
                              ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$set %d\n", set);
                              for (num = 1; num <= mc->set[set].num; num++)
                                    if (s = mc->set[set].msg[num])
                                    {
                                          ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "%d \"", num);
                                          while (c = *s++)
                                          {
                                                /*INDENT...*/

                  switch (c)
                  {
                  case 0x22: /* " */
                  case 0x5C: /* \ */
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x07: /* \a */
                        c = 0x61;
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x08: /* \b */
                        c = 0x62;
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x0A: /* \n */
                        c = 0x6E;
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x0B: /* \v */
                        c = 0x76;
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x0C: /* \f */
                        c = 0x66;
                        sfputc(sfstdout, 0x5C);
                        break;
                  case 0x0D: /* \r */
                        c = 0x72;
                        sfputc(sfstdout, 0x5C);
                        break;
                  }

                                                /*...UNDENT*/
                                                sfputc(sfstdout, c);
                                          }
                                          ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\"\n");
                                    }
                        }
            }
            mcclose(mc);
            return error_info.errors != 0;
      }
      else if (!(msgfile = *argv++) || *argv)
            error(3, "exactly one message file must be specified");

      /*
       * open the files and handles
       */

      if (!(tp = sfstropen()))
            error(ERROR_SYSTEM|3, "out of space [string stream]");
      if (!(mp = sfopen(NiL, msgfile, "r")))
            error(ERROR_SYSTEM|3, "%s: cannot read message file", msgfile);
      sp = sfopen(NiL, catfile, "r");
      if (!(mc = mcopen(sp)))
            error(ERROR_SYSTEM|3, "%s: catalog content error", catfile);
      if (sp)
            sfclose(sp);
      xp = translation(xp, mc->translation);

      /*
       * read the message file
       */

      q = 0;
      set = 1;
      error_info.file = msgfile;
      while (s = sfgetr(mp, '\n', 1))
      {
            error_info.line++;
            if (!*s)
                  continue;
            if (*s == '$')
            {
                  if (!*++s || isspace(*s))
                        continue;
                  for (t = s; *s && !isspace(*s); s++);
                  if (*s)
                        *s++ = 0;
                  if (streq(t, "delset"))
                  {
                        while (isspace(*s))
                              s++;
                        num = (int)strtol(s, NiL, 0);
                        if (num < mc->num && mc->set[num].num)
                              for (i = 1; i <= mc->set[num].num; i++)
                                    mcput(mc, num, i, NiL);
                  }
                  else if (streq(t, "quote"))
                        q = *s ? *s : 0;
                  else if (streq(t, "set"))
                  {
                        while (isspace(*s))
                              s++;
                        num = (int)strtol(s, &e, 0);
                        if (e != s)
                              set = num;
                        else
                              error(2, "set number expected");
                  }
                  else if (streq(t, "translation"))
                        xp = translation(xp, s);
            }
            else
            {
                  t = s + sfvalue(mp);
                  num = (int)strtol(s, &e, 0);
                  if (e != s)
                  {
                        s = e;
                        if (!*s)
                        {
                              if (mcput(mc, set, num, NiL))
                                    error(2, "(%d,%d): cannot delete message", set, num);
                        }
                        else if (isspace(*s++))
                        {
                              if (t > (s + 1) && *(t -= 2) == '\\')
                              {
                                    sfwrite(tp, s, t - s);
                                    while (s = sfgetr(mp, '\n', 0))
                                    {
                                          error_info.line++;
                                          t = s + sfvalue(mp);
                                          if (t <= (s + 1) || *(t -= 2) != '\\')
                                                break;
                                          sfwrite(tp, s, t - s);
                                    }
                                    if (!(s = sfstruse(tp)))
                                          error(ERROR_SYSTEM|3, "out of space");
                              }
                              if (q)
                              {
                                    if (*s++ != q)
                                    {
                                          error(2, "(%d,%d): %c quote expected", set, num, q);
                                          continue;
                                    }
                                    b = t = s;
                                    while (c = *s++)
                                    {
                                          if (c == '\\')
                                          {
                                                c = chresc(s - 1, &e);
                                                s = e;
                                                if (c)
                                                      *t++ = c;
                                                else
                                                      error(1, "nul character ignored");
                                          }
                                          else if (c == q)
                                                break;
                                          else
                                                *t++ = c;
                                    }
                                    if (*s)
                                    {
                                          error(2, "(%d,%d): characters after quote not expected", set, num);
                                          continue;
                                    }
                                    *t = 0;
                                    s = b;
                              }
                              if (mcput(mc, set, num, s))
                                    error(2, "(%d,%d): cannot add message", set, num);
                        }
                        else
                              error(2, "message text expected");
                  }
                  else
                        error(2, "message number expected");
            }
      }
      error_info.file = 0;
      error_info.line = 0;

      /*
       * fix up the translation record
       */

      if (xp)
      {
            t = "";
            for (;;)
            {
                  for (bp = 0, px = xp; px; px = px->next)
                        if (px->date && (!bp || strcoll(bp->date, px->date) < 0))
                              bp = px;
                  if (!bp)
                        break;
                  sfprintf(tp, "%s%s %s", t, bp->name, bp->date);
                  t = ", ";
                  bp->date = 0;
            }
            if (!(mc->translation = sfstruse(tp)))
                  error(ERROR_SYSTEM|3, "out of space");
      }

      /*
       * dump the catalog to a local temporary
       * rename if no errors
       */

      if (!(s = pathtemp(NiL, 0, "", error_info.id, NiL)) || !(sp = sfopen(NiL, s, "w")))
            error(ERROR_SYSTEM|3, "%s: cannot write catalog file", catfile);
      if (mcdump(mc, sp) || mcclose(mc) || sfclose(sp))
      {
            remove(s);
            error(ERROR_SYSTEM|3, "%s: temporary catalog file write error", s);
      }
      remove(catfile);
      if (rename(s, catfile))
            error(ERROR_SYSTEM|3, "%s: cannot rename from temporary catalog file %s", catfile, s);
      return error_info.errors != 0;
}

Generated by  Doxygen 1.6.0   Back to index