about summary refs log tree commit diff
path: root/intl/finddomain.c
diff options
context:
space:
mode:
Diffstat (limited to 'intl/finddomain.c')
-rw-r--r--intl/finddomain.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/intl/finddomain.c b/intl/finddomain.c
new file mode 100644
index 0000000000..19cf2d7581
--- /dev/null
+++ b/intl/finddomain.c
@@ -0,0 +1,476 @@
+/* finddomain.c -- handle list of needed message catalogs
+   Copyright (C) 1995 Software Foundation, Inc.
+   Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+
+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; either version 2, 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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#if defined STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+#else
+# ifdef HAVE_MALLOC_H
+#  include <malloc.h>
+# else
+void free ();
+# endif
+#endif
+
+#if defined HAVE_STRING_H || defined _LIBC
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+#if !HAVE_STRCHR && !defined _LIBC
+# ifndef strchr
+#  define strchr index
+# endif
+#endif
+
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgettext.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ANSI C functions.  This is required by the standard
+   because some ANSI C functions will require linking with this object
+   file and the name space must not be polluted.  */
+# define stpcpy __stpcpy
+#endif
+
+/* Encoding of locale name parts.  */
+#define CEN_REVISION	1
+#define CEN_SPONSOR	2
+#define CEN_SPECIAL	4
+#define XPG_CODESET	8
+#define TERRITORY	16
+#define CEN_AUDIENCE	32
+#define XPG_MODIFIER	64
+
+#define CEN_SPECIFIC	(CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE)
+#define XPG_SPECIFIC	(XPG_CODESET|XPG_MODIFIER)
+
+
+/* List of already loaded domains.  */
+static struct loaded_domain *_nl_loaded_domains;
+
+/* Prototypes for local functions.  */
+static struct loaded_domain *make_entry_rec __P ((const char *dirname,
+						  int mask,
+						  const char *language,
+						  const char *territory,
+						  const char *codeset,
+						  const char *modifier,
+						  const char *special,
+						  const char *sponsor,
+						  const char *revision,
+						  const char *domainname,
+						  int do_allocate));
+
+
+/* Return a data structure describing the message catalog described by
+   the DOMAINNAME and CATEGORY parameters with respect to the currently
+   established bindings.  */
+struct loaded_domain *
+_nl_find_domain (dirname, locale, domainname)
+     const char *dirname;
+     char *locale;
+     const char *domainname;
+{
+  enum { undecided, xpg, cen } syntax;
+  struct loaded_domain *retval;
+  const char *language;
+  const char *modifier = NULL;
+  const char *territory = NULL;
+  const char *codeset = NULL;
+  const char *special = NULL;
+  const char *sponsor = NULL;
+  const char *revision = NULL;
+  const char *alias_value = NULL;
+  char *cp;
+  int mask;
+
+  /* CATEGORYVALUE now possibly contains a colon separated list of
+     locales.  Each single locale can consist of up to four recognized
+     parts for the XPG syntax:
+
+		language[_territory[.codeset]][@modifier]
+
+     and six parts for the CEN syntax:
+
+	language[_territory][+audience][+special][,sponsor][_revision]
+
+     Beside the first all of them are allowed to be missing.  If the
+     full specified locale is not found, the less specific one are
+     looked for.  The various part will be stripped of according to
+     the following order:
+		(1) revision
+		(2) sponsor
+		(3) special
+		(4) codeset
+		(5) territory
+		(6) audience/modifier
+   */
+
+  /* If we have already tested for this locale entry there has to
+     be one data set in the list of loaded domains.  */
+  retval = make_entry_rec (dirname, 0, locale, NULL, NULL, NULL,
+			   NULL, NULL, NULL, domainname, 0);
+  if (retval != NULL)
+    {
+      /* We know something about this locale.  */
+      int cnt;
+
+      if (retval->decided == 0)
+	_nl_load_domain (retval); /* @@@ */
+
+      if (retval->data != NULL)
+	return retval;
+
+      for (cnt = 6; cnt >= 0; --cnt)
+	{
+	  if (retval->successor[cnt] == 0)
+	    _nl_load_domain (retval->successor[cnt]);
+
+	  if (retval->successor[cnt]->data != NULL)
+	    break;
+	}
+
+      /* We really found some usable information.  */
+      return cnt >= 0 ? retval : NULL;
+      /* NOTREACHED */
+    }
+
+  /* See whether the locale value is an alias.  If yes its value
+     *overwrites* the alias name.  No test for the original value is
+     done.  */
+  alias_value = _nl_expand_alias (locale);
+  if (alias_value != NULL)
+    {
+      size_t len = strlen (alias_value) + 1;
+      locale = (char *) malloc (len);
+      if (locale == NULL)
+	return NULL;
+
+      memcpy (locale, alias_value, len);
+    }
+
+  /* Now we determine the single parts of the locale name.  First
+     look for the language.  Termination symbols are `_' and `@' if
+     we use XPG4 style, and `_', `+', and `,' if we use CEN syntax.  */
+  mask = 0;
+  syntax = undecided;
+  language = cp = locale;
+  while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@'
+	 && cp[0] != '+' && cp[0] != ',')
+    ++cp;
+
+  if (language == cp)
+    /* This does not make sense: language has to be specified.  Use
+       this entry as it is without exploding.  Perhaps it is an alias.  */
+    cp = strchr (language, '\0');
+  else if (cp[0] == '_')
+    {
+      /* Next is the territory.  */
+      cp[0] = '\0';
+      territory = ++cp;
+
+      while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@'
+	     && cp[0] != '+' && cp[0] != ',' && cp[0] != '_')
+	++cp;
+
+      mask |= TERRITORY;
+
+      if (cp[0] == '.')
+	{
+	  /* Next is the codeset.  */
+	  syntax = xpg;
+	  cp[0] = '\0';
+	  codeset = ++cp;
+
+	  while (cp[0] != '\0' && cp[0] != '@')
+	    ++cp;
+
+	  mask |= XPG_CODESET;
+	}
+    }
+
+  if (cp[0] == '@' || (syntax != xpg && cp[0] == '+'))
+    {
+      /* Next is the modifier.  */
+      syntax = cp[0] == '@' ? xpg : cen;
+      cp[0] = '\0';
+      modifier = ++cp;
+
+      while (syntax == cen && cp[0] != '\0' && cp[0] != '+'
+	     && cp[0] != ',' && cp[0] != '_')
+	++cp;
+
+      mask |= XPG_MODIFIER | CEN_AUDIENCE;
+    }
+
+  if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_'))
+    {
+      syntax = cen;
+
+      if (cp[0] == '+')
+	{
+ 	  /* Next is special application (CEN syntax).  */
+	  cp[0] = '\0';
+	  special = ++cp;
+
+	  while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_')
+	    ++cp;
+
+	  mask |= CEN_SPECIAL;
+	}
+
+      if (cp[0] == ',')
+	{
+ 	  /* Next is sponsor (CEN syntax).  */
+	  cp[0] = '\0';
+	  sponsor = ++cp;
+
+	  while (cp[0] != '\0' && cp[0] != '_')
+	    ++cp;
+
+	  mask |= CEN_SPONSOR;
+	}
+
+      if (cp[0] == '_')
+	{
+ 	  /* Next is revision (CEN syntax).  */
+	  cp[0] = '\0';
+	  revision = ++cp;
+
+	  mask |= CEN_REVISION;
+	}
+    }
+
+  /* For CEN sytnax values it might be important to have the
+     separator character in the file name, not for XPG syntax.  */
+  if (syntax == xpg)
+    {
+      if (territory[0] == '\0')
+	mask &= ~TERRITORY;
+
+      if (codeset[0] == '\0')
+	mask &= ~XPG_CODESET;
+
+      if (modifier[0] == '\0')
+	mask &= ~XPG_MODIFIER;
+    }
+
+  /* Create all possible locale entries which might be interested in
+     generalzation.  */
+  retval = make_entry_rec (dirname, mask, language, territory, codeset,
+			   modifier, special, sponsor, revision,
+			   domainname, 1);
+  if (retval == NULL)
+    /* This means we are out of core.  */
+    return NULL;
+
+  if (retval->decided == 0)
+    _nl_load_domain (retval);
+  if (retval->data == NULL)
+    {
+      int cnt;
+      for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
+	{
+	  if (retval->successor[cnt]->decided == 0)
+	    _nl_load_domain (retval->successor[cnt]);
+	  if (retval->successor[cnt]->data != NULL)
+	    break;
+
+	  /* Signal that locale is not available.  */
+	  retval->successor[cnt] = NULL;
+	}
+      if (retval->successor[cnt] == NULL)
+	retval = NULL;
+    }
+
+  /* The room for an alias was dynamically allocated.  Free it now.  */
+  if (alias_value != NULL)
+    free (locale);
+
+  return retval;
+}
+
+
+static struct loaded_domain *
+make_entry_rec (dirname, mask, language, territory, codeset, modifier,
+		special, sponsor, revision, domain, do_allocate)
+     const char *dirname;
+     int mask;
+     const char *language;
+     const char *territory;
+     const char *codeset;
+     const char *modifier;
+     const char *special;
+     const char *sponsor;
+     const char *revision;
+     const char *domain;
+     int do_allocate;
+{
+  struct loaded_domain *retval, *last;
+  char *filename, *cp;
+  size_t entries;
+  int cnt;
+
+  /* Allocate room for the full file name.  */
+  filename = (char *) malloc (strlen (dirname) + 1
+			      + strlen (language)
+			      + ((mask & TERRITORY) != 0
+				 ? strlen (territory) : 0)
+			      + ((mask & XPG_CODESET) != 0
+				 ? strlen (codeset) : 0)
+			      + ((mask & XPG_MODIFIER) != 0 ?
+				 strlen (modifier) : 0)
+			      + ((mask & CEN_SPECIAL) != 0
+				 ? strlen (special) : 0)
+			      + ((mask & CEN_SPONSOR) != 0
+				 ? strlen (sponsor) : 0)
+			      + ((mask & CEN_REVISION) != 0
+				 ? strlen (revision) : 0) + 1
+			      + strlen (domain) + 1);
+
+  if (filename == NULL)
+    return NULL;
+
+  retval = NULL;
+  last = NULL;
+
+  /* We don't want libintl.a to depend on any other library.  So we
+     avoid the non-standard function stpcpy.  In GNU C Library this
+     function is available, though.  Also allow the symbol HAVE_STPCPY
+     to be defined.  */
+#if !defined _LIBC && !defined HAVE_STPCPY
+# define stpcpy(p, s)							    \
+  (strcpy (p, s), strchr (p, '\0'))
+#endif
+
+  /* Construct file name.  */
+  cp = stpcpy (filename, dirname);
+  *cp++ = '/';
+  cp = stpcpy (cp, language);
+
+  if ((mask & TERRITORY) != 0)
+    {
+      *cp++ = '_';
+      cp = stpcpy (cp, territory);
+    }
+  if ((mask & XPG_CODESET) != 0)
+    {
+      *cp++ = '.';
+      cp = stpcpy (cp, codeset);
+    }
+  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
+    {
+      /* This component can be part of both syntaces but has different
+	 leading characters.  For CEN we use `+', else `@'.  */
+      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
+      cp = stpcpy (cp, modifier);
+    }
+  if ((mask & CEN_SPECIAL) != 0)
+    {
+      *cp++ = '+';
+      cp = stpcpy (cp, special);
+    }
+  if ((mask & CEN_SPONSOR) != 0)
+    {
+      *cp++ = ',';
+      cp = stpcpy (cp, sponsor);
+    }
+  if ((mask & CEN_REVISION) != 0)
+    {
+      *cp++ = '_';
+      cp = stpcpy (cp, revision);
+    }
+
+  *cp++ = '/';
+  stpcpy (cp, domain);
+
+  /* Look in list of already loaded domains whether it is already
+     available.  */
+  last = NULL;
+  for (retval = _nl_loaded_domains; retval != NULL; retval = retval->next)
+    {
+      int compare = strcmp (retval->filename, filename);
+      if (compare == 0)
+	/* We found it!  */
+	break;
+      if (compare < 0)
+	{
+	  /* It's not in the list.  */
+	  retval = NULL;
+	  break;
+	}
+
+      last = retval;
+    }
+
+  if (retval != NULL || do_allocate == 0)
+    {
+      free (filename);
+      return retval;
+    }
+
+  retval = (struct loaded_domain *) malloc (sizeof (*retval));
+  if (retval == NULL)
+    return NULL;
+
+  retval->filename = filename;
+  retval->decided = 0;
+
+  if (last == NULL)
+    {
+      retval->next = _nl_loaded_domains;
+     _nl_loaded_domains = retval;
+     }
+  else
+    {
+      retval->next = last->next;
+      last->next = retval;
+    }
+
+  entries = 0;
+  for (cnt = 126; cnt >= 0; --cnt)
+    if (cnt < mask && (cnt & ~mask) == 0
+	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0))
+      retval->successor[entries++] = make_entry_rec (dirname, cnt,
+						     language, territory,
+						     codeset, modifier,
+						     special, sponsor,
+						     revision, domain, 1);
+  retval->successor[entries] = NULL;
+
+  return retval;
+}