about summary refs log tree commit diff
path: root/REORG.TODO/locale/programs/repertoire.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/locale/programs/repertoire.c')
-rw-r--r--REORG.TODO/locale/programs/repertoire.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/REORG.TODO/locale/programs/repertoire.c b/REORG.TODO/locale/programs/repertoire.c
new file mode 100644
index 0000000000..61f2c055e7
--- /dev/null
+++ b/REORG.TODO/locale/programs/repertoire.c
@@ -0,0 +1,524 @@
+/* Copyright (C) 1998-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <error.h>
+#include <limits.h>
+#include <obstack.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "localedef.h"
+#include "linereader.h"
+#include "charmap.h"
+#include "repertoire.h"
+#include "simple-hash.h"
+
+
+/* Simple keyword hashing for the repertoiremap.  */
+static const struct keyword_t *repertoiremap_hash (const char *str,
+						   unsigned int len);
+static void repertoire_new_char (struct linereader *lr, hash_table *ht,
+				 hash_table *rt, struct obstack *ob,
+				 uint32_t value, const char *from,
+				 const char *to, int decimal_ellipsis);
+static int repertoire_compare (const void *p1, const void *p2);
+
+/* Already known repertoire maps.  */
+static void *known;
+
+/* List of repertoire maps which are not available and which have been
+   reported to not be.  */
+static void *unavailable;
+
+
+struct repertoire_t *
+repertoire_read (const char *filename)
+{
+  struct linereader *repfile;
+  struct repertoire_t *result;
+  struct repertoire_t **resultp;
+  struct repertoire_t search;
+  int state;
+  char *from_name = NULL;
+  char *to_name = NULL;
+  enum token_t ellipsis = tok_none;
+
+  search.name = filename;
+  resultp = tfind (&search, &known, &repertoire_compare);
+  if (resultp != NULL)
+    return *resultp;
+
+  /* Determine path.  */
+  repfile = lr_open (filename, repertoiremap_hash);
+  if (repfile == NULL)
+    {
+      if (strchr (filename, '/') == NULL)
+	{
+	  char *i18npath = getenv ("I18NPATH");
+	  if (i18npath != NULL && *i18npath != '\0')
+	    {
+	      const size_t pathlen = strlen (i18npath);
+	      char i18npathbuf[pathlen + 1];
+	      char path[strlen (filename) + 1 + pathlen
+		        + sizeof ("/repertoiremaps/") - 1];
+	      char *next;
+	      i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
+
+	      while (repfile == NULL
+		     && (next = strsep (&i18npath, ":")) != NULL)
+		{
+		  stpcpy (stpcpy (stpcpy (path, next), "/repertoiremaps/"),
+			  filename);
+
+		  repfile = lr_open (path, repertoiremap_hash);
+
+		  if (repfile == NULL)
+		    {
+		      stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
+
+		      repfile = lr_open (path, repertoiremap_hash);
+		    }
+		}
+	    }
+
+	  if (repfile == NULL)
+	    {
+	      /* Look in the systems charmap directory.  */
+	      char *buf = xmalloc (strlen (filename) + 1
+				   + sizeof (REPERTOIREMAP_PATH));
+
+	      stpcpy (stpcpy (stpcpy (buf, REPERTOIREMAP_PATH), "/"),
+		      filename);
+	      repfile = lr_open (buf, repertoiremap_hash);
+
+	      free (buf);
+	    }
+	}
+
+      if (repfile == NULL)
+	return NULL;
+    }
+
+  /* We don't want symbolic names in string to be translated.  */
+  repfile->translate_strings = 0;
+
+  /* Allocate room for result.  */
+  result = (struct repertoire_t *) xmalloc (sizeof (struct repertoire_t));
+  memset (result, '\0', sizeof (struct repertoire_t));
+
+  result->name = xstrdup (filename);
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+  obstack_init (&result->mem_pool);
+
+  if (init_hash (&result->char_table, 256)
+      || init_hash (&result->reverse_table, 256)
+      || init_hash (&result->seq_table, 256))
+    {
+      free (result);
+      return NULL;
+    }
+
+  /* We use a state machine to describe the charmap description file
+     format.  */
+  state = 1;
+  while (1)
+    {
+      /* What's on?  */
+      struct token *now = lr_token (repfile, NULL, NULL, NULL, verbose);
+      enum token_t nowtok = now->tok;
+      struct token *arg;
+
+      if (nowtok == tok_eof)
+	break;
+
+      switch (state)
+	{
+	case 1:
+	  /* We haven't yet read any character definition.  This is where
+	     we accept escape_char and comment_char definitions.  */
+	  if (nowtok == tok_eol)
+	    /* Ignore empty lines.  */
+	    continue;
+
+	  if (nowtok == tok_escape_char || nowtok == tok_comment_char)
+	    {
+	      /* We know that we need an argument.  */
+	      arg = lr_token (repfile, NULL, NULL, NULL, verbose);
+
+	      if (arg->tok != tok_ident)
+		{
+		  lr_error (repfile, _("syntax error in prolog: %s"),
+			    _("bad argument"));
+
+		  lr_ignore_rest (repfile, 0);
+		  continue;
+		}
+
+	      if (arg->val.str.lenmb != 1)
+		{
+		  lr_error (repfile, _("\
+argument to <%s> must be a single character"),
+			    nowtok == tok_escape_char ? "escape_char"
+						      : "comment_char");
+
+		  lr_ignore_rest (repfile, 0);
+		  continue;
+		}
+
+	      if (nowtok == tok_escape_char)
+		repfile->escape_char = *arg->val.str.startmb;
+	      else
+		repfile->comment_char = *arg->val.str.startmb;
+
+	      lr_ignore_rest (repfile, 1);
+	      continue;
+	    }
+
+	  if (nowtok == tok_charids)
+	    {
+	      lr_ignore_rest (repfile, 1);
+
+	      state = 2;
+	      continue;
+	    }
+
+	  /* Otherwise we start reading the character definitions.  */
+	  state = 2;
+	  /* FALLTHROUGH */
+
+	case 2:
+	  /* We are now are in the body.  Each line
+	     must have the format "%s %s %s\n" or "%s...%s %s %s\n".  */
+	  if (nowtok == tok_eol)
+	    /* Ignore empty lines.  */
+	    continue;
+
+	  if (nowtok == tok_end)
+	    {
+	      state = 90;
+	      continue;
+	    }
+
+	  if (nowtok != tok_bsymbol)
+	    {
+	      lr_error (repfile,
+			_("syntax error in repertoire map definition: %s"),
+			_("no symbolic name given"));
+
+	      lr_ignore_rest (repfile, 0);
+	      continue;
+	    }
+
+	  /* If the previous line was not completely correct free the
+	     used memory.  */
+	  if (from_name != NULL)
+	    obstack_free (&result->mem_pool, from_name);
+
+	  from_name = (char *) obstack_copy0 (&result->mem_pool,
+					      now->val.str.startmb,
+					      now->val.str.lenmb);
+	  to_name = NULL;
+
+	  state = 3;
+	  continue;
+
+	case 3:
+	  /* We have two possibilities: We can see an ellipsis or an
+	     encoding value.  */
+	  if (nowtok == tok_ellipsis3 || nowtok == tok_ellipsis4
+	      || nowtok == tok_ellipsis2)
+	    {
+	      ellipsis = nowtok;
+	      state = 4;
+	      continue;
+	    }
+	  /* FALLTHROUGH */
+
+	case 5:
+	  /* We expect a value of the form <Uxxxx> or <Uxxxxxxxx> where
+	     the xxx mean a hexadecimal value.  */
+	  state = 2;
+
+	  errno = 0;
+	  if (nowtok != tok_ucs4)
+	    {
+	      lr_error (repfile,
+			_("syntax error in repertoire map definition: %s"),
+			_("no <Uxxxx> or <Uxxxxxxxx> value given"));
+
+	      lr_ignore_rest (repfile, 0);
+	      continue;
+	    }
+
+	  /* We've found a new valid definition.  */
+	  repertoire_new_char (repfile, &result->char_table,
+			       &result->reverse_table, &result->mem_pool,
+			       now->val.ucs4, from_name, to_name,
+			       ellipsis != tok_ellipsis2);
+
+	  /* Ignore the rest of the line.  */
+	  lr_ignore_rest (repfile, 0);
+
+	  from_name = NULL;
+	  to_name = NULL;
+
+	  continue;
+
+	case 4:
+	  if (nowtok != tok_bsymbol)
+	    {
+	      lr_error (repfile,
+			_("syntax error in repertoire map definition: %s"),
+			_("no symbolic name given for end of range"));
+
+	      lr_ignore_rest (repfile, 0);
+	      state = 2;
+	      continue;
+	    }
+
+	  /* Copy the to-name in a safe place.  */
+	  to_name = (char *) obstack_copy0 (&result->mem_pool,
+					    repfile->token.val.str.startmb,
+					    repfile->token.val.str.lenmb);
+
+	  state = 5;
+	  continue;
+
+	case 90:
+	  if (nowtok != tok_charids)
+	    lr_error (repfile, _("\
+%1$s: definition does not end with `END %1$s'"), "CHARIDS");
+
+	  lr_ignore_rest (repfile, nowtok == tok_charids);
+	  break;
+	}
+
+      break;
+    }
+
+  if (state != 2 && state != 90 && !be_quiet)
+    WITH_CUR_LOCALE (error (0, 0, _("%s: premature end of file"),
+			    repfile->fname));
+
+  lr_close (repfile);
+
+  if (tsearch (result, &known, &repertoire_compare) == NULL)
+    /* Something went wrong.  */
+    WITH_CUR_LOCALE (error (0, errno, _("cannot save new repertoire map")));
+
+  return result;
+}
+
+
+void
+repertoire_complain (const char *name)
+{
+  if (tfind (name, &unavailable, (__compar_fn_t) strcmp) == NULL)
+    {
+      WITH_CUR_LOCALE (error (0, errno, _("\
+repertoire map file `%s' not found"), name));
+
+      /* Remember that we reported this map.  */
+      tsearch (name, &unavailable, (__compar_fn_t) strcmp);
+    }
+}
+
+
+static int
+repertoire_compare (const void *p1, const void *p2)
+{
+  struct repertoire_t *r1 = (struct repertoire_t *) p1;
+  struct repertoire_t *r2 = (struct repertoire_t *) p2;
+
+  return strcmp (r1->name, r2->name);
+}
+
+
+static const struct keyword_t *
+repertoiremap_hash (const char *str, unsigned int len)
+{
+  static const struct keyword_t wordlist[] =
+  {
+    {"escape_char",      tok_escape_char,     0},
+    {"comment_char",     tok_comment_char,    0},
+    {"CHARIDS",          tok_charids,         0},
+    {"END",              tok_end,             0},
+  };
+
+  if (len == 11 && memcmp (wordlist[0].name, str, 11) == 0)
+    return &wordlist[0];
+  if (len == 12 && memcmp (wordlist[1].name, str, 12) == 0)
+    return &wordlist[1];
+  if (len == 7 && memcmp (wordlist[2].name, str, 7) == 0)
+    return &wordlist[2];
+  if (len == 3 && memcmp (wordlist[3].name, str, 3) == 0)
+    return &wordlist[3];
+
+  return NULL;
+}
+
+
+static void
+repertoire_new_char (struct linereader *lr, hash_table *ht, hash_table *rt,
+		     struct obstack *ob, uint32_t value, const char *from,
+		     const char *to, int decimal_ellipsis)
+{
+  char *from_end;
+  char *to_end;
+  const char *cp;
+  char *buf = NULL;
+  int prefix_len, len1, len2;
+  unsigned long int from_nr, to_nr, cnt;
+
+  if (to == NULL)
+    {
+      insert_entry (ht, from, strlen (from),
+		    (void *) (unsigned long int) value);
+      /* Please note that it isn't a bug if a symbol is defined more
+	 than once.  All later definitions are simply discarded.  */
+
+      insert_entry (rt, obstack_copy (ob, &value, sizeof (value)),
+		    sizeof (value), (void *) from);
+
+      return;
+    }
+
+  /* We have a range: the names must have names with equal prefixes
+     and an equal number of digits, where the second number is greater
+     or equal than the first.  */
+  len1 = strlen (from);
+  len2 = strlen (to);
+
+  if (len1 != len2)
+    {
+    invalid_range:
+      lr_error (lr, _("invalid names for character range"));
+      return;
+    }
+
+  cp = &from[len1 - 1];
+  if (decimal_ellipsis)
+    while (isdigit (*cp) && cp >= from)
+      --cp;
+  else
+    while (isxdigit (*cp) && cp >= from)
+      {
+	if (!isdigit (*cp) && !isupper (*cp))
+	  lr_error (lr, _("\
+hexadecimal range format should use only capital characters"));
+	--cp;
+      }
+
+  prefix_len = (cp - from) + 1;
+
+  if (cp == &from[len1 - 1] || strncmp (from, to, prefix_len) != 0)
+    goto invalid_range;
+
+  errno = 0;
+  from_nr = strtoul (&from[prefix_len], &from_end, decimal_ellipsis ? 10 : 16);
+  if (*from_end != '\0' || (from_nr == ULONG_MAX && errno == ERANGE)
+      || ((to_nr = strtoul (&to[prefix_len], &to_end,
+			    decimal_ellipsis ? 10 : 16)) == ULONG_MAX
+          && errno == ERANGE)
+      || *to_end != '\0')
+    {
+      lr_error (lr, _("<%s> and <%s> are invalid names for range"),
+		from, to);
+      return;
+    }
+
+  if (from_nr > to_nr)
+    {
+      lr_error (lr, _("upper limit in range is smaller than lower limit"));
+      return;
+    }
+
+  for (cnt = from_nr; cnt <= to_nr; ++cnt)
+    {
+      uint32_t this_value = value + (cnt - from_nr);
+
+      obstack_printf (ob, decimal_ellipsis ? "%.*s%0*ld" : "%.*s%0*lX",
+		      prefix_len, from, len1 - prefix_len, cnt);
+      obstack_1grow (ob, '\0');
+
+      insert_entry (ht, buf, len1,
+		    (void *) (unsigned long int) this_value);
+      /* Please note we don't examine the return value since it is no error
+	 if we have two definitions for a symbol.  */
+
+      insert_entry (rt, obstack_copy (ob, &this_value, sizeof (this_value)),
+		    sizeof (this_value), (void *) from);
+    }
+}
+
+
+uint32_t
+repertoire_find_value (const struct repertoire_t *rep, const char *name,
+		       size_t len)
+{
+  void *result;
+
+  if (rep == NULL)
+    return ILLEGAL_CHAR_VALUE;
+
+  if (find_entry ((hash_table *) &rep->char_table, name, len, &result) < 0)
+    return ILLEGAL_CHAR_VALUE;
+
+  return (uint32_t) ((unsigned long int) result);
+}
+
+
+const char *
+repertoire_find_symbol (const struct repertoire_t *rep, uint32_t ucs)
+{
+  void *result;
+
+  if (rep == NULL)
+    return NULL;
+
+  if (find_entry ((hash_table *) &rep->reverse_table, &ucs, sizeof (ucs),
+		  &result) < 0)
+    return NULL;
+
+  return (const char *) result;
+}
+
+
+struct charseq *
+repertoire_find_seq (const struct repertoire_t *rep, uint32_t ucs)
+{
+  void *result;
+
+  if (rep == NULL)
+    return NULL;
+
+  if (find_entry ((hash_table *) &rep->seq_table, &ucs, sizeof (ucs),
+		  &result) < 0)
+    return NULL;
+
+  return (struct charseq *) result;
+}