(file) Return to dcgettext.c CVS log (file) (dir) Up to [RizwankCVS] / testProject / intl

  1 rizwank 1.1 /* Implementation of the dcgettext(3) function.
  2                Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
  3             
  4                This program is free software; you can redistribute it and/or modify
  5                it under the terms of the GNU General Public License as published by
  6                the Free Software Foundation; either version 2, or (at your option)
  7                any later version.
  8             
  9                This program is distributed in the hope that it will be useful,
 10                but WITHOUT ANY WARRANTY; without even the implied warranty of
 11                MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12                GNU General Public License for more details.
 13             
 14                You should have received a copy of the GNU General Public License
 15                along with this program; if not, write to the Free Software Foundation,
 16                Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 17             
 18             #ifdef HAVE_CONFIG_H
 19             # include <config.h>
 20             #endif
 21             
 22 rizwank 1.1 #include <sys/types.h>
 23             
 24             #ifdef __GNUC__
 25             # define alloca __builtin_alloca
 26             # define HAVE_ALLOCA 1
 27             #else
 28             # if defined HAVE_ALLOCA_H || defined _LIBC
 29             #  include <alloca.h>
 30             # else
 31             #  ifdef _AIX
 32              #pragma alloca
 33             #  else
 34             #   ifndef alloca
 35             char *alloca ();
 36             #   endif
 37             #  endif
 38             # endif
 39             #endif
 40             
 41             #include <errno.h>
 42             #ifndef errno
 43 rizwank 1.1 extern int errno;
 44             #endif
 45             #ifndef __set_errno
 46             # define __set_errno(val) errno = (val)
 47             #endif
 48             
 49             #if defined STDC_HEADERS || defined _LIBC
 50             # include <stdlib.h>
 51             #else
 52             char *getenv ();
 53             # ifdef HAVE_MALLOC_H
 54             #  include <malloc.h>
 55             # else
 56             void free ();
 57             # endif
 58             #endif
 59             
 60             #if defined HAVE_STRING_H || defined _LIBC
 61             # ifndef _GNU_SOURCE
 62             #  define _GNU_SOURCE	1
 63             # endif
 64 rizwank 1.1 # include <string.h>
 65             #else
 66             # include <strings.h>
 67             #endif
 68             #if !HAVE_STRCHR && !defined _LIBC
 69             # ifndef strchr
 70             #  define strchr index
 71             # endif
 72             #endif
 73             
 74             #if defined HAVE_UNISTD_H || defined _LIBC
 75             # include <unistd.h>
 76             #endif
 77             
 78             #include "gettext.h"
 79             #include "gettextP.h"
 80             #ifdef _LIBC
 81             # include <libintl.h>
 82             #else
 83             # include "libgettext.h"
 84             #endif
 85 rizwank 1.1 #include "hash-string.h"
 86             
 87             /* @@ end of prolog @@ */
 88             
 89             #ifdef _LIBC
 90             /* Rename the non ANSI C functions.  This is required by the standard
 91                because some ANSI C functions will require linking with this object
 92                file and the name space must not be polluted.  */
 93             # define getcwd __getcwd
 94             # ifndef stpcpy
 95             #  define stpcpy __stpcpy
 96             # endif
 97             #else
 98             # if !defined HAVE_GETCWD
 99             char *getwd ();
100             #  define getcwd(buf, max) getwd (buf)
101             # else
102             char *getcwd ();
103             # endif
104             # ifndef HAVE_STPCPY
105             static char *stpcpy PARAMS ((char *dest, const char *src));
106 rizwank 1.1 # endif
107             #endif
108             
109             /* Amount to increase buffer size by in each try.  */
110             #define PATH_INCR 32
111             
112             /* The following is from pathmax.h.  */
113             /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
114                PATH_MAX but might cause redefinition warnings when sys/param.h is
115                later included (as on MORE/BSD 4.3).  */
116             #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
117             # include <limits.h>
118             #endif
119             
120             #ifndef _POSIX_PATH_MAX
121             # define _POSIX_PATH_MAX 255
122             #endif
123             
124             #if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
125             # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
126             #endif
127 rizwank 1.1 
128             /* Don't include sys/param.h if it already has been.  */
129             #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
130             # include <sys/param.h>
131             #endif
132             
133             #if !defined(PATH_MAX) && defined(MAXPATHLEN)
134             # define PATH_MAX MAXPATHLEN
135             #endif
136             
137             #ifndef PATH_MAX
138             # define PATH_MAX _POSIX_PATH_MAX
139             #endif
140             
141             /* XPG3 defines the result of `setlocale (category, NULL)' as:
142                ``Directs `setlocale()' to query `category' and return the current
143                  setting of `local'.''
144                However it does not specify the exact format.  And even worse: POSIX
145                defines this not at all.  So we can use this feature only on selected
146                system (e.g. those using GNU C Library).  */
147             #ifdef _LIBC
148 rizwank 1.1 # define HAVE_LOCALE_NULL
149             #endif
150             
151             /* Name of the default domain used for gettext(3) prior any call to
152                textdomain(3).  The default value for this is "messages".  */
153             const char _nl_default_default_domain[] = "messages";
154             
155             /* Value used as the default domain for gettext(3).  */
156             const char *_nl_current_default_domain = _nl_default_default_domain;
157             
158             /* Contains the default location of the message catalogs.  */
159             const char _nl_default_dirname[] = GNULOCALEDIR;
160             
161             /* List with bindings of specific domains created by bindtextdomain()
162                calls.  */
163             struct binding *_nl_domain_bindings;
164             
165             /* Prototypes for local functions.  */
166             static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file,
167             			       const char *msgid)) internal_function;
168             static const char *category_to_name PARAMS ((int category)) internal_function;
169 rizwank 1.1 static const char *guess_category_value PARAMS ((int category,
170             						 const char *categoryname))
171                  internal_function;
172             
173             
174             /* For those loosing systems which don't have `alloca' we have to add
175                some additional code emulating it.  */
176             #ifdef HAVE_ALLOCA
177             /* Nothing has to be done.  */
178             # define ADD_BLOCK(list, address) /* nothing */
179             # define FREE_BLOCKS(list) /* nothing */
180             #else
181             struct block_list
182             {
183               void *address;
184               struct block_list *next;
185             };
186             # define ADD_BLOCK(list, addr)						      \
187               do {									      \
188                 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
189                 /* If we cannot get a free block we cannot add the new element to	      \
190 rizwank 1.1        the list.  */							      \
191                 if (newp != NULL) {							      \
192                   newp->address = (addr);						      \
193                   newp->next = (list);						      \
194                   (list) = newp;							      \
195                 }									      \
196               } while (0)
197             # define FREE_BLOCKS(list)						      \
198               do {									      \
199                 while (list != NULL) {						      \
200                   struct block_list *old = list;					      \
201                   list = list->next;						      \
202                   free (old);							      \
203                 }									      \
204               } while (0)
205             # undef alloca
206             # define alloca(size) (malloc (size))
207             #endif	/* have alloca */
208             
209             
210             /* Names for the libintl functions are a problem.  They must not clash
211 rizwank 1.1    with existing names and they should follow ANSI C.  But this source
212                code is also used in GNU C Library where the names have a __
213                prefix.  So we have to make a difference here.  */
214             #ifdef _LIBC
215             # define DCGETTEXT __dcgettext
216             #else
217             # define DCGETTEXT dcgettext__
218             #endif
219             
220             /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
221                locale.  */
222             char *
223             DCGETTEXT (domainname, msgid, category)
224                  const char *domainname;
225                  const char *msgid;
226                  int category;
227             {
228             #ifndef HAVE_ALLOCA
229               struct block_list *block_list = NULL;
230             #endif
231               struct loaded_l10nfile *domain;
232 rizwank 1.1   struct binding *binding;
233               const char *categoryname;
234               const char *categoryvalue;
235               char *dirname, *xdomainname;
236               char *single_locale;
237               char *retval;
238               int saved_errno = errno;
239             
240               /* If no real MSGID is given return NULL.  */
241               if (msgid == NULL)
242                 return NULL;
243             
244               /* If DOMAINNAME is NULL, we are interested in the default domain.  If
245                  CATEGORY is not LC_MESSAGES this might not make much sense but the
246                  defintion left this undefined.  */
247               if (domainname == NULL)
248                 domainname = _nl_current_default_domain;
249             
250               /* First find matching binding.  */
251               for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
252                 {
253 rizwank 1.1       int compare = strcmp (domainname, binding->domainname);
254                   if (compare == 0)
255             	/* We found it!  */
256             	break;
257                   if (compare < 0)
258             	{
259             	  /* It is not in the list.  */
260             	  binding = NULL;
261             	  break;
262             	}
263                 }
264             
265               if (binding == NULL)
266                 dirname = (char *) _nl_default_dirname;
267               else if (binding->dirname[0] == '/')
268                 dirname = binding->dirname;
269               else
270                 {
271                   /* We have a relative path.  Make it absolute now.  */
272                   size_t dirname_len = strlen (binding->dirname) + 1;
273                   size_t path_max;
274 rizwank 1.1       char *ret;
275             
276                   path_max = (unsigned) PATH_MAX;
277                   path_max += 2;		/* The getcwd docs say to do this.  */
278             
279                   dirname = (char *) alloca (path_max + dirname_len);
280                   ADD_BLOCK (block_list, dirname);
281             
282                   __set_errno (0);
283                   while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
284             	{
285             	  path_max += PATH_INCR;
286             	  dirname = (char *) alloca (path_max + dirname_len);
287             	  ADD_BLOCK (block_list, dirname);
288             	  __set_errno (0);
289             	}
290             
291                   if (ret == NULL)
292             	{
293             	  /* We cannot get the current working directory.  Don't signal an
294             	     error but simply return the default string.  */
295 rizwank 1.1 	  FREE_BLOCKS (block_list);
296             	  __set_errno (saved_errno);
297             	  return (char *) msgid;
298             	}
299             
300                   stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
301                 }
302             
303               /* Now determine the symbolic name of CATEGORY and its value.  */
304               categoryname = category_to_name (category);
305               categoryvalue = guess_category_value (category, categoryname);
306             
307               xdomainname = (char *) alloca (strlen (categoryname)
308             				 + strlen (domainname) + 5);
309               ADD_BLOCK (block_list, xdomainname);
310             
311               stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
312             		  domainname),
313             	  ".mo");
314             
315               /* Creating working area.  */
316 rizwank 1.1   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
317               ADD_BLOCK (block_list, single_locale);
318             
319             
320               /* Search for the given string.  This is a loop because we perhaps
321                  got an ordered list of languages to consider for th translation.  */
322               while (1)
323                 {
324                   /* Make CATEGORYVALUE point to the next element of the list.  */
325                   while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
326             	++categoryvalue;
327                   if (categoryvalue[0] == '\0')
328             	{
329             	  /* The whole contents of CATEGORYVALUE has been searched but
330             	     no valid entry has been found.  We solve this situation
331             	     by implicitly appending a "C" entry, i.e. no translation
332             	     will take place.  */
333             	  single_locale[0] = 'C';
334             	  single_locale[1] = '\0';
335             	}
336                   else
337 rizwank 1.1 	{
338             	  char *cp = single_locale;
339             	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
340             	    *cp++ = *categoryvalue++;
341             	  *cp = '\0';
342             	}
343             
344                   /* If the current locale value is C (or POSIX) we don't load a
345             	 domain.  Return the MSGID.  */
346                   if (strcmp (single_locale, "C") == 0
347             	  || strcmp (single_locale, "POSIX") == 0)
348             	{
349             	  FREE_BLOCKS (block_list);
350             	  __set_errno (saved_errno);
351             	  return (char *) msgid;
352             	}
353             
354             
355                   /* Find structure describing the message catalog matching the
356             	 DOMAINNAME and CATEGORY.  */
357                   domain = _nl_find_domain (dirname, single_locale, xdomainname);
358 rizwank 1.1 
359                   if (domain != NULL)
360             	{
361             	  retval = find_msg (domain, msgid);
362             
363             	  if (retval == NULL)
364             	    {
365             	      int cnt;
366             
367             	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
368             		{
369             		  retval = find_msg (domain->successor[cnt], msgid);
370             
371             		  if (retval != NULL)
372             		    break;
373             		}
374             	    }
375             
376             	  if (retval != NULL)
377             	    {
378             	      FREE_BLOCKS (block_list);
379 rizwank 1.1 	      __set_errno (saved_errno);
380             	      return retval;
381             	    }
382             	}
383                 }
384               /* NOTREACHED */
385             }
386             
387             #ifdef _LIBC
388             /* Alias for function name in GNU C Library.  */
389             weak_alias (__dcgettext, dcgettext);
390             #endif
391             
392             
393             static char *
394             internal_function
395             find_msg (domain_file, msgid)
396                  struct loaded_l10nfile *domain_file;
397                  const char *msgid;
398             {
399               size_t top, act, bottom;
400 rizwank 1.1   struct loaded_domain *domain;
401             
402               if (domain_file->decided == 0)
403                 _nl_load_domain (domain_file);
404             
405               if (domain_file->data == NULL)
406                 return NULL;
407             
408               domain = (struct loaded_domain *) domain_file->data;
409             
410               /* Locate the MSGID and its translation.  */
411               if (domain->hash_size > 2 && domain->hash_tab != NULL)
412                 {
413                   /* Use the hashing table.  */
414                   nls_uint32 len = strlen (msgid);
415                   nls_uint32 hash_val = hash_string (msgid);
416                   nls_uint32 idx = hash_val % domain->hash_size;
417                   nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
418                   nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
419             
420                   if (nstr == 0)
421 rizwank 1.1 	/* Hash table entry is empty.  */
422             	return NULL;
423             
424                   if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
425             	  && strcmp (msgid,
426             		     domain->data + W (domain->must_swap,
427             				       domain->orig_tab[nstr - 1].offset)) == 0)
428             	return (char *) domain->data + W (domain->must_swap,
429             					  domain->trans_tab[nstr - 1].offset);
430             
431                   while (1)
432             	{
433             	  if (idx >= domain->hash_size - incr)
434             	    idx -= domain->hash_size - incr;
435             	  else
436             	    idx += incr;
437             
438             	  nstr = W (domain->must_swap, domain->hash_tab[idx]);
439             	  if (nstr == 0)
440             	    /* Hash table entry is empty.  */
441             	    return NULL;
442 rizwank 1.1 
443             	  if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
444             	      && strcmp (msgid,
445             			 domain->data + W (domain->must_swap,
446             					   domain->orig_tab[nstr - 1].offset))
447             	         == 0)
448             	    return (char *) domain->data
449             	      + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
450             	}
451                   /* NOTREACHED */
452                 }
453             
454               /* Now we try the default method:  binary search in the sorted
455                  array of messages.  */
456               bottom = 0;
457               top = domain->nstrings;
458               while (bottom < top)
459                 {
460                   int cmp_val;
461             
462                   act = (bottom + top) / 2;
463 rizwank 1.1       cmp_val = strcmp (msgid, domain->data
464             			       + W (domain->must_swap,
465             				    domain->orig_tab[act].offset));
466                   if (cmp_val < 0)
467             	top = act;
468                   else if (cmp_val > 0)
469             	bottom = act + 1;
470                   else
471             	break;
472                 }
473             
474               /* If an translation is found return this.  */
475               return bottom >= top ? NULL : (char *) domain->data
476                                             + W (domain->must_swap,
477             				     domain->trans_tab[act].offset);
478             }
479             
480             
481             /* Return string representation of locale CATEGORY.  */
482             static const char *
483             internal_function
484 rizwank 1.1 category_to_name (category)
485                  int category;
486             {
487               const char *retval;
488             
489               switch (category)
490               {
491             #ifdef LC_COLLATE
492               case LC_COLLATE:
493                 retval = "LC_COLLATE";
494                 break;
495             #endif
496             #ifdef LC_CTYPE
497               case LC_CTYPE:
498                 retval = "LC_CTYPE";
499                 break;
500             #endif
501             #ifdef LC_MONETARY
502               case LC_MONETARY:
503                 retval = "LC_MONETARY";
504                 break;
505 rizwank 1.1 #endif
506             #ifdef LC_NUMERIC
507               case LC_NUMERIC:
508                 retval = "LC_NUMERIC";
509                 break;
510             #endif
511             #ifdef LC_TIME
512               case LC_TIME:
513                 retval = "LC_TIME";
514                 break;
515             #endif
516             #ifdef LC_MESSAGES
517               case LC_MESSAGES:
518                 retval = "LC_MESSAGES";
519                 break;
520             #endif
521             #ifdef LC_RESPONSE
522               case LC_RESPONSE:
523                 retval = "LC_RESPONSE";
524                 break;
525             #endif
526 rizwank 1.1 #ifdef LC_ALL
527               case LC_ALL:
528                 /* This might not make sense but is perhaps better than any other
529                    value.  */
530                 retval = "LC_ALL";
531                 break;
532             #endif
533               default:
534                 /* If you have a better idea for a default value let me know.  */
535                 retval = "LC_XXX";
536               }
537             
538               return retval;
539             }
540             
541             /* Guess value of current locale from value of the environment variables.  */
542             static const char *
543             internal_function
544             guess_category_value (category, categoryname)
545                  int category;
546                  const char *categoryname;
547 rizwank 1.1 {
548               const char *retval;
549             
550               /* The highest priority value is the `LANGUAGE' environment
551                  variable.  This is a GNU extension.  */
552               retval = getenv ("LANGUAGE");
553               if (retval != NULL && retval[0] != '\0')
554                 return retval;
555             
556               /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
557                  methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
558                  systems this can be done by the `setlocale' function itself.  */
559             #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
560               return setlocale (category, NULL);
561             #else
562               /* Setting of LC_ALL overwrites all other.  */
563               retval = getenv ("LC_ALL");
564               if (retval != NULL && retval[0] != '\0')
565                 return retval;
566             
567               /* Next comes the name of the desired category.  */
568 rizwank 1.1   retval = getenv (categoryname);
569               if (retval != NULL && retval[0] != '\0')
570                 return retval;
571             
572               /* Last possibility is the LANG environment variable.  */
573               retval = getenv ("LANG");
574               if (retval != NULL && retval[0] != '\0')
575                 return retval;
576             
577               /* We use C as the default domain.  POSIX says this is implementation
578                  defined.  */
579               return "C";
580             #endif
581             }
582             
583             /* @@ begin of epilog @@ */
584             
585             /* We don't want libintl.a to depend on any other library.  So we
586                avoid the non-standard function stpcpy.  In GNU C Library this
587                function is available, though.  Also allow the symbol HAVE_STPCPY
588                to be defined.  */
589 rizwank 1.1 #if !_LIBC && !HAVE_STPCPY
590             static char *
591             stpcpy (dest, src)
592                  char *dest;
593                  const char *src;
594             {
595               while ((*dest++ = *src++) != '\0')
596                 /* Do nothing. */ ;
597               return dest - 1;
598             }
599             #endif
600             
601             
602             #ifdef _LIBC
603             /* If we want to free all resources we have to do some work at
604                program's end.  */
605             static void __attribute__ ((unused))
606             free_mem (void)
607             {
608               struct binding *runp;
609             
610 rizwank 1.1   for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
611                 {
612                   free (runp->domainname);
613                   if (runp->dirname != _nl_default_dirname)
614             	/* Yes, this is a pointer comparison.  */
615             	free (runp->dirname);
616                 }
617             
618               if (_nl_current_default_domain != _nl_default_default_domain)
619                 /* Yes, again a pointer comparison.  */
620                 free ((char *) _nl_current_default_domain);
621             }
622             
623             text_set_element (__libc_subfreeres, free_mem);
624             #endif

Rizwan Kassim
Powered by
ViewCVS 0.9.2