home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 10 / Fresh_Fish_10_2352.bin / new / dev / lang / sgmls / src / msgcat.c < prev    next >
C/C++ Source or Header  |  1994-07-10  |  16KB  |  841 lines

  1. /* msgcat.c -
  2.    X/Open message catalogue functions and gencat utility.
  3.  
  4.      Written by James Clark (jjc@jclark.com).
  5. */
  6.  
  7. #include "config.h"
  8.  
  9. #ifndef HAVE_CAT
  10.  
  11. /* In this implementation the message catalogue format is the same as the
  12. message text source file format (see pp 42-43 of the X/Open
  13. Portability Guide, Issue 3, Volume 3.)  This means that you don't have
  14. to use the gencat utility, but it is still useful for checking and
  15. merging catalogues. */
  16.  
  17. /* Compile this with -DGENCAT to get the gencat utility. */
  18.  
  19. #include "std.h"
  20. #include "msgcat.h"
  21.  
  22. #ifdef USE_PROTOTYPES
  23. #define P(parms) parms
  24. #else
  25. #define P(parms) ()
  26. #endif
  27.  
  28. #ifdef USE_ISASCII
  29. #define ISASCII(c) isascii(c)
  30. #else
  31. #define ISASCII(c) (1)
  32. #endif
  33.  
  34. /* Default message set. */
  35. #define NL_SETD 1
  36.  
  37. #ifndef PATH_FILE_SEP
  38. #define PATH_FILE_SEP ':'
  39. #endif
  40.  
  41. #ifndef DEFAULT_NLSPATH
  42. #define DEFAULT_NLSPATH ""
  43. #endif
  44.  
  45. #ifndef DEFAULT_LANG
  46. #define DEFAULT_LANG "default"
  47. #endif
  48.  
  49. #define HASH_TAB_SIZE 251
  50.  
  51. struct message {
  52.      struct message *next;
  53.      unsigned msgnum;
  54.      unsigned setnum;
  55.      char *text;
  56. };
  57.  
  58. struct cat {
  59.      char *name;
  60.      int loaded;
  61.      int bad;
  62.      struct message *table[HASH_TAB_SIZE];
  63. };
  64.  
  65. static char *read_buf = 0;
  66. static unsigned read_buf_len = 0;
  67.  
  68. /* Errors that can be generated by read_catalog. */
  69.  
  70. enum cat_err {
  71.      E_ZERO,            /* not an error */
  72.      E_BADARG,
  73.      E_NOMEM,
  74.      E_NOSUCHCOMMAND,
  75.      E_INPUT,
  76.      E_EOF,
  77.      E_BADSEP,
  78.      E_BADLINE
  79. };
  80.  
  81. #ifdef GENCAT
  82. /* These must match enum cat_err. */
  83. static char *cat_errlist[] = {
  84.      "Error 0",
  85.      "Invalid argument to command",
  86.      "Out of memory",
  87.      "Unrecognized command",
  88.      "Input error",
  89.      "Unexpected end of file",
  90.      "Space or tab expected after message number",
  91.      "Invalid line",
  92. };
  93. #endif /* GENCAT */
  94.  
  95. #ifndef GENCAT
  96. /* The value of NLSPATH. */
  97. static char *nlspath = 0;
  98. /* The value of LANG. */
  99. static char *lang = 0;
  100. #endif /* not GENCAT */
  101.  
  102. static int current_lineno = -1;
  103. static enum cat_err cat_errno = E_ZERO;
  104.  
  105. #ifndef GENCAT
  106. static void load_catalog P((struct cat *));
  107. static FILE *find_catalog P((char *, char **));
  108. #endif
  109. static int read_catalog P((FILE *, struct message **));
  110. static void delete_set P((struct message **, unsigned));
  111. static void delete_message P((struct message **, unsigned, unsigned));
  112. static int hash P((unsigned setnum, unsigned msgnum));
  113. static char *parse_text P((FILE *, int));
  114.  
  115. #ifndef GENCAT
  116.  
  117. nl_catd catopen(char *name, int oflag)
  118.  
  119.  
  120. {
  121.      struct cat *catp;
  122.      int i;
  123.  
  124.      if (!name)
  125.       return 0;
  126.  
  127.      catp = (struct cat *)malloc(sizeof *catp);
  128.      if (!catp)
  129.       return 0;
  130.      for (i = 0; i < HASH_TAB_SIZE; i++)
  131.       catp->table[i] = 0;
  132.      catp->name = malloc(strlen(name) + 1);
  133.      catp->loaded = 0;
  134.      catp->bad = 0;
  135.      strcpy(catp->name, name);
  136.      return (nl_catd)catp;
  137. }
  138.  
  139. int catclose(nl_catd catd)
  140.  
  141. {
  142.      int i;
  143.      struct cat *catp = (struct cat *)catd;
  144.  
  145.      if (!catp)
  146.       return 0;
  147.  
  148.      for (i = 0; i < HASH_TAB_SIZE; i++) {
  149.       struct message *p, *nextp;
  150.       for (p = catp->table[i]; p; p = nextp) {
  151.            nextp = p->next;
  152.            free(p->text);
  153.            free((char *)p);
  154.       }
  155.      }
  156.      if (catp->name)
  157.       free(catp->name);
  158.      free((char *)catp);
  159.      return 0;
  160. }
  161.  
  162. char *catgets(nl_catd catd, int setnum, int msgnum, char *dflt)
  163.  
  164.  
  165.  
  166. {
  167.      struct message *p;
  168.      struct cat *catp;
  169.  
  170.      /* setnum and msgnum are required to be >= 1. */
  171.      if (!catd || setnum <= 0 || msgnum <= 0)
  172.       return dflt;
  173.      catp = (struct cat *)catd;
  174.      if (!catp->loaded)
  175.       load_catalog(catp);
  176.      if (catp->bad)
  177.       return dflt;
  178.      for (p = catp->table[hash(setnum, msgnum)]; p; p = p->next)
  179.       if (p->msgnum == msgnum && p->setnum == setnum)
  180.            break;
  181.      if (!p)
  182.       return dflt;
  183.      return p->text;
  184. }
  185.  
  186. static
  187. VOID load_catalog(catp)
  188. struct cat *catp;
  189. {
  190.      FILE *fp;
  191.      char *path;
  192.  
  193.      catp->loaded = 1;
  194.      fp = find_catalog(catp->name, &path);
  195.      if (!fp) {
  196.       catp->bad = 1;
  197.       return;
  198.      }
  199.      current_lineno = 0;
  200.      if (read_catalog(fp, catp->table) < 0)
  201.       catp->bad = 1;
  202.      fclose(fp);
  203.      if (read_buf) {
  204.       free(read_buf);
  205.       read_buf = 0;
  206.      }
  207.      read_buf_len = 0;
  208.      free(path);
  209. }
  210.  
  211. static
  212. FILE *find_catalog(name, pathp)
  213. char *name;
  214. char **pathp;
  215. {
  216.      char *path;
  217.  
  218.      if (!name)
  219.       return 0;
  220.      if (!nlspath) {
  221.       nlspath = getenv("NLSPATH");
  222.       if (!nlspath)
  223.            nlspath = DEFAULT_NLSPATH;
  224.      }
  225.      if (!lang) {
  226.       lang = getenv("LANG");
  227.       if (!lang)
  228.            lang = DEFAULT_LANG;
  229.      }
  230.      path = nlspath;
  231.      for (;;) {
  232.       char *p;
  233.       unsigned len = 0;
  234.  
  235.       for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
  236.            if (*p == '%') {
  237.             if (p[1] == 'N') {
  238.              p++;
  239.              len += strlen(name);
  240.             }
  241.             else if (p[1] == 'L') {
  242.              p++;
  243.              len += strlen(lang);
  244.             }
  245.             else if (p[1] == '%') {
  246.              p++;
  247.              len++;
  248.             }
  249.             else
  250.              len++;
  251.  
  252.            }
  253.            else
  254.             len++;
  255.       }
  256.       if (len > 0) {
  257.            char *s, *try;
  258.            FILE *fp;
  259.            s = try = malloc(len + 1);
  260.            if (!s)
  261.             return 0;
  262.            for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
  263.             if (*p == '%') {
  264.              if (p[1] == 'N') {
  265.                   p++;
  266.                   strcpy(s, name);
  267.                   s += strlen(name);
  268.              }
  269.              else if (p[1] == 'L') {
  270.                   p++;
  271.                   strcpy(s, lang);
  272.                   s += strlen(lang);
  273.              }
  274.              else if (p[1] == '%') {
  275.                   p++;
  276.                   *s++ = '%';
  277.              }
  278.              else
  279.                   *s++ = *p;
  280.             }
  281.             else
  282.              *s++ = *p;
  283.            }
  284.            *s++ = '\0';
  285.            fp = fopen(try, "r");
  286.            if (fp) {
  287.             *pathp = try;
  288.             return fp;
  289.            }
  290.            free(try);
  291.       }
  292.       if (*p == '\0')
  293.            break;
  294.       path = ++p;
  295.      }
  296.      return 0;
  297. }
  298.  
  299. #endif /* not GENCAT */
  300.  
  301. /* 0 success, -1 error */
  302.  
  303. static
  304. int parse_message(int c, FILE *fp, struct message **table, unsigned setnum, int quote)
  305.  
  306.  
  307.  
  308.  
  309.  
  310. {
  311.      unsigned msgnum;
  312.      struct message *msgp;
  313.      char *text;
  314.      int hc;
  315.  
  316.      msgnum = c - '0';
  317.      for (;;) {
  318.       c = getc(fp);
  319.       if (!isdigit(c))
  320.            break;
  321.       msgnum = msgnum*10 + (c - '0');
  322.      }
  323.      if (c == '\n') {
  324.       delete_message(table, setnum, msgnum);
  325.       return 0;
  326.      }
  327.      if (c != ' ' && c != '\t') {
  328.       cat_errno = E_BADSEP;
  329.       return -1;
  330.      }
  331.      text = parse_text(fp, quote);
  332.      if (!text)
  333.       return -1;
  334.      hc = hash(setnum, msgnum);
  335.      for (msgp = table[hc]; msgp; msgp = msgp->next)
  336.       if (msgp->setnum == setnum && msgp->msgnum == msgnum)
  337.            break;
  338.      if (msgp)
  339.       free(msgp->text);
  340.      else {
  341.       msgp = (struct message *)malloc(sizeof *msgp);
  342.       if (!msgp) {
  343.            cat_errno = E_NOMEM;
  344.            return -1;
  345.       }
  346.       msgp->next = table[hc];
  347.       table[hc] = msgp;
  348.       msgp->msgnum = msgnum;
  349.       msgp->setnum = setnum;
  350.      }
  351.      msgp->text = text;
  352.      return 0;
  353. }
  354.  
  355. static
  356. char *parse_text(fp, quote)
  357. FILE *fp;
  358. int quote;
  359. {
  360.      unsigned i = 0;
  361.      char *p;
  362.      int c;
  363.      int quoted;
  364.  
  365.      c = getc(fp);
  366.      if (c == quote) {
  367.       quoted = 1;
  368.       c = getc(fp);
  369.      }
  370.      else
  371.       quoted = 0;
  372.      for (;; c = getc(fp)) {
  373.       if (c == EOF) {
  374.            if (ferror(fp)) {
  375.             cat_errno = E_INPUT;
  376.             return 0;
  377.            }
  378.            break;
  379.       }
  380.       if (c == '\n')
  381.            break;
  382.       /* XXX
  383.  
  384.          Can quotes be used in quoted message text if protected by \ ?
  385.  
  386.          Is it illegal to omit the closing quote if there's an opening
  387.          quote?
  388.  
  389.          Is it illegal to have anything after a closing quote?
  390.  
  391.       */
  392.  
  393.       if (quoted && c == quote) {
  394.            /* Skip the rest of the line. */
  395.            while ((c = getc(fp)) != '\n')
  396.             if (c == EOF) {
  397.              if (ferror(fp)) {
  398.                   cat_errno = E_INPUT;
  399.                   return 0;
  400.              }
  401.              break;
  402.             }
  403.            break;
  404.       }
  405.       if (c == '\\') {
  406.            int d;
  407.  
  408.            c = getc(fp);
  409.            if (c == EOF)
  410.             break;
  411.            switch (c) {
  412.            case '\n':
  413.             current_lineno++;
  414.             continue;
  415.            case 'n':
  416.             c = '\n';
  417.             break;
  418.            case 'b':
  419.             c = '\b';
  420.             break;
  421.            case 'f':
  422.             c = '\f';
  423.             break;
  424.            case 't':
  425.             c = '\t';
  426.             break;
  427.            case 'v':
  428.             c = '\v';
  429.             break;
  430.            case 'r':
  431.             c = '\r';
  432.             break;
  433.            case '\\':
  434.             c = '\\';
  435.             break;
  436.            case '0':
  437.            case '1':
  438.            case '2':
  439.