Thu Jan 18 00:32:43 1996 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / locale / locfile-parse.c
1 /* Copyright (C) 1995 Free Software Foundation, Inc.
2
3 The GNU C Library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Library General Public License as
5 published by the Free Software Foundation; either version 2 of the
6 License, or (at your option) any later version.
7
8 The GNU C Library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public
14 License along with the GNU C Library; see the file COPYING.LIB.  If
15 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
16 Cambridge, MA 02139, USA.  */
17
18 #include <errno.h>
19 #include <assert.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <langinfo.h>
23 #include <libintl.h>
24 #include <limits.h>
25 #include <obstack.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/uio.h>
32
33 #include "localedef.h"
34 #include "localeinfo.h"
35 #include "token.h"
36
37 /* We don't have these constants defined because we don't use them.  Give
38    default values.  */
39 #define CTYPE_MB_CUR_MIN 0
40 #define CTYPE_MB_CUR_MAX 0
41 #define CTYPE_HASH_SIZE 0
42 #define CTYPE_HASH_LAYERS 0
43 #define CTYPE_CLASS 0
44 #define CTYPE_TOUPPER_EB 0
45 #define CTYPE_TOLOWER_EB 0
46 #define CTYPE_TOUPPER_EL 0
47 #define CTYPE_TOLOWER_EL 0
48  
49
50 /* We have all categories defined in `categories.def'.  Now construct
51    the description and data structure used for all categories.  */
52 #define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
53     struct cat_item category##_desc[] =                                       \
54       {                                                                       \
55         NO_PAREN items                                                        \
56       };                                                                      \
57                                                                               \
58     char *category##_values[NELEMS (category##_desc) - 1] = { NULL, };
59 #include "categories.def"
60 #undef DEFINE_CATEGORY
61
62 struct category category[] =
63   {
64 #define DEFINE_CATEGORY(category, name, items, postload, in, check, out)      \
65     [category] = { _NL_NUM_##category, name, NELEMS (category##_desc) - 1,    \
66                    category##_desc, category##_values, in, check, out },
67 #include "categories.def"
68 #undef DEFINE_CATEGORY
69   };
70 #define NCATEGORIES NELEMS (category)
71
72
73 #define SYNTAX_ERROR                                                          \
74     error (0, 0, gettext ("%s:%Zd: syntax error in locale definition file"),  \
75            locfile_data.filename, locfile_data.line_no)
76
77
78 /* Prototypes for local functions.  */
79 static int get_byte (char *byte_ptr);
80 static char *is_locale_name (int cat_no, const char *str, int len);
81
82
83 /* Read a locale definition file FILE.  The format is defined in
84    POSIX.2 2.5.3.  */
85 void
86 locfile_read (const char *fname)
87 {
88   /* Pointer to text of last token.  */
89   char *ptr;
90   /* Length of last token (or if NUMBER the value itself).  */
91   int len;
92   /* The last returned token.  */
93   int token;
94   /* For error correction we remember whether the last token was correct.  */
95   int correct_token = 1;
96
97   /* Open the desired input file on stdin.  */
98   locfile_open (fname);
99
100   while ((token = locfile_lex (&ptr, &len)) != 0)
101     {
102       int cat_no;
103
104       for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
105         if (token == category[cat_no].cat_id)
106           break;
107
108       if (cat_no >= NCATEGORIES)
109         /* A syntax error occured.  No valid category defintion starts.  */
110         {
111           if (correct_token != 0)
112             error (0, 0, gettext ("%s:%Zd: locale category start expected"),
113                    locfile_data.filename, locfile_data.line_no);
114
115           /* To prevent following errors mark as error case.  */
116           correct_token = 0;
117
118           /* Synchronization point is the beginning of a new category.
119              Overread all line upto this silently.  */
120           ignore_to_eol (0, 0);
121           continue;
122         }
123
124       /* Rest of the line should be empty.  */
125       ignore_to_eol (0, 1);
126
127       /* Perhaps these category is already specified.  We simply give a
128          warning and overwrite the values.  */
129       if (category[cat_no].filled != 0)
130         error (0, 0, gettext ("%s:%Zd: multiple definition of locale "
131                               "category %s"), locfile_data.filename,
132                locfile_data.line_no, category[cat_no].name);
133
134       /* We read the first token because this could be the copy statement.  */
135       token = xlocfile_lex (&ptr, &len);
136
137       if (token == TOK_COPY)
138         /* Copying the definitions from an existing locale is requested.  */
139         {
140           char *str;
141
142           /* Get the name of the locale to copy from.  */
143           token = xlocfile_lex (&ptr, &len);
144           if (token != TOK_IDENT && token != TOK_STRING)
145             /* No name, then mark error and ignore everything upto next
146                start of an category section.  */
147             {
148               /* To prevent following errors mark as error case.  */
149               correct_token = 0;
150
151               /* Synchronization point is the beginning of a new category.
152                  Overread all line upto this silently.  */
153               ignore_to_eol (0, 0);
154             }
155           else if ((str = is_locale_name (cat_no, ptr, len)) != NULL)
156             /* Yes the name really names an existing locale file.  We are
157                returned the complete file name.  Store it so that we can
158                copy it in the output phase.  */
159             {
160               category[cat_no].copy_locale = str;
161               category[cat_no].filled = 1;
162               
163               ignore_to_eol (0, 1);
164             }
165           else
166             /* No, the name does not address a valid locale file.  Mark
167                error case and ignore rest of category.  */
168             {
169               char tmp[len + 1];
170               memcpy (tmp, ptr, len);
171               tmp[len] = '\0';
172               error (0, 0, gettext ("%s:%Zd: invalid locale `%s' in copy "
173                                     "statement"), locfile_data.filename,
174                          locfile_data.line_no, tmp);
175               correct_token = 0;
176               ignore_to_eol (0, 0);
177             }
178
179           /* This should END as the next token.  */
180           token = xlocfile_lex (&ptr, &len);
181
182           if (token == TOK_END)
183             /* This is the end of the category.  */
184             {
185               token = xlocfile_lex (&ptr, &len);
186
187               if (token != category[cat_no].cat_id)
188                 /* Wrong category name after END.  */
189                 {
190                   error (0, 0, gettext ("%s:%Zd: category `%s' does not "
191                                         "end with `END %s'"),
192                          locfile_data.filename, locfile_data.line_no,
193                          category[cat_no].name, category[cat_no].name);
194                   ignore_to_eol (0, 0);
195                 }
196               else
197                 ignore_to_eol (0, 1);
198
199               correct_token = 1;
200             }
201           else
202             /* No END following copy.  Give error while not in error case.  */
203             {
204               if (correct_token != 0)
205                 error (0, 0, gettext ("%s:%Zd: `copy' must be sole rule"),
206                        locfile_data.filename, locfile_data.line_no);
207               correct_token = 0;
208               ignore_to_eol (0, 0);
209             }
210
211           continue;
212         }
213
214       /* Now it's time to mark as mentioned in the locale file.  */
215       category[cat_no].filled = 1;
216
217       if (category[cat_no].infct != NULL)
218         /* The category needs a special input handling.  */
219         {
220           category[cat_no].infct(token);
221           continue;
222         }
223
224       /* Now process the given items.  */
225       while (1)
226         {
227           int item_no;
228
229           if (token == TOK_END)
230             /* This is the end of the category.  */
231             {
232               token = xlocfile_lex (&ptr, &len);
233
234               if (token != category[cat_no].cat_id)
235                 {
236                   error (0, 0, gettext ("%s:%Zd: category `%s' does not end "
237                                         "with `END %s'"),
238                          locfile_data.filename, locfile_data.line_no,
239                          category[cat_no].name, category[cat_no].name);
240                   ignore_to_eol (0, 0);
241                 }
242               else
243                 ignore_to_eol (0, 1);
244
245               /* Start next category.  */
246               break;
247             }
248
249           /* All other lines should describe valid items of the category.  */
250           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
251             if (category[cat_no].item_desc[item_no].item_id == token)
252               break;
253
254           if (item_no >= category[cat_no].number)
255             /* This is not a valid item of the category.  */
256             {
257               SYNTAX_ERROR;
258               ignore_to_eol (0, 0);
259
260               token = xlocfile_lex (&ptr, &len);
261
262               /* And process next item.  */
263               continue;
264             }
265
266           /* Test whether already a value is defined.  */
267           if (category[cat_no].item_value[item_no] != NULL)
268             error (0, 0, gettext ("%s:%Zd: category item `%s' already "
269                                   "defined"),
270                    locfile_data.filename, locfile_data.line_no,
271                    category[cat_no].item_desc[item_no].name);
272
273           switch (category[cat_no].item_desc[item_no].value_type)
274             {
275             case string:
276               /* Get next token.  This is the argument to the item.  */
277               token = xlocfile_lex (&ptr, &len);
278
279               if (token != TOK_STRING)
280                 SYNTAX_ERROR;
281               else
282                 category[cat_no].item_value[item_no] = strdup (ptr);
283               ignore_to_eol (0, ptr != NULL);
284               break;
285             case stringarray:
286               /* This is a difficult case.  The number of strings in
287                  the array may vary.  But for now its only necessary
288                  with ALT_DIGITS from LC_TIME.  This item is the last
289                  so be can solve it by storing the number of string in
290                  the first place and the string indeces following
291                  that.  */
292               {
293                 int cnt;
294                 char **buffer;
295                 if (category[cat_no].item_value[item_no] != NULL)
296                   buffer = (char **) category[cat_no].item_value[item_no];
297                 else
298                   buffer = (char **) xmalloc (
299                     sizeof (char *) * category[cat_no].item_desc[item_no].max);
300
301                 category[cat_no].item_value[item_no] = (char *) buffer;
302
303                 /* As explained we may need a place to store the real number
304                    of strings.  */
305                 if (category[cat_no].item_desc[item_no].min
306                     != category[cat_no].item_desc[item_no].max)
307                   ++buffer;
308
309                 cnt = 0;
310                 do
311                   {
312                     token = xlocfile_lex (&ptr, &len);
313                     if (token != TOK_STRING)
314                       {
315                         SYNTAX_ERROR;
316                         break;
317                       }
318
319                     if (cnt >= category[cat_no].item_desc[item_no].max)
320                       {
321                         error (0, 0, gettext ("%s:%Zd: too many elements "
322                                               "for item `%s`"),
323                                locfile_data.filename, locfile_data.line_no,
324                                category[cat_no].item_desc[item_no].name);
325                         break;
326                       }
327
328                     buffer[cnt++] = strdup (ptr);
329
330                     token = locfile_lex (&ptr, &len);
331                   }
332                 while (token == TOK_CHAR && len == ';');
333
334                 ignore_to_eol (token, ptr != NULL);
335
336                 if (cnt < category[cat_no].item_desc[item_no].min)
337                   error (0, 0, gettext ("%s:%Zd: too few elements for item "
338                                         "`%s'"),
339                          locfile_data.filename, locfile_data.line_no,
340                          category[cat_no].item_desc[item_no].name);
341
342                 if (category[cat_no].item_desc[item_no].min
343                     != category[cat_no].item_desc[item_no].max)
344                   *(int *) category[cat_no].item_value[item_no] = cnt;
345               }
346               break;
347             case byte:
348               {
349                 int ok;
350                 category[cat_no].item_value[item_no] = (char *) xmalloc (
351                   __alignof__ (char));
352                 ok = get_byte (category[cat_no].item_value[item_no]);
353                 ignore_to_eol (0, ok);
354               }
355               break;
356             case bytearray:
357               {
358                 char *buffer;
359                 int maxsize;
360                 int cnt;
361                 char byte;
362                 int ok;
363
364                 buffer = (char *) xmalloc ((maxsize = 30));
365                 cnt = 0;
366
367                 while ((ok = get_byte (&byte)))
368                   {
369                     if (cnt >= maxsize)
370                       buffer = (char *) xmalloc ((maxsize *= 2));
371
372                     buffer[cnt++] = byte;
373
374                     token = locfile_lex (&ptr, &len);
375                     if (token != TOK_CHAR || len != ';')
376                       break;
377                   }
378
379                 buffer[cnt] = '\0';
380                 category[cat_no].item_value[item_no] = buffer;
381                 ignore_to_eol (token, ok);
382               }
383               break;
384             default:
385               error (5, 0, gettext ("internal error in %s, line %u"),
386                      __FUNCTION__, __LINE__);
387               /* NOTREACHED */
388             }
389
390           /* Get next token.  */
391           token = xlocfile_lex (&ptr, &len);
392         } /* while (1) */
393     }
394 }
395
396
397 /* Check given values for categories for consistency.  */
398 void
399 categories_check (void)
400 {
401   int cat_no;
402
403   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
404     if (category[cat_no].copy_locale == NULL)
405       if (category[cat_no].filled != 0)
406         if (category[cat_no].checkfct)
407           category[cat_no].checkfct();
408         else
409           {
410             int item_no;
411
412             for (item_no = 0; item_no < category[cat_no].number; ++item_no)
413               if (category[cat_no].item_value[item_no] == NULL)
414                 {
415                   int errcode;
416
417                   /* If the item is defined in the standard is it an error to
418                      have it not defined.  */
419                   errcode = category[cat_no].item_desc[item_no].status == std
420                             ? 5 : 0;
421
422                   error (errcode, 0, gettext ("item `%s' of category `%s' "
423                                               "undefined"),
424                          category[cat_no].item_desc[item_no].name,
425                          category[cat_no].name);
426                 }
427           }
428       else
429         error (0, 0, gettext ("category `%s' not defined"),
430                category[cat_no].name);
431 }
432
433
434 /* Write out the binary representation of the category data which can be
435    loaded by setlocale(1).  */
436 void
437 categories_write (void)
438 {
439   struct locale_file
440   {
441       int magic;
442       int n;
443       int idx[0];
444   } *data;
445   struct obstack obstk;
446   int cat_no;
447
448 #define obstack_chunk_alloc xmalloc
449 #define obstack_chunk_free free
450   obstack_init (&obstk);
451
452   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
453     {
454       int result = 0;
455
456       if (category[cat_no].copy_locale != NULL)
457         /* Simply copy the addressed locale file of the specified
458            category.  Please note that this is tried before the distinction
459            between categories which need special handling is made.  */
460         {
461           int source;
462
463           /* Open source file.  */
464           source = open (category[cat_no].copy_locale, O_RDONLY);
465           if (source < 0)
466             error (0, 0, gettext ("cannot copy locale definition file `%s'"),
467                    category[cat_no].copy_locale);
468           else
469             {
470               /* Construct file name of output file and open for writing.  */
471               char path[strlen (output_path)
472                         + strlen(category[cat_no].name) + 1];
473               int dest;
474               char *t;
475
476               t = stpcpy (path, output_path);
477               strcpy (t, category[cat_no].name);
478
479               dest = creat (path, 0666);
480               if (dest == -1)
481                 error (0, 0, gettext ("cannot open output file `%s': %m"),
482                        path);
483               else
484                 {
485                   char buffer[BUFSIZ];
486                   int size;
487                   
488                   /* Copy the files.  */
489                   do
490                     {
491                       size = read (source, buffer, BUFSIZ);
492                       write (dest, buffer, size);
493                     }
494                   while (size > 0);
495
496                   close (dest);
497
498                   /* Show success.  */
499                   puts (category[cat_no].name);
500                 }
501               close (source);
502             }
503
504           /* Next category.   */
505           continue;
506         }
507
508       if (category[cat_no].outfct)
509         result = category[cat_no].outfct();
510       else
511         {
512           char *path, *t;
513           int fd;
514           struct iovec *iov;
515           int item_no, len, slen, cnt;
516           int elems = 0;
517
518           /* Count number of elements.  */
519           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
520             {
521               switch (category[cat_no].item_desc[item_no].value_type)
522                 {
523                 case string:
524                 case byte:
525                 case bytearray:
526                   ++elems;
527                   break;
528                 case stringarray:
529                   elems += category[cat_no].item_desc[item_no].max;
530                   break;
531                 default:
532                   error (5, 0, gettext ("internal error in %s, line %u"),
533                          __FUNCTION__, __LINE__);
534                   /* NOTREACHED */
535                 }
536             }
537
538           /* We now have the number of elements.  We build the structure
539              and a helper structure for writing all out.  */
540           len = sizeof (struct locale_file) + elems * sizeof (int);
541           data = obstack_alloc (&obstk, len);
542           iov = obstack_alloc (&obstk, (elems + 1) * sizeof (struct iovec));
543
544           data->magic = LIMAGIC (cat_no);
545           data->n = elems;
546           iov[0].iov_base = data;
547           iov[0].iov_len = len;
548
549           cnt = 0;
550           for (item_no = 0; item_no < category[cat_no].number; ++item_no)
551             if (category[cat_no].item_value[item_no] == NULL)
552               {
553                 switch (category[cat_no].item_desc[item_no].value_type)
554                   {
555                   case string:
556                   case byte:
557                   case bytearray:
558                     data->idx[cnt] = len;
559                     ++len;  /* We reserve one single byte for this entry.  */
560                     iov[1 + cnt].iov_base = (char *) "";
561                     iov[1 + cnt].iov_len = 1;
562                     ++cnt;
563                     break;
564                   case stringarray:
565                     {
566                       int max;
567                       int nstr;
568
569                       max = category[cat_no].item_desc[item_no].max;
570
571                       for (nstr = 0; nstr < max; ++nstr)
572                         {
573                           data->idx[cnt] = len;
574                           ++len;
575                           iov[1 + cnt].iov_base = (char *) "";
576                           iov[1 + cnt].iov_len = 1;
577                           ++cnt;
578                         }
579                     }
580                   }
581               }
582             else
583               switch (category[cat_no].item_desc[item_no].value_type)
584                 {
585                 case string:
586                 case bytearray:
587                   data->idx[cnt] = len;
588                   slen = strlen (category[cat_no].item_value[item_no]) + 1;
589                   len += slen;
590                   iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
591                   iov[1 + cnt].iov_len = slen;
592                   ++cnt;
593                   break;
594                 case byte:
595                   data->idx[cnt] = len;
596                   slen = 1;
597                   len += slen;
598                   iov[1 + cnt].iov_base = category[cat_no].item_value[item_no];
599                   iov[1 + cnt].iov_len = slen;
600                   ++cnt;
601                   break;
602                 case stringarray:
603                   {
604                     int nstr, nact;
605                     char **first;
606                   
607                     if (category[cat_no].item_desc[item_no].min
608                         == category[cat_no].item_desc[item_no].max)
609                       {
610                         nstr = category[cat_no].item_desc[item_no].min;
611                         first = (char **) category[cat_no].item_value[item_no];
612                       }
613                     else
614                       {
615                         nstr = *(int *) category[cat_no].item_value[item_no];
616                         first =
617                           ((char **) category[cat_no].item_value[item_no]) + 1;
618                       }
619                     nact = nstr;
620                     while (nstr > 0)
621                       {
622                         data->idx[cnt] = len;
623                         if (*first != NULL)
624                           {
625                             slen = strlen (*first) + 1;
626                             iov[1 + cnt].iov_base = *first;
627                           }
628                         else
629                           {
630                             slen = 1;
631                             iov[1 + cnt].iov_base = (char *) "";
632                           }
633                         len += slen;
634                         iov[1 + cnt].iov_len = slen;
635                         ++cnt;
636                         ++first;
637                         --nstr;
638                       }
639                     while (nact < category[cat_no].item_desc[item_no].max)
640                       {
641                         data->idx[cnt] = len;
642                         len += 1;
643                         iov[1 + cnt].iov_base = (char *) "";
644                         iov[1 + cnt].iov_len = 1;
645                         ++cnt;
646                         ++nact;
647                       }
648                   }
649                   break;
650                 default:
651                   /* Cannot happen.  */
652                   break;
653                 }
654           assert (cnt <= elems);
655
656           /* Construct the output filename from the argument given to
657              localedef on the command line.  */
658           path = (char *) obstack_alloc (&obstk, strlen (output_path) +
659                                          2 * strlen (category[cat_no].name) + 5);
660           t = stpcpy (path, output_path);
661           strcpy (t, category[cat_no].name);
662
663           fd = creat (path, 0666);
664
665           if (fd == -1)
666             {
667               /* Check whether it failed because the named file is a directory.
668                  In that case we use the file .../LC_xxx/SYS_LC_xxx, as the
669                  loading functions of the C Library do.  */
670               struct stat st;
671
672               if (stat (path, &st) == 0 && S_ISDIR (st.st_mode))
673                 {
674                   stpcpy (stpcpy (strchr (path, '\0'), "/SYS_"),
675                           category[cat_no].name);
676                   fd = creat (path, 0666);
677                 }
678             }
679
680           if (fd == -1)
681             {
682               error (0, 0, gettext ("cannot open output file `%s': %m"),
683                      path);
684               result = 1;
685             }
686           else
687             {
688               if (writev (fd, iov, cnt + 1) == -1)
689                 {
690                   error (0, 0, gettext ("cannot write output file `%s': %m"),
691                          path);
692                   result = 1;
693                 }
694
695 if (elems==0) write(fd, &elems, 10);
696
697               close (fd);
698             }
699           /* The old data is not needed anymore, but keep the obstack
700              intact.  */
701           obstack_free (&obstk, data);
702         }
703
704       if (result == 0)
705         puts (category[cat_no].name);
706     }
707   /* Now the whole obstack can be removed.  */
708   obstack_free (&obstk, NULL);
709 }
710
711
712 /* Get the representation of a number.  This is a positive integer or
713    the number -1 which is handled as a special symbol by the scanner.  */
714 static int
715 get_byte (char *byte_ptr)
716 {
717   int token;
718   char *ptr;
719   int len;
720
721   token = locfile_lex (&ptr, &len);
722   if (token != TOK_NUMBER && token != TOK_MINUS1)
723     /* None of the valid number format.  */
724     {
725       error (0, 0, gettext ("%s:%Zd: number expected"),
726              locfile_data.filename, locfile_data.line_no);
727       *byte_ptr = 0;
728       return 0;
729     }
730
731   if (token == TOK_MINUS1)
732     {
733       *byte_ptr = CHAR_MAX;
734       return 1;
735     }
736
737   if (len > CHAR_MAX)
738     /* The value of the numbers has to be less than CHAR_MAX.  This is
739        ok for the information they have to express.  */
740     {
741       error (0, 0, gettext ("%s:%Zd: invalid number"),
742              locfile_data.filename, locfile_data.line_no);
743       *byte_ptr = 0;
744       return 0;
745     }
746
747   *byte_ptr = len;
748   return 1;
749 }
750
751
752 /* Test whether the string STR with length LEN is the name of an existing
753    locale and whether a file for category CAT_NO is found in this directory.
754    This categories are looked for in the system locale definition file
755    directory.
756    Return the complete file name for the category file.  */
757 static char *
758 is_locale_name (int cat_no, const char *str, int len)
759 {
760   static char **locale_names = NULL;
761   static int max_count = 0;
762   static int locale_count = 0;
763   int cnt, exist, fd;
764   char *fname;
765   struct stat st;
766
767   if (locale_names == NULL)
768     /* Read in the list of all available locales.  */
769     {
770       DIR *dir;
771       struct dirent *dirent;
772       
773       /* LOCALE_NAMES is not NULL anymore, but LOCALE_COUNT == 0.  */
774       ++locale_names;
775           
776       dir = opendir (LOCALE_PATH);
777       if (dir == NULL)
778         {
779           error (1, errno, gettext ("cannot read locale directory `%s'"),
780                  LOCALE_PATH);
781
782           return NULL;
783         }
784
785       /* Now we can look for all files in the directory.  */
786       while ((dirent = readdir (dir)) != NULL)
787         if (strcmp (dirent->d_name, ".") != 0
788             && strcmp (dirent->d_name, "..") != 0)
789           {
790             if (max_count == 0)
791               locale_names = (char **) xmalloc ((max_count = 10)
792                                                 * sizeof (char *));
793             else if (locale_count >= max_count)
794               locale_names = (char **) xrealloc (locale_names,
795                                                  (max_count *= 2)
796                                                  * sizeof (char *));
797             locale_names[locale_count++] = strdup (dirent->d_name);
798           }
799       closedir (dir);
800     }
801
802   for (cnt = 0; cnt < locale_count; ++cnt)
803     if (strncmp (str, locale_names[cnt], len) == 0
804         && locale_names[cnt][len] == '\0')
805       break;
806   
807   if (cnt >= locale_count)
808     return NULL;
809   
810   /* Now search for this specific locale file.  */
811   asprintf (&fname, "%s/%s/%s", LOCALE_PATH, locale_names[cnt],
812             category[cat_no].name);
813   
814   fd = open (fname, O_RDONLY);
815   if (fd < 0)
816     {
817       free (fname);
818       return NULL;
819     }
820   
821   exist = fstat (fd, &st);
822   close (fd);
823   
824   if (exist < 0)
825     {
826       free (fname);
827       return NULL;
828     }
829
830   return fname;
831 }
832
833 /*
834  * Local Variables:
835  *  mode:c
836  *  c-basic-offset:2
837  * End:
838  */