*** empty log message ***
[m17n/m17n-lib.git] / src / locale.c
1 /* locale.c -- locale module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nLocale
25     @brief Locale objects and API for them
26
27     The m17n library represents locale related information as objects
28     of type #MLocale.  */
29
30 /***ja
31     @addtogroup m17nLocale
32     @brief ¥í¥±¡¼¥ë¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API
33
34     m17n ¥é¥¤¥Ö¥é¥ê¤Ï¥í¥±¡¼¥ë´ØÏ¢¾ðÊó¤ò #MLocale ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç
35     É½¸½¤¹¤ë¡£  */
36
37 /*=*/
38
39 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
40 /*** @addtogroup m17nInternal
41      @{ */
42
43 #define _GNU_SOURCE
44
45 #include <config.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <locale.h>
50 #ifdef HAVE_LANGINFO_H
51 #include <langinfo.h>
52 #endif
53 #include <time.h>
54
55 #include "m17n.h"
56 #include "m17n-misc.h"
57 #include "internal.h"
58 #include "symbol.h"
59 #include "coding.h"
60 #include "textprop.h"
61 #include "mlocale.h"
62
63 static MSymbol M_locale;
64 static MSymbol M_xfrm;
65
66
67 /** Structure of locales.  */
68
69 struct MLocale
70 {
71   M17NObject control;
72   MSymbol name;
73   MSymbol language;
74   MSymbol territory;
75   MSymbol modifier;
76   MSymbol codeset;
77   MSymbol coding;
78 };
79
80
81 /** The current locales of each category.  */
82 MLocale *mlocale__collate, *mlocale__ctype;
83 MLocale *mlocale__messages, *mlocale__time;
84
85 /* These are currently not used.  */
86 #if 0
87 MLocale *mlocale_monetary, *mlocale_numeric, ;
88 #endif
89
90 /** Parse locale name NAME and return a newly created MLocale object.
91     If the locale is not supported by the system, return NULL.  */
92
93 static MLocale *
94 make_locale (const char *name)
95 {
96   char *current, *new, *str;
97   int len;
98   MLocale *locale;
99   char c;
100
101   str = setlocale (LC_CTYPE, NULL);
102   len = strlen (str) + 1;
103   current = alloca (len);
104   memcpy (current, str, len); 
105
106   if (! (new = setlocale (LC_CTYPE, name)))
107     return NULL;
108
109
110   M17N_OBJECT (locale, NULL, MERROR_LOCALE);
111   locale->name = msymbol (new);
112   msymbol_put (locale->name, M_locale, (void *) locale);
113   M17N_OBJECT_UNREF (locale);
114
115   len = strlen (new) + 1;
116   str = alloca (len);
117   memcpy (str, new, len);
118
119   c = '\0';
120   while (1)
121     {
122       char c1;
123       int i;
124
125       for (i = 0; str[i]; i++)
126         if (str[i] == '_' || str[i] == '.' || str[i] == '@')
127           break;
128       c1 = str[i];
129       str[i] = '\0';
130       if (c == '\0')
131         /* The first field is for language.  */
132         locale->language = msymbol (name);      
133       else if (c == '_')
134         /* The field following '_' is for territory.  */
135         locale->territory = msymbol (name);
136       else if (c == '.')
137         /* The field following '.' is for codeset.  */
138         locale->codeset = msymbol (name);
139       else
140         /* The other field is for modifier.  */
141         locale->modifier = msymbol (name);
142       if (! c1)
143         break;
144       c = c1;
145       name += i + 1;
146     }
147
148 #ifdef HAVE_NL_LANGINFO
149 #ifdef CODESET
150   /* If we can use nl_langinfo () to retrieve a codeset name, respect
151      it over the codeset name extracted from the locale name.  */
152   locale->codeset = msymbol (nl_langinfo (CODESET));
153 #endif
154 #endif
155
156   /* If the locale name specifies a codeset, get the corresponding
157      coding system.  */
158   if (locale->codeset != Mnil)
159     {
160       locale->coding = mconv_resolve_coding (locale->codeset);
161       if (locale->coding == Mnil)
162         locale->coding = Mcoding_us_ascii;
163     }
164   else
165     locale->coding = Mcoding_us_ascii;
166
167   setlocale (LC_CTYPE, current);
168   return locale;
169 }
170
171
172 /** Decode the byte sequence at BUF of length SIZE bytes by the coding
173     system associated with LOCALE, and return a generated M-text.  */
174
175 static MText *
176 decode_locale (unsigned char *buf, int size, MLocale *locale)
177 {
178   return mconv_decode_buffer (locale->coding, buf, size);
179 }
180
181
182 /** Encode the M-text MT by the coding system associated with LOCALE,
183     and store the resulting bytes in the memory area at BUF of *SIZE
184     bytes.  If the area is too short, allocate a new and wider area.
185     Store the length of the generated bytes in the place pointed by
186     SIZE, and return the address of those bytes.  */
187
188 static unsigned char *
189 encode_locale (MText *mt, unsigned char *buf, int *size, MLocale *locale)
190 {
191   int nbytes = mconv_encode_buffer (locale->coding, mt, buf, *size - 1);
192
193   if (nbytes < 0)
194     {
195       buf = NULL;
196       *size *= 2;
197       do {
198         MTABLE_REALLOC (buf, *size, MERROR_LOCALE);
199         nbytes = mconv_encode_buffer (mlocale__ctype->coding, mt, buf,
200                                       *size - 1);
201       } while (nbytes < 0);
202     }
203   buf[nbytes] = '\0';
204   *size = nbytes;
205   return buf;
206 }
207
208
209 /** Structure of transformed strings.  The function mtext_coll ()
210     caches this object in an M-text as a text property.  */
211
212 typedef struct {
213   /* Common header for a managed object.  */
214   M17NObject control;
215
216   /* Locale corresponding to <str>.  */
217   MLocale *locale;
218
219   /** Result of strxfrm.  */
220   char *str;
221 } MXfrm;
222
223
224 static void
225 free_xfrm (void *object)
226 {
227   MXfrm *xfrm = (MXfrm *) object;
228
229   M17N_OBJECT_UNREF (xfrm->locale);
230   free (xfrm->str);
231 }
232
233 static char *
234 get_xfrm (MText *mt)
235 {
236   MTextProperty *prop = mtext_get_property (mt, 0, M_xfrm);
237   MXfrm *xfrm;
238   int size;
239   unsigned char *buf, *newbuf;
240   int request;
241
242   if (prop)
243     {
244       if (prop->end == mt->nchars)
245         {
246           xfrm = (MXfrm *) prop->val;
247           if (xfrm->locale == mlocale__ctype)
248             return xfrm->str;
249         }
250       mtext_detach_property (prop);
251     }
252
253   size = mt->nbytes;
254   buf = alloca (size);
255   newbuf = encode_locale (mt, buf, &size, mlocale__ctype);
256   M17N_OBJECT (xfrm, free_xfrm, MERROR_MTEXT);
257   xfrm->str = malloc (size);
258   request = strxfrm (xfrm->str, (char *) newbuf, size);
259   if (request >= size)
260     {
261       xfrm->str = realloc (xfrm->str, request);
262       strxfrm (xfrm->str, (char *) newbuf, size);
263     }
264   if (buf != newbuf)
265     free (newbuf);
266   prop = mtext_property (M_xfrm, xfrm, MTEXTPROP_VOLATILE_WEAK);
267   mtext_attach_property (mt, 0, mt->nchars, prop);
268   M17N_OBJECT_UNREF (prop);
269   return xfrm->str;
270 }
271
272 \f
273 /* Internal API */
274
275 int
276 mlocale__init ()
277 {
278   M_locale = msymbol_as_managing_key ("  locale");
279
280   Mlanguage = msymbol ("language");
281   Mterritory = msymbol ("territory");
282   Mcodeset = msymbol ("codeset");
283
284   mlocale__collate = mlocale_set (LC_COLLATE, NULL);
285   M17N_OBJECT_REF (mlocale__collate);
286   mlocale__ctype = mlocale_set (LC_CTYPE, NULL);
287   M17N_OBJECT_REF (mlocale__ctype);
288   mlocale__messages = mlocale_set (LC_MESSAGES, NULL);
289   M17N_OBJECT_REF (mlocale__messages);
290   mlocale__time = mlocale_set (LC_TIME, NULL);
291   M17N_OBJECT_REF (mlocale__time);
292
293   M_xfrm = msymbol_as_managing_key ("  xfrm");
294   return 0;
295 }
296
297 void
298 mlocale__fini ()
299 {
300   M17N_OBJECT_UNREF (mlocale__collate);
301   M17N_OBJECT_UNREF (mlocale__ctype);
302   M17N_OBJECT_UNREF (mlocale__messages);
303   M17N_OBJECT_UNREF (mlocale__time);
304 }
305
306 /*** @} */
307 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
308
309 \f
310 /* External API */
311 /*** @addtogroup m17nLocale */
312 /*** @{ */
313
314 /*=*/
315 /***en The symbol whose name is "language".  */
316 /***ja ¥·¥ó¥Ü¥ë "language" */
317 MSymbol Mlanguage;
318
319 /*=*/
320 /***en The symbol whose name is "territory".  */
321 /***ja ¥·¥ó¥Ü¥ë "territory" */
322 MSymbol Mterritory;
323
324 /*=*/
325 /***en The symbol whose name is "modifier".  */
326 /***ja ¥·¥ó¥Ü¥ë "modifier" */
327 MSymbol Mmodifier;
328
329 /*=*/
330 /***en The symbol whose name is "codeset".  */
331 /***ja ¥·¥ó¥Ü¥ë "codeset" */
332 MSymbol Mcodeset;
333
334 /*=*/
335
336 /***en
337     @brief Set the current locale.
338
339     The mlocale_set () function sets or query a part of the current
340     locale.  The part is specified by $CATEGORY which must be a valid
341     first argument to setlocale ().
342
343     If $LOCALE is not NULL, the locale of the specified part is set to
344     $LOCALE.  If $LOCALE is not supported by the system, the current
345     locale is not changed.
346
347     If $LOCALE is NULL, the current locale of the specified part is
348     queried.
349
350     @return
351     If the call is successful, mlocale_set () returns an opaque locale
352     object that corresponds to the locale.  The name of the locale can
353     be acquired by the function mlocale_get_prop ().
354
355     Otherwise, it returns NULL.  */
356
357 /***ja
358     @brief ¸½ºß¤Î¥í¥±¡¼¥ë¤òÀßÄꤹ¤ë.
359
360     ´Ø¿ô mlocale_set () ¤Ï $LOCALE_NAME ¤ò¸½ºß¤Î¥í¥±¡¼¥ë¤È¤¹¤ë¡£¤³¤Î´Ø
361     ¿ô¤Ï¥·¥¹¥Æ¥à´Ø¿ô <tt>setlocale ()</tt> ¤ò¸Æ¤Ó¡¢³°ÉôÊÑ¿ô @c
362     mlocale_current ¤òÀßÄꤹ¤ë¡£
363
364     @return
365     ¥·¥¹¥Æ¥à¤¬ $LOCALE_NAME ¤ò¥µ¥Ý¡¼¥È¤¹¤ë¤Ê¤é¤Ð mlocale_set () ¤Ï 0 
366     ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼
367     ¥É¤òÀßÄꤹ¤ë¡£  */
368
369 /***
370     @errors
371     @c MERROR_LOCALE  */
372
373 MLocale *
374 mlocale_set (int category, const char *name)
375 {
376   char *new;
377   MLocale *locale;
378
379   new = setlocale (category, name);
380   if (! new)
381     return NULL;
382
383   locale = (MLocale *) msymbol_get (msymbol (new), M_locale);
384   if (! locale)
385     locale = make_locale (new);
386   if (! locale)
387     return NULL;
388   if (name && (category == LC_ALL || category == LC_COLLATE))
389     {
390       M17N_OBJECT_UNREF (mlocale__collate);
391       M17N_OBJECT_REF (locale);
392       mlocale__collate = locale;
393     }
394   else if (name && (category == LC_ALL || category == LC_CTYPE))
395     {
396       M17N_OBJECT_UNREF (mlocale__ctype);
397       M17N_OBJECT_REF (locale);
398       mlocale__ctype = locale;
399     }
400   if (name && (category == LC_ALL || category == LC_MESSAGES))
401     {
402       M17N_OBJECT_UNREF (mlocale__messages);
403       M17N_OBJECT_REF (locale);
404       mlocale__messages = locale;
405     }
406   if (name && (category == LC_ALL || category == LC_TIME))
407     {
408       M17N_OBJECT_UNREF (mlocale__time);
409       M17N_OBJECT_REF (locale);
410       mlocale__time = locale;
411     }
412   return locale;
413 }
414
415 /*=*/
416
417 /***en
418     @brief Get the value of a locale property.
419
420     The mlocale_get_prop () function returns the value of a property
421     $KEY of local $LOCALE.  $KEY must be #Mname, #Mlanguage,
422     #Mterritory, #Mcodeset, #Mmodifier, or #Mcoding.  */ 
423
424 /***ja
425     @brief ¥í¥±¡¼¥ë¤Î¥×¥í¥Ñ¥Æ¥£ÃͤòÆÀ¤ë
426
427     ´Ø¿ô mlocale_get_prop () ¤Ï¡¢¥í¥±¡¼¥ë $LOCALE ¤Î $KEY ¥×¥í¥Ñ¥Æ¥£¤Î
428     ÃͤòÊÖ¤¹¡£ $KEY ¤Ï #Mname ¡¢ #Mlanguage ¡¢ #Mterritory ¡¢
429     #Mcodeset ¡¢ #Mmodifier ¤â¤·¤¯¤Ï #Mcoding ¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê
430     ¤¤¡£  */ 
431
432 MSymbol
433 mlocale_get_prop (MLocale *locale, MSymbol key)
434 {
435   if (key == Mcoding)
436     return locale->coding;
437   if (key == Mname)
438     return locale->name;
439   if (key == Mlanguage)
440     return locale->language;
441   if (key == Mterritory)
442     return locale->territory;
443   if (key == Mcodeset)
444     return locale->codeset;
445   if (key == Mmodifier)
446     return locale->modifier;
447   return Mnil;
448 }
449
450 /*=*/
451 /***en
452     @brief Format date and time
453
454     The mtext_ftime () function formats the broken-down time $TM
455     according to the format specification $FORMAT and append the
456     result to the M-text $MT.  The formating is done according to the
457     locale $LOCALE (if not NULL) or the current locale (LC_TIME).
458
459     The meaning of the arguments $TM and $FORMAT are the same as those
460     of strftime ().
461
462     @seealso
463     strftime ()
464
465 */
466
467 int
468 mtext_ftime (MText *mt, const char *format, const struct tm *tm,
469              MLocale *locale)
470 {
471   int bufsize;
472   unsigned char *buf;
473   size_t nbytes, nchars;
474   char *current_locale = NULL;
475
476   if (locale)
477     {
478       char *str = setlocale (LC_TIME, NULL);
479       int len = strlen (str) + 1;
480
481       current_locale = alloca (len);
482       memcpy (current_locale, str, len);
483       mlocale_set (LC_TIME, msymbol_name (locale->name));
484     }
485
486   bufsize = 1024;
487   while (1)
488     {
489       MTABLE_ALLOCA (buf, bufsize, MERROR_MTEXT);
490       buf[0] = 1;
491       nbytes = strftime ((char *) buf, bufsize, format, tm);
492       if (nbytes > 0
493           || ! buf[0])
494         break;
495       bufsize *= 2;
496     }
497
498   if (nbytes > 0)
499     {
500       MText *work = decode_locale (buf, nbytes, mlocale__time);
501
502       if (work)
503         {
504           nchars = work->nchars;
505           mtext_cat (mt, work);
506           M17N_OBJECT_UNREF (work);
507         }
508       else
509         nchars = 0;
510     }
511   else
512     nchars = 0;
513
514   if (current_locale)
515     mlocale_set (LC_TIME, current_locale);
516
517   return nchars;
518 }
519           
520 /*=*/
521
522 /***en
523     @brief Get an environment variable
524
525     The mtext_getenv () function searches the environment list for a
526     string that matches the string pointed to by $NAME.
527
528     If there is a match, the function decodes the value according to
529     the current locale (LC_CTYPE) into an M-text, and return that
530     M-text.
531
532     If there is no match, the function returns NULL.  */
533
534 MText *
535 mtext_getenv (const char *name)
536 {
537   char *p = getenv (name);
538
539   if (!p)
540     return NULL;
541   return decode_locale ((unsigned char *) p, strlen (p), mlocale__ctype);
542 }
543
544 /*=*/
545
546 /***en
547     @brief Change or add an environment variable.
548
549     The mtext_putenv () function adds or changed the value of
550     environment variables according to M-text $MT.  It simply calls
551     the function putenv with an argument generated by encoding $MT
552     according to the current locale (LC_CTYPE).
553
554     @return
555     This function returns zero on success, or -1 if an error
556     occurs.  */
557
558 int
559 mtext_putenv (MText *mt)
560 {
561   unsigned char buf[1024];
562   int size = 1024;
563   unsigned char *newbuf;
564   int result;
565
566   newbuf = encode_locale (mt, buf, &size, mlocale__ctype);
567   result = putenv ((char *) newbuf);
568   if (buf != newbuf)
569     free (newbuf);
570   return result;
571 }
572
573 /*=*/
574
575 /***en
576     @brief Compare two M-texts using the current locale.
577
578     The mtext_coll () function compares the two M-texts $MT1 and $MT2.
579     It returns an integer less than, equal to, or greater than zero if
580     $MT1 is found, respectively, to be less than, to match, or to be
581     greater than $MT2.  The comparison is based on texts as
582     appropriate for the current locale (LC_COLLATE).
583
584     This function makes use of information that is automatically
585     cached in the M-texts as a text property.  So, the second call of
586     this function with $MT1 or $MT2 finishes faster than the first
587     call.  */
588
589 int
590 mtext_coll (MText *mt1, MText *mt2)
591 {
592   char *str1, *str2;
593
594   if (mt1->nchars == 0)
595     return (mt2->nchars == 0 ? 0 : -1);
596   else if (mt2->nchars == 0)
597     return 1;
598
599   str1 = get_xfrm (mt1);
600   str2 = get_xfrm (mt2);
601   return strcoll (str1, str2);
602 }
603
604 /*** @} */
605
606 /*
607   Local Variables:
608   coding: euc-japan
609   End:
610 */