Internationalization of tinc.
[tinc] / intl / localealias.c
1 /* Handle aliases for locale names.
2    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
3    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <sys/types.h>
26
27 #ifdef __GNUC__
28 # define alloca __builtin_alloca
29 # define HAVE_ALLOCA 1
30 #else
31 # if defined HAVE_ALLOCA_H || defined _LIBC
32 #  include <alloca.h>
33 # else
34 #  ifdef _AIX
35  #pragma alloca
36 #  else
37 #   ifndef alloca
38 char *alloca ();
39 #   endif
40 #  endif
41 # endif
42 #endif
43
44 #if defined STDC_HEADERS || defined _LIBC
45 # include <stdlib.h>
46 #else
47 char *getenv ();
48 # ifdef HAVE_MALLOC_H
49 #  include <malloc.h>
50 # else
51 void free ();
52 # endif
53 #endif
54
55 #if defined HAVE_STRING_H || defined _LIBC
56 # ifndef _GNU_SOURCE
57 #  define _GNU_SOURCE   1
58 # endif
59 # include <string.h>
60 #else
61 # include <strings.h>
62 # ifndef memcpy
63 #  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
64 # endif
65 #endif
66 #if !HAVE_STRCHR && !defined _LIBC
67 # ifndef strchr
68 #  define strchr index
69 # endif
70 #endif
71
72 #include "gettext.h"
73 #include "gettextP.h"
74
75 /* @@ end of prolog @@ */
76
77 #ifdef _LIBC
78 /* Rename the non ANSI C functions.  This is required by the standard
79    because some ANSI C functions will require linking with this object
80    file and the name space must not be polluted.  */
81 # define strcasecmp __strcasecmp
82
83 # ifndef mempcpy
84 #  define mempcpy __mempcpy
85 # endif
86 # define HAVE_MEMPCPY   1
87
88 /* We need locking here since we can be called from different places.  */
89 # include <bits/libc-lock.h>
90
91 __libc_lock_define_initialized (static, lock);
92 #endif
93
94 #ifndef internal_function
95 # define internal_function
96 #endif
97
98 /* For those loosing systems which don't have `alloca' we have to add
99    some additional code emulating it.  */
100 #ifdef HAVE_ALLOCA
101 /* Nothing has to be done.  */
102 # define ADD_BLOCK(list, address) /* nothing */
103 # define FREE_BLOCKS(list) /* nothing */
104 #else
105 struct block_list
106 {
107   void *address;
108   struct block_list *next;
109 };
110 # define ADD_BLOCK(list, addr)                                                \
111   do {                                                                        \
112     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
113     /* If we cannot get a free block we cannot add the new element to         \
114        the list.  */                                                          \
115     if (newp != NULL) {                                                       \
116       newp->address = (addr);                                                 \
117       newp->next = (list);                                                    \
118       (list) = newp;                                                          \
119     }                                                                         \
120   } while (0)
121 # define FREE_BLOCKS(list)                                                    \
122   do {                                                                        \
123     while (list != NULL) {                                                    \
124       struct block_list *old = list;                                          \
125       list = list->next;                                                      \
126       free (old);                                                             \
127     }                                                                         \
128   } while (0)
129 # undef alloca
130 # define alloca(size) (malloc (size))
131 #endif  /* have alloca */
132
133 #if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
134 # undef fgets
135 # define fgets(buf, len, s) fgets_unlocked (buf, len, s)
136 #endif
137 #if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
138 # undef feof
139 # define feof(s) feof_unlocked (s)
140 #endif
141
142
143 struct alias_map
144 {
145   const char *alias;
146   const char *value;
147 };
148
149
150 static char *string_space = NULL;
151 static size_t string_space_act = 0;
152 static size_t string_space_max = 0;
153 static struct alias_map *map;
154 static size_t nmap = 0;
155 static size_t maxmap = 0;
156
157
158 /* Prototypes for local functions.  */
159 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
160      internal_function;
161 static void extend_alias_table PARAMS ((void));
162 static int alias_compare PARAMS ((const struct alias_map *map1,
163                                   const struct alias_map *map2));
164
165
166 const char *
167 _nl_expand_alias (name)
168     const char *name;
169 {
170   static const char *locale_alias_path = LOCALE_ALIAS_PATH;
171   struct alias_map *retval;
172   const char *result = NULL;
173   size_t added;
174
175 #ifdef _LIBC
176   __libc_lock_lock (lock);
177 #endif
178
179   do
180     {
181       struct alias_map item;
182
183       item.alias = name;
184
185       if (nmap > 0)
186         retval = (struct alias_map *) bsearch (&item, map, nmap,
187                                                sizeof (struct alias_map),
188                                                (int (*) PARAMS ((const void *,
189                                                                  const void *))
190                                                 ) alias_compare);
191       else
192         retval = NULL;
193
194       /* We really found an alias.  Return the value.  */
195       if (retval != NULL)
196         {
197           result = retval->value;
198           break;
199         }
200
201       /* Perhaps we can find another alias file.  */
202       added = 0;
203       while (added == 0 && locale_alias_path[0] != '\0')
204         {
205           const char *start;
206
207           while (locale_alias_path[0] == ':')
208             ++locale_alias_path;
209           start = locale_alias_path;
210
211           while (locale_alias_path[0] != '\0' && locale_alias_path[0] != ':')
212             ++locale_alias_path;
213
214           if (start < locale_alias_path)
215             added = read_alias_file (start, locale_alias_path - start);
216         }
217     }
218   while (added != 0);
219
220 #ifdef _LIBC
221   __libc_lock_unlock (lock);
222 #endif
223
224   return result;
225 }
226
227
228 static size_t
229 internal_function
230 read_alias_file (fname, fname_len)
231      const char *fname;
232      int fname_len;
233 {
234 #ifndef HAVE_ALLOCA
235   struct block_list *block_list = NULL;
236 #endif
237   FILE *fp;
238   char *full_fname;
239   size_t added;
240   static const char aliasfile[] = "/locale.alias";
241
242   full_fname = (char *) alloca (fname_len + sizeof aliasfile);
243   ADD_BLOCK (block_list, full_fname);
244 #ifdef HAVE_MEMPCPY
245   mempcpy (mempcpy (full_fname, fname, fname_len),
246            aliasfile, sizeof aliasfile);
247 #else
248   memcpy (full_fname, fname, fname_len);
249   memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
250 #endif
251
252   fp = fopen (full_fname, "r");
253   if (fp == NULL)
254     {
255       FREE_BLOCKS (block_list);
256       return 0;
257     }
258
259   added = 0;
260   while (!feof (fp))
261     {
262       /* It is a reasonable approach to use a fix buffer here because
263          a) we are only interested in the first two fields
264          b) these fields must be usable as file names and so must not
265             be that long
266        */
267       char buf[BUFSIZ];
268       char *alias;
269       char *value;
270       char *cp;
271
272       if (fgets (buf, sizeof buf, fp) == NULL)
273         /* EOF reached.  */
274         break;
275
276       /* Possibly not the whole line fits into the buffer.  Ignore
277          the rest of the line.  */
278       if (strchr (buf, '\n') == NULL)
279         {
280           char altbuf[BUFSIZ];
281           do
282             if (fgets (altbuf, sizeof altbuf, fp) == NULL)
283               /* Make sure the inner loop will be left.  The outer loop
284                  will exit at the `feof' test.  */
285               break;
286           while (strchr (altbuf, '\n') == NULL);
287         }
288
289       cp = buf;
290       /* Ignore leading white space.  */
291       while (isspace (cp[0]))
292         ++cp;
293
294       /* A leading '#' signals a comment line.  */
295       if (cp[0] != '\0' && cp[0] != '#')
296         {
297           alias = cp++;
298           while (cp[0] != '\0' && !isspace (cp[0]))
299             ++cp;
300           /* Terminate alias name.  */
301           if (cp[0] != '\0')
302             *cp++ = '\0';
303
304           /* Now look for the beginning of the value.  */
305           while (isspace (cp[0]))
306             ++cp;
307
308           if (cp[0] != '\0')
309             {
310               size_t alias_len;
311               size_t value_len;
312
313               value = cp++;
314               while (cp[0] != '\0' && !isspace (cp[0]))
315                 ++cp;
316               /* Terminate value.  */
317               if (cp[0] == '\n')
318                 {
319                   /* This has to be done to make the following test
320                      for the end of line possible.  We are looking for
321                      the terminating '\n' which do not overwrite here.  */
322                   *cp++ = '\0';
323                   *cp = '\n';
324                 }
325               else if (cp[0] != '\0')
326                 *cp++ = '\0';
327
328               if (nmap >= maxmap)
329                 extend_alias_table ();
330
331               alias_len = strlen (alias) + 1;
332               value_len = strlen (value) + 1;
333
334               if (string_space_act + alias_len + value_len > string_space_max)
335                 {
336                   /* Increase size of memory pool.  */
337                   size_t new_size = (string_space_max
338                                      + (alias_len + value_len > 1024
339                                         ? alias_len + value_len : 1024));
340                   char *new_pool = (char *) realloc (string_space, new_size);
341                   if (new_pool == NULL)
342                     {
343                       FREE_BLOCKS (block_list);
344                       return added;
345                     }
346                   string_space = new_pool;
347                   string_space_max = new_size;
348                 }
349
350               map[nmap].alias = memcpy (&string_space[string_space_act],
351                                         alias, alias_len);
352               string_space_act += alias_len;
353
354               map[nmap].value = memcpy (&string_space[string_space_act],
355                                         value, value_len);
356               string_space_act += value_len;
357
358               ++nmap;
359               ++added;
360             }
361         }
362     }
363
364   /* Should we test for ferror()?  I think we have to silently ignore
365      errors.  --drepper  */
366   fclose (fp);
367
368   if (added > 0)
369     qsort (map, nmap, sizeof (struct alias_map),
370            (int (*) PARAMS ((const void *, const void *))) alias_compare);
371
372   FREE_BLOCKS (block_list);
373   return added;
374 }
375
376
377 static void
378 extend_alias_table ()
379 {
380   size_t new_size;
381   struct alias_map *new_map;
382
383   new_size = maxmap == 0 ? 100 : 2 * maxmap;
384   new_map = (struct alias_map *) realloc (map, (new_size
385                                                 * sizeof (struct alias_map)));
386   if (new_map == NULL)
387     /* Simply don't extend: we don't have any more core.  */
388     return;
389
390   map = new_map;
391   maxmap = new_size;
392 }
393
394
395 #ifdef _LIBC
396 static void __attribute__ ((unused))
397 free_mem (void)
398 {
399   if (string_space != NULL)
400     free (string_space);
401   if (map != NULL)
402     free (map);
403 }
404 text_set_element (__libc_subfreeres, free_mem);
405 #endif
406
407
408 static int
409 alias_compare (map1, map2)
410      const struct alias_map *map1;
411      const struct alias_map *map2;
412 {
413 #if defined _LIBC || defined HAVE_STRCASECMP
414   return strcasecmp (map1->alias, map2->alias);
415 #else
416   const unsigned char *p1 = (const unsigned char *) map1->alias;
417   const unsigned char *p2 = (const unsigned char *) map2->alias;
418   unsigned char c1, c2;
419
420   if (p1 == p2)
421     return 0;
422
423   do
424     {
425       /* I know this seems to be odd but the tolower() function in
426          some systems libc cannot handle nonalpha characters.  */
427       c1 = isupper (*p1) ? tolower (*p1) : *p1;
428       c2 = isupper (*p2) ? tolower (*p2) : *p2;
429       if (c1 == '\0')
430         break;
431       ++p1;
432       ++p2;
433     }
434   while (c1 == c2);
435
436   return c1 - c2;
437 #endif
438 }