about summary refs log tree commit diff
path: root/locale/locfile-parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'locale/locfile-parse.c')
-rw-r--r--locale/locfile-parse.c820
1 files changed, 820 insertions, 0 deletions
diff --git a/locale/locfile-parse.c b/locale/locfile-parse.c
new file mode 100644
index 0000000000..15e25c7afe
--- /dev/null
+++ b/locale/locfile-parse.c
@@ -0,0 +1,820 @@
+/* Copyright (C) 1995 Free Software Foundation, Inc.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <limits.h>
+#include <obstack.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include "localedef.h"
+#include "localeinfo.h"
+#include "token.h"
+
+/* We don't have these constants defined because we don't use them.  Give
+   default values.  */
+#define CTYPE_MB_CUR_MIN 0
+#define CTYPE_MB_CUR_MAX 0
+#define CTYPE_HASH_SIZE 0
+#define CTYPE_HASH_LAYERS 0
+#define CTYPE_CLASS 0
+#define CTYPE_TOUPPER_EB 0
+#define CTYPE_TOLOWER_EB 0
+#define CTYPE_TOUPPER_EL 0
+#define CTYPE_TOLOWER_EL 0
+ 
+
+/* We have all categories defined in `categories.def'.  Now construct
+   the description and data structure used for all categories.  */
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    struct cat_item category##_desc[] =					      \
+      {									      \
+        NO_PAREN items							      \
+      };								      \
+									      \
+    char *category##_values[NELEMS (category##_desc) - 1] = { NULL, };
+#include "categories.def"
+#undef DEFINE_CATEGORY
+
+struct category category[] =
+  {
+#define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
+    [category] = { _NL_NUM_##category, name, NELEMS (category##_desc) - 1,    \
+                   category##_desc, category##_values, in, check, out },
+#include "categories.def"
+#undef DEFINE_CATEGORY
+  };
+#define NCATEGORIES NELEMS (category)
+
+
+#define SYNTAX_ERROR							      \
+    error (0, 0, gettext ("%s:%Zd: syntax error in locale definition file"),  \
+	   locfile_data.filename, locfile_data.line_no)
+
+
+/* Prototypes for local functions.  */
+static int get_byte (char *byte_ptr);
+static char *is_locale_name (int cat_no, const char *str, int len);
+
+
+/* Read a locale definition file FILE.  The format is defined in
+   POSIX.2 2.5.3.  */
+void
+locfile_read (const char *fname)
+{
+  /* Pointer to text of last token.  */
+  char *ptr;
+  /* Length of last token (or if NUMBER the value itself).  */
+  int len;
+  /* The last returned token.  */
+  int token;
+  /* For error correction we remember whether the last token was correct.  */
+  int correct_token = 1;
+
+  /* Open the desired input file on stdin.  */
+  locfile_open (fname);
+
+  while ((token = locfile_lex (&ptr, &len)) != 0)
+    {
+      int cat_no;
+
+      for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+	if (token == category[cat_no].cat_id)
+	  break;
+
+      if (cat_no >= NCATEGORIES)
+	/* A syntax error occured.  No valid category defintion starts.  */
+	{
+	  if (correct_token != 0)
+	    error (0, 0, gettext ("%s:%Zd: locale category start expected"),
+		   locfile_data.filename, locfile_data.line_no);
+
+	  /* To prevent following errors mark as error case.  */
+	  correct_token = 0;
+
+	  /* Synchronization point is the beginning of a new category.
+	     Overread all line upto this silently.  */
+	  ignore_to_eol (0, 0);
+	  continue;
+	}
+
+      /* Rest of the line should be empty.  */
+      ignore_to_eol (0, 1);
+
+      /* Perhaps these category is already specified.  We simply give a
+         warning and overwrite the values.  */
+      if (category[cat_no].filled != 0)
+	error (0, 0, gettext ("%s:%Zd: multiple definition of locale "
+			      "category %s"), locfile_data.filename,
+	       locfile_data.line_no, category[cat_no].name);
+
+      /* We read the first token because this could be the copy statement.  */
+      token = xlocfile_lex (&ptr, &len);
+
+      if (token == TOK_COPY)
+	/* Copying the definitions from an existing locale is requested.  */
+	{
+	  char *str;
+
+	  /* Get the name of the locale to copy from.  */
+	  token = xlocfile_lex (&ptr, &len);
+	  if (token != TOK_IDENT && token != TOK_STRING)
+	    /* No name, then mark error and ignore everything upto next
+	       start of an category section.  */
+	    {
+	      /* To prevent following errors mark as error case.  */
+	      correct_token = 0;
+
+	      /* Synchronization point is the beginning of a new category.
+		 Overread all line upto this silently.  */
+	      ignore_to_eol (0, 0);
+	    }
+	  else if ((str = is_locale_name (cat_no, ptr, len)) != NULL)
+	    /* Yes the name really names an existing locale file.  We are
+	       returned the complete file name.  Store it so that we can
+	       copy it in the output phase.  */
+	    {
+	      category[cat_no].copy_locale = str;
+	      category[cat_no].filled = 1;
+	      
+	      ignore_to_eol (0, 1);
+	    }
+	  else
+	    /* No, the name does not address a valid locale file.  Mark
+	       error case and ignore rest of category.  */
+	    {
+	      char tmp[len + 1];
+	      memcpy (tmp, ptr, len);
+	      tmp[len] = '\0';
+	      error (0, 0, gettext ("%s:%Zd: invalid locale `%s' in copy "
+				    "statement"), locfile_data.filename,
+			 locfile_data.line_no, tmp);
+	      correct_token = 0;
+	      ignore_to_eol (0, 0);
+	    }
+
+	  /* This should END as the next token.  */
+	  token = xlocfile_lex (&ptr, &len);
+
+	  if (token == TOK_END)
+	    /* This is the end of the category.  */
+	    {
+	      token = xlocfile_lex (&ptr, &len);
+
+	      if (token != category[cat_no].cat_id)
+		/* Wrong category name after END.  */
+		{
+		  error (0, 0, gettext ("%s:%Zd: category `%s' does not "
+					"end with `END %s'"),
+			 locfile_data.filename, locfile_data.line_no,
+			 category[cat_no].name, category[cat_no].name);
+		  ignore_to_eol (0, 0);
+		}
+	      else
+		ignore_to_eol (0, 1);
+
+	      correct_token = 1;
+	    }
+	  else
+	    /* No END following copy.  Give error while not in error case.  */
+	    {
+	      if (correct_token != 0)
+		error (0, 0, gettext ("%s:%Zd: `copy' must be sole rule"),
+		       locfile_data.filename, locfile_data.line_no);
+	      correct_token = 0;
+	      ignore_to_eol (0, 0);
+	    }
+
+	  continue;
+	}
+
+      /* Now it's time to mark as mentioned in the locale file.  */
+      category[cat_no].filled = 1;
+
+      if (category[cat_no].infct != NULL)
+	/* The category needs a special input handling.  */
+	{
+	  category[cat_no].infct(token);
+	  continue;
+	}
+
+      /* Now process the given items.  */
+      while (1)
+	{
+	  int item_no;
+
+	  if (token == TOK_END)
+	    /* This is the end of the category.  */
+	    {
+	      token = xlocfile_lex (&ptr, &len);
+
+	      if (token != category[cat_no].cat_id)
+		{
+		  error (0, 0, gettext ("%s:%Zd: category `%s' does not end "
+					"with `END %s'"),
+			 locfile_data.filename, locfile_data.line_no,
+			 category[cat_no].name, category[cat_no].name);
+		  ignore_to_eol (0, 0);
+		}
+	      else
+		ignore_to_eol (0, 1);
+
+	      /* Start next category.  */
+	      break;
+	    }
+
+	  /* All other lines should describe valid items of the category.  */
+	  for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+	    if (category[cat_no].item_desc[item_no].item_id == token)
+	      break;
+
+	  if (item_no >= category[cat_no].number)
+	    /* This is not a valid item of the category.  */
+	    {
+	      SYNTAX_ERROR;
+	      ignore_to_eol (0, 0);
+
+	      token = xlocfile_lex (&ptr, &len);
+
+	      /* And process next item.  */
+	      continue;
+	    }
+
+	  /* Test whether already a value is defined.  */
+	  if (category[cat_no].item_value[item_no] != NULL)
+	    error (0, 0, gettext ("%s:%Zd: category item `%s' already "
+				  "defined"),
+		   locfile_data.filename, locfile_data.line_no,
+		   category[cat_no].item_desc[item_no].name);
+
+	  switch (category[cat_no].item_desc[item_no].value_type)
+	    {
+	    case string:
+	      /* Get next token.  This is the argument to the item.  */
+	      token = xlocfile_lex (&ptr, &len);
+
+	      if (token != TOK_STRING)
+		SYNTAX_ERROR;
+	      else
+		category[cat_no].item_value[item_no] = strdup (ptr);
+	      ignore_to_eol (0, ptr != NULL);
+	      break;
+	    case stringarray:
+	      /* This is a difficult case.  The number of strings in
+		 the array may vary.  But for now its only necessary
+		 with ALT_DIGITS from LC_TIME.  This item is the last
+		 so be can solve it by storing the number of string in
+		 the first place and the string indeces following
+		 that.  */
+	      {
+		int cnt;
+		char **buffer;
+		if (category[cat_no].item_value[item_no] != NULL)
+		  buffer = (char **) category[cat_no].item_value[item_no];
+		else
+		  buffer = (char **) xmalloc (
+		    sizeof (char *) * category[cat_no].item_desc[item_no].max);
+
+		category[cat_no].item_value[item_no] = (char *) buffer;
+
+		/* As explained we may need a place to store the real number
+		   of strings.  */
+		if (category[cat_no].item_desc[item_no].min
+		    != category[cat_no].item_desc[item_no].max)
+		  ++buffer;
+
+		cnt = 0;
+		do
+		  {
+		    token = xlocfile_lex (&ptr, &len);
+		    if (token != TOK_STRING)
+		      {
+			SYNTAX_ERROR;
+			break;
+		      }
+
+		    if (cnt >= category[cat_no].item_desc[item_no].max)
+		      {
+			error (0, 0, gettext ("%s:%Zd: too many elements "
+					      "for item `%s`"),
+			       locfile_data.filename, locfile_data.line_no,
+			       category[cat_no].item_desc[item_no].name);
+			break;
+		      }
+
+		    buffer[cnt++] = strdup (ptr);
+
+		    token = locfile_lex (&ptr, &len);
+		  }
+		while (token == TOK_CHAR && len == ';');
+
+		ignore_to_eol (token, ptr != NULL);
+
+		if (cnt < category[cat_no].item_desc[item_no].min)
+		  error (0, 0, gettext ("%s:%Zd: too few elements for item "
+					"`%s'"),
+			 locfile_data.filename, locfile_data.line_no,
+			 category[cat_no].item_desc[item_no].name);
+
+		if (category[cat_no].item_desc[item_no].min
+		    != category[cat_no].item_desc[item_no].max)
+		  *(int *) category[cat_no].item_value[item_no] = cnt;
+	      }
+	      break;
+	    case byte:
+	      {
+		int ok;
+		category[cat_no].item_value[item_no] = (char *) xmalloc (
+		  __alignof__ (char));
+		ok = get_byte (category[cat_no].item_value[item_no]);
+		ignore_to_eol (0, ok);
+	      }
+	      break;
+	    case bytearray:
+	      {
+		char *buffer;
+		int maxsize;
+		int cnt;
+		char byte;
+		int ok;
+
+		buffer = (char *) xmalloc ((maxsize = 30));
+		cnt = 0;
+
+		while ((ok = get_byte (&byte)))
+		  {
+		    if (cnt >= maxsize)
+		      buffer = (char *) xmalloc ((maxsize *= 2));
+
+		    buffer[cnt++] = byte;
+
+		    token = locfile_lex (&ptr, &len);
+		    if (token != TOK_CHAR || len != ';')
+		      break;
+		  }
+
+		buffer[cnt] = '\0';
+		category[cat_no].item_value[item_no] = buffer;
+		ignore_to_eol (token, ok);
+	      }
+	      break;
+	    default:
+	      error (5, 0, gettext ("internal error in %s, line %u"),
+		     __FUNCTION__, __LINE__);
+	      /* NOTREACHED */
+	    }
+
+	  /* Get next token.  */
+	  token = xlocfile_lex (&ptr, &len);
+	} /* while (1) */
+    }
+}
+
+
+/* Check given values for categories for consistency.  */
+void
+categories_check (void)
+{
+  int cat_no;
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    if (category[cat_no].copy_locale == NULL)
+      if (category[cat_no].filled != 0)
+	if (category[cat_no].checkfct)
+	  category[cat_no].checkfct();
+	else
+	  {
+	    int item_no;
+
+	    for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+	      if (category[cat_no].item_value[item_no] == NULL)
+		{
+		  int errcode;
+
+		  /* If the item is defined in the standard is it an error to
+		     have it not defined.  */
+		  errcode = category[cat_no].item_desc[item_no].status == std
+		            ? 5 : 0;
+
+		  error (errcode, 0, gettext ("item `%s' of category `%s' "
+					      "undefined"),
+			 category[cat_no].item_desc[item_no].name,
+			 category[cat_no].name);
+		}
+	  }
+      else
+	error (0, 0, gettext ("category `%s' not defined"),
+	       category[cat_no].name);
+}
+
+
+/* Write out the binary representation of the category data which can be
+   loaded by setlocale(1).  */
+void
+categories_write (void)
+{
+  struct locale_file
+  {
+      int magic;
+      int n;
+      int idx[0];
+  } *data;
+  struct obstack obstk;
+  int cat_no;
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+  obstack_init (&obstk);
+
+  for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
+    {
+      int result = 0;
+
+      if (category[cat_no].copy_locale != NULL)
+	/* Simply copy the addressed locale file of the specified
+	   category.  Please note that this is tried before the distinction
+	   between categories which need special handling is made.  */
+	{
+	  int source;
+
+	  /* Open source file.  */
+	  source = open (category[cat_no].copy_locale, O_RDONLY);
+	  if (source < 0)
+	    error (0, 0, gettext ("cannot copy locale definition file `%s'"),
+		   category[cat_no].copy_locale);
+	  else
+	    {
+	      /* Construct file name of output file and open for writing.  */
+	      char path[strlen (output_path)
+                        + strlen(category[cat_no].name) + 1];
+	      int dest;
+	      char *t;
+
+	      t = stpcpy (path, output_path);
+	      strcpy (t, category[cat_no].name);
+
+	      dest = creat (path, 0666);
+	      if (dest == -1)
+		error (0, 0, gettext ("cannot open output file `%s': %m"),
+		       path);
+	      else
+		{
+		  char buffer[BUFSIZ];
+		  int size;
+		  
+		  /* Copy the files.  */
+		  do
+		    {
+		      size = read (source, buffer, BUFSIZ);
+		      write (dest, buffer, size);
+		    }
+		  while (size > 0);
+
+		  close (dest);
+
+		  /* Show success.  */
+		  puts (category[cat_no].name);
+		}
+	      close (source);
+	    }
+
+	  /* Next category.   */
+	  continue;
+	}
+
+      if (category[cat_no].outfct)
+	result = category[cat_no].outfct();
+      else
+	{
+	  char *path, *t;
+	  int fd;
+	  struct iovec *iov;
+	  int item_no, len, slen, cnt;
+	  int elems = 0;
+
+	  /* Count number of elements.  */
+	  for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+	    {
+	      switch (category[cat_no].item_desc[item_no].value_type)
+		{
+		case string:
+		case byte:
+		case bytearray:
+		  ++elems;
+		  break;
+		case stringarray:
+		  elems += category[cat_no].item_desc[item_no].max;
+		  break;
+		default:
+		  error (5, 0, gettext ("internal error in %s, line %u"),
+			 __FUNCTION__, __LINE__);
+		  /* NOTREACHED */
+		}
+	    }
+
+	  /* We now have the number of elements.  We build the structure
+	     and a helper structure for writing all out.  */
+	  len = sizeof (struct locale_file) + elems * sizeof (int);
+	  data = obstack_alloc (&obstk, len);
+	  iov = obstack_alloc (&obstk, (elems + 1) * sizeof (struct iovec));
+
+	  data->magic = LIMAGIC (cat_no);
+	  data->n = elems;
+	  iov[0].iov_base = data;
+	  iov[0].iov_len = len;
+
+	  cnt = 0;
+	  for (item_no = 0; item_no < category[cat_no].number; ++item_no)
+	    if (category[cat_no].item_value[item_no] == NULL)
+	      {
+		switch (category[cat_no].item_desc[item_no].value_type)
+		  {
+		  case string:
+		  case byte:
+		  case bytearray:
+		    data->idx[cnt] = len;
+		    ++len;  /* We reserve one single byte for this entry.  */
+		    iov[1 + cnt].iov_base = (char *) "";
+		    iov[1 + cnt].iov_len = 1;
+		    ++cnt;
+		    break;
+		  case stringarray:
+		    {
+		      int max;
+		      int nstr;
+
+		      max = category[cat_no].item_desc[item_no].max;
+
+		      for (nstr = 0; nstr < max; ++nstr)
+			{
+			  data->idx[cnt] = len;
+			  ++len;
+			  iov[1 + cnt].iov_base = (char *) "";
+			  iov[1 + cnt].iov_len = 1;
+			  ++cnt;
+			}
+		    }
+		  }
+	      }
+	    else
+	      switch (category[cat_no].item_desc[item_no].value_type)
+		{
+		case string:
+		case bytearray:
+		  data->idx[cnt] = len;
+		  slen = strlen (category[cat_no].item_value[item_no]) + 1;
+		  len += slen;
+		  iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
+		  iov[1 + cnt].iov_len = slen;
+		  ++cnt;
+		  break;
+		case byte:
+		  data->idx[cnt] = len;
+		  slen = 1;
+		  len += slen;
+		  iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
+		  iov[1 + cnt].iov_len = slen;
+		  ++cnt;
+		  break;
+		case stringarray:
+		  {
+		    int nstr, nact;
+		    char **first;
+		  
+		    if (category[cat_no].item_desc[item_no].min
+			== category[cat_no].item_desc[item_no].max)
+		      {
+			nstr = category[cat_no].item_desc[item_no].min;
+			first = (char **) category[cat_no].item_value[item_no];
+		      }
+		    else
+		      {
+			nstr = *(int *) category[cat_no].item_value[item_no];
+			first =
+			  ((char **) category[cat_no].item_value[item_no]) + 1;
+		      }
+		    nact = nstr;
+		    while (nstr > 0)
+		      {
+			data->idx[cnt] = len;
+			if (*first != NULL)
+			  {
+			    slen = strlen (*first) + 1;
+			    iov[1 + cnt].iov_base = first;
+			  }
+			else
+			  {
+			    slen = 1;
+			    iov[1 + cnt].iov_base = (char *) "";
+			  }
+			len += slen;
+			iov[1 + cnt].iov_len = slen;
+			++cnt;
+			++first;
+			--nstr;
+		      }
+		    while (nact < category[cat_no].item_desc[item_no].max)
+		      {
+			data->idx[cnt] = len;
+			len += 1;
+			iov[1 + cnt].iov_base = (char *) "";
+			iov[1 + cnt].iov_len = 1;
+			++cnt;
+			++nact;
+		      }
+		  }
+		  break;
+		default:
+		  /* Cannot happen.  */
+		  break;
+		}
+	  assert (cnt <= elems);
+
+	  /* Construct the output filename from the argument given to
+	     localedef on the command line.  */
+	  path = (char *) obstack_alloc (&obstk, strlen (output_path) +
+					 strlen (category[cat_no].name) + 1);
+	  t = stpcpy (path, output_path);
+	  strcpy (t, category[cat_no].name);
+
+	  fd = creat (path, 0666);
+	  if (fd == -1)
+	    {
+	      error (0, 0, gettext ("cannot open output file `%s': %m"), path);
+	      result = 1;
+	    }
+	  else
+	    {
+	      if (writev (fd, iov, cnt + 1) == -1)
+		{
+		  error (0, 0, gettext ("cannot write output file `%s': %m"),
+			 path);
+		  result = 1;
+		}
+
+if (elems==0) write(fd, &elems, 1);
+
+	      close (fd);
+	    }
+	  /* The old data is not needed anymore, but keep the obstack
+	     intact.  */
+	  obstack_free (&obstk, data);
+	}
+
+      if (result == 0)
+	puts (category[cat_no].name);
+    }
+  /* Now the whole obstack can be removed.  */
+  obstack_free (&obstk, NULL);
+}
+
+
+/* Get the representation of a number.  This is a positive integer or
+   the number -1 which is handled as a special symbol by the scanner.  */
+static int
+get_byte (char *byte_ptr)
+{
+  int token;
+  char *ptr;
+  int len;
+
+  token = locfile_lex (&ptr, &len);
+  if (token != TOK_NUMBER && token != TOK_MINUS1)
+    /* None of the valid number format.  */
+    {
+      error (0, 0, gettext ("%s:%Zd: number expected"),
+	     locfile_data.filename, locfile_data.line_no);
+      *byte_ptr = 0;
+      return 0;
+    }
+
+  if (token == TOK_MINUS1)
+    {
+      *byte_ptr = CHAR_MAX;
+      return 1;
+    }
+
+  if (len > CHAR_MAX)
+    /* The value of the numbers has to be less than CHAR_MAX.  This is
+       ok for the information they have to express.  */
+    {
+      error (0, 0, gettext ("%s:%Zd: invalid number"),
+	     locfile_data.filename, locfile_data.line_no);
+      *byte_ptr = 0;
+      return 0;
+    }
+
+  *byte_ptr = len;
+  return 1;
+}
+
+
+/* Test whether the string STR with length LEN is the name of an existing
+   locale and whether a file for category CAT_NO is found in this directory.
+   This categories are looked for in the system locale definition file
+   directory.
+   Return the complete file name for the category file.  */
+static char *
+is_locale_name (int cat_no, const char *str, int len)
+{
+  static char **locale_names = NULL;
+  static int max_count = 0;
+  static int locale_count = 0;
+  int cnt, exist, fd;
+  char *fname;
+  struct stat st;
+
+  if (locale_names == NULL)
+    /* Read in the list of all available locales.  */
+    {
+      DIR *dir;
+      struct dirent *dirent;
+      
+      /* LOCALE_NAMES is not NULL anymore, but LOCALE_COUNT == 0.  */
+      ++locale_names;
+	  
+      dir = opendir (LOCALE_PATH);
+      if (dir == NULL)
+	{
+	  error (1, errno, gettext ("cannot read locale directory `%s'"),
+		 LOCALE_PATH);
+
+	  return NULL;
+	}
+
+      /* Now we can look for all files in the directory.  */
+      while ((dirent = readdir (dir)) != NULL)
+	if (strcmp (dirent->d_name, ".") != 0
+	    && strcmp (dirent->d_name, "..") != 0)
+	  {
+	    if (max_count == 0)
+	      locale_names = (char **) xmalloc ((max_count = 10)
+						* sizeof (char *));
+	    else
+	      locale_names = (char **) xrealloc (locale_names,
+						 (max_count *= 2)
+						 * sizeof (char *));
+	    locale_names[locale_count++] = strdup (dirent->d_name);
+	  }
+      closedir (dir);
+    }
+
+  for (cnt = 0; cnt < locale_count; ++cnt)
+    if (strncmp (str, locale_names[cnt], len) == 0
+	&& locale_names[cnt][len] == '\0')
+      break;
+  
+  if (cnt >= locale_count)
+    return NULL;
+  
+  /* Now search for this specific locale file.  */
+  asprintf (&fname, "%s/%s/%s", LOCALE_PATH, locale_names[cnt],
+	    category[cat_no].name);
+  
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    {
+      free (fname);
+      return NULL;
+    }
+  
+  exist = fstat (fd, &st);
+  close (fd);
+  
+  if (exist < 0)
+    {
+      free (fname);
+      return NULL;
+    }
+
+  return fname;
+}
+
+/*
+ * Local Variables:
+ *  mode:c
+ *  c-basic-offset:2
+ * End:
+ */