8cacf5f8d281faa40b0fbfaa2ebf153ace8c0acb
[bpt/guile.git] / libguile / i18n.c
1 /* Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This 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 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <alloca.h>
23
24 #include "libguile/_scm.h"
25 #include "libguile/feature.h"
26 #include "libguile/i18n.h"
27 #include "libguile/strings.h"
28 #include "libguile/chars.h"
29 #include "libguile/dynwind.h"
30 #include "libguile/validate.h"
31 #include "libguile/values.h"
32 #include "libguile/threads.h"
33
34 #include <locale.h>
35 #include <string.h> /* `strcoll ()' */
36 #include <ctype.h> /* `toupper ()' et al. */
37 #include <errno.h>
38
39 #if (defined HAVE_NEWLOCALE) && (defined HAVE_STRCOLL_L)
40 /* The GNU thread-aware locale API is documented in ``Thread-Aware Locale
41 Model, a Proposal'', by Ulrich Drepper:
42
43 http://people.redhat.com/drepper/tllocale.ps.gz
44
45 It is now also implemented by Darwin:
46
47 http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/newlocale.3.html
48
49 The whole API was eventually standardized in the ``Open Group Base
50 Specifications Issue 7'' (aka. "POSIX 2008"):
51
52 http://www.opengroup.org/onlinepubs/9699919799/basedefs/locale.h.html */
53 # define USE_GNU_LOCALE_API
54 #endif
55
56 #if (defined USE_GNU_LOCALE_API) && (defined HAVE_XLOCALE_H)
57 # include <xlocale.h>
58 #endif
59
60 #include "libguile/posix.h" /* for `scm_i_locale_mutex' */
61
62 #if (defined HAVE_LANGINFO_H) && (defined HAVE_NL_TYPES_H)
63 # include <langinfo.h>
64 # include <nl_types.h>
65 #endif
66
67 #ifndef HAVE_SETLOCALE
68 static inline char *
69 setlocale (int category, const char *name)
70 {
71 errno = ENOSYS;
72 return NULL;
73 }
74 #endif
75
76 /* Helper stringification macro. */
77 #define SCM_I18N_STRINGIFY(_name) # _name
78
79
80 \f
81 /* Locale objects, string and character collation, and other locale-dependent
82 string operations.
83
84 A large part of the code here deals with emulating glibc's reentrant
85 locale API on non-GNU systems. The emulation is a bit "brute-force":
86 Whenever a `-locale<?' procedure is passed a locale object, then:
87
88 1. The `scm_i_locale_mutex' is locked.
89 2. A series of `setlocale ()' call is performed to store the current
90 locale for each category in an `scm_t_locale' object.
91 3. A series of `setlocale ()' call is made to install each of the locale
92 categories of each of the base locales of each locale object,
93 recursively, starting from the last locale object of the chain.
94 4. The settings captured in step (2) are restored.
95 5. The `scm_i_locale_mutex' is released.
96
97 Hopefully, the X/Open standard will eventually make this hack useless.
98
99 Note: We don't wrap glibc's `uselocale ()' call because it sets the locale
100 of the current _thread_ (unlike `setlocale ()') and doing so would require
101 maintaining per-thread locale information on non-GNU systems and always
102 re-installing this locale upon locale-dependent calls. */
103
104
105 /* Return the category mask corresponding to CAT. */
106 #define SCM_LOCALE_CATEGORY_MASK(_cat) LC_ ## _cat ## _MASK
107
108
109 #ifndef USE_GNU_LOCALE_API
110
111 /* Provide the locale category masks as found in glibc. This must be kept in
112 sync with `locale-categories.h'. */
113
114 # define LC_CTYPE_MASK 1
115 # define LC_COLLATE_MASK 2
116 # define LC_MESSAGES_MASK 4
117 # define LC_MONETARY_MASK 8
118 # define LC_NUMERIC_MASK 16
119 # define LC_TIME_MASK 32
120
121 # ifdef LC_PAPER
122 # define LC_PAPER_MASK 64
123 # else
124 # define LC_PAPER_MASK 0
125 # endif
126 # ifdef LC_NAME
127 # define LC_NAME_MASK 128
128 # else
129 # define LC_NAME_MASK 0
130 # endif
131 # ifdef LC_ADDRESS
132 # define LC_ADDRESS_MASK 256
133 # else
134 # define LC_ADDRESS_MASK 0
135 # endif
136 # ifdef LC_TELEPHONE
137 # define LC_TELEPHONE_MASK 512
138 # else
139 # define LC_TELEPHONE_MASK 0
140 # endif
141 # ifdef LC_MEASUREMENT
142 # define LC_MEASUREMENT_MASK 1024
143 # else
144 # define LC_MEASUREMENT_MASK 0
145 # endif
146 # ifdef LC_IDENTIFICATION
147 # define LC_IDENTIFICATION_MASK 2048
148 # else
149 # define LC_IDENTIFICATION_MASK 0
150 # endif
151
152 # define LC_ALL_MASK (LC_CTYPE_MASK \
153 | LC_NUMERIC_MASK \
154 | LC_TIME_MASK \
155 | LC_COLLATE_MASK \
156 | LC_MONETARY_MASK \
157 | LC_MESSAGES_MASK \
158 | LC_PAPER_MASK \
159 | LC_NAME_MASK \
160 | LC_ADDRESS_MASK \
161 | LC_TELEPHONE_MASK \
162 | LC_MEASUREMENT_MASK \
163 | LC_IDENTIFICATION_MASK \
164 )
165
166 /* Locale objects as returned by `make-locale' on non-GNU systems. */
167 typedef struct scm_locale
168 {
169 SCM base_locale; /* a `locale' object */
170 char *locale_name;
171 int category_mask;
172 } *scm_t_locale;
173
174
175 /* Free the resources used by LOCALE. */
176 static inline void
177 scm_i_locale_free (scm_t_locale locale)
178 {
179 free (locale->locale_name);
180 locale->locale_name = NULL;
181 }
182
183 #else /* USE_GNU_LOCALE_API */
184
185 /* Alias for glibc's locale type. */
186 typedef locale_t scm_t_locale;
187
188 #define scm_i_locale_free freelocale
189
190 #endif /* USE_GNU_LOCALE_API */
191
192
193 /* A locale object denoting the global locale. */
194 SCM_GLOBAL_VARIABLE (scm_global_locale, "%global-locale");
195
196
197 /* Validate parameter ARG as a locale object and set C_LOCALE to the
198 corresponding C locale object. */
199 #define SCM_VALIDATE_LOCALE_COPY(_pos, _arg, _c_locale) \
200 do \
201 { \
202 SCM_VALIDATE_SMOB ((_pos), (_arg), locale_smob_type); \
203 (_c_locale) = (scm_t_locale)SCM_SMOB_DATA (_arg); \
204 } \
205 while (0)
206
207 /* Validate optional parameter ARG as either undefined or bound to a locale
208 object. Set C_LOCALE to the corresponding C locale object or NULL. */
209 #define SCM_VALIDATE_OPTIONAL_LOCALE_COPY(_pos, _arg, _c_locale) \
210 do \
211 { \
212 if ((_arg) != SCM_UNDEFINED) \
213 SCM_VALIDATE_LOCALE_COPY (_pos, _arg, _c_locale); \
214 else \
215 (_c_locale) = NULL; \
216 } \
217 while (0)
218
219
220 SCM_SMOB (scm_tc16_locale_smob_type, "locale", 0);
221
222 SCM_SMOB_FREE (scm_tc16_locale_smob_type, smob_locale_free, locale)
223 {
224 scm_t_locale c_locale;
225
226 c_locale = (scm_t_locale) SCM_SMOB_DATA (locale);
227 scm_i_locale_free (c_locale);
228
229 return 0;
230 }
231
232 #ifndef USE_GNU_LOCALE_API
233 static SCM
234 smob_locale_mark (SCM locale)
235 {
236 register SCM dependency;
237
238 if (!scm_is_eq (locale, SCM_VARIABLE_REF (scm_global_locale)))
239 {
240 scm_t_locale c_locale;
241
242 c_locale = (scm_t_locale) SCM_SMOB_DATA (locale);
243 dependency = (c_locale->base_locale);
244 }
245 else
246 dependency = SCM_BOOL_F;
247
248 return dependency;
249 }
250 #endif
251
252
253 static void inline scm_locale_error (const char *, int) SCM_NORETURN;
254
255 /* Throw an exception corresponding to error ERR. */
256 static void inline
257 scm_locale_error (const char *func_name, int err)
258 {
259 scm_syserror_msg (func_name,
260 "Failed to install locale",
261 SCM_EOL, err);
262 }
263
264
265 \f
266 /* Emulating GNU's reentrant locale API. */
267 #ifndef USE_GNU_LOCALE_API
268
269
270 /* Maximum number of chained locales (via `base_locale'). */
271 #define LOCALE_STACK_SIZE_MAX 256
272
273 typedef struct
274 {
275 #define SCM_DEFINE_LOCALE_CATEGORY(_name) char * _name;
276 #include "locale-categories.h"
277 #undef SCM_DEFINE_LOCALE_CATEGORY
278 } scm_t_locale_settings;
279
280 /* Fill out SETTINGS according to the current locale settings. On success
281 zero is returned and SETTINGS is properly initialized. */
282 static int
283 get_current_locale_settings (scm_t_locale_settings *settings)
284 {
285 const char *locale_name;
286
287 #define SCM_DEFINE_LOCALE_CATEGORY(_name) \
288 { \
289 SCM_SYSCALL (locale_name = setlocale (LC_ ## _name, NULL)); \
290 if (locale_name == NULL) \
291 goto handle_error; \
292 \
293 settings-> _name = strdup (locale_name); \
294 if (settings-> _name == NULL) \
295 goto handle_oom; \
296 }
297
298 #include "locale-categories.h"
299 #undef SCM_DEFINE_LOCALE_CATEGORY
300
301 return 0;
302
303 handle_error:
304 return EINVAL;
305
306 handle_oom:
307 return ENOMEM;
308 }
309
310 /* Restore locale settings SETTINGS. On success, return zero. */
311 static int
312 restore_locale_settings (const scm_t_locale_settings *settings)
313 {
314 const char *result;
315
316 #define SCM_DEFINE_LOCALE_CATEGORY(_name) \
317 SCM_SYSCALL (result = setlocale (LC_ ## _name, settings-> _name)); \
318 if (result == NULL) \
319 goto handle_error;
320
321 #include "locale-categories.h"
322 #undef SCM_DEFINE_LOCALE_CATEGORY
323
324 return 0;
325
326 handle_error:
327 return EINVAL;
328 }
329
330 /* Free memory associated with SETTINGS. */
331 static void
332 free_locale_settings (scm_t_locale_settings *settings)
333 {
334 #define SCM_DEFINE_LOCALE_CATEGORY(_name) \
335 free (settings-> _name); \
336 settings->_name = NULL;
337 #include "locale-categories.h"
338 #undef SCM_DEFINE_LOCALE_CATEGORY
339 }
340
341 /* Install the locale named LOCALE_NAME for all the categories listed in
342 CATEGORY_MASK. */
343 static int
344 install_locale_categories (const char *locale_name, int category_mask)
345 {
346 const char *result;
347
348 if (category_mask == LC_ALL_MASK)
349 {
350 SCM_SYSCALL (result = setlocale (LC_ALL, locale_name));
351 if (result == NULL)
352 goto handle_error;
353 }
354 else
355 {
356 #define SCM_DEFINE_LOCALE_CATEGORY(_name) \
357 if (category_mask & SCM_LOCALE_CATEGORY_MASK (_name)) \
358 { \
359 SCM_SYSCALL (result = setlocale (LC_ ## _name, locale_name)); \
360 if (result == NULL) \
361 goto handle_error; \
362 }
363 #include "locale-categories.h"
364 #undef SCM_DEFINE_LOCALE_CATEGORY
365 }
366
367 return 0;
368
369 handle_error:
370 return EINVAL;
371 }
372
373 /* Install LOCALE, recursively installing its base locales first. On
374 success, zero is returned. */
375 static int
376 install_locale (scm_t_locale locale)
377 {
378 scm_t_locale stack[LOCALE_STACK_SIZE_MAX];
379 int category_mask = 0;
380 size_t stack_size = 0;
381 int stack_offset = 0;
382 const char *result = NULL;
383
384 /* Build up a locale stack by traversing the `base_locale' link. */
385 do
386 {
387 if (stack_size >= LOCALE_STACK_SIZE_MAX)
388 /* We cannot use `scm_error ()' here because otherwise the locale
389 mutex may remain locked. */
390 return EINVAL;
391
392 stack[stack_size++] = locale;
393
394 /* Keep track of which categories have already been taken into
395 account. */
396 category_mask |= locale->category_mask;
397
398 if (locale->base_locale != SCM_UNDEFINED)
399 locale = (scm_t_locale) SCM_SMOB_DATA (locale->base_locale);
400 else
401 locale = NULL;
402 }
403 while ((locale != NULL) && (category_mask != LC_ALL_MASK));
404
405 /* Install the C locale to start from a pristine state. */
406 SCM_SYSCALL (result = setlocale (LC_ALL, "C"));
407 if (result == NULL)
408 goto handle_error;
409
410 /* Install the locales in reverse order. */
411 for (stack_offset = stack_size - 1;
412 stack_offset >= 0;
413 stack_offset--)
414 {
415 int err;
416 scm_t_locale locale;
417
418 locale = stack[stack_offset];
419 err = install_locale_categories (locale->locale_name,
420 locale->category_mask);
421 if (err)
422 goto handle_error;
423 }
424
425 return 0;
426
427 handle_error:
428 return EINVAL;
429 }
430
431 /* Leave the locked locale section. */
432 static inline void
433 leave_locale_section (const scm_t_locale_settings *settings)
434 {
435 /* Restore the previous locale settings. */
436 (void)restore_locale_settings (settings);
437
438 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
439 }
440
441 /* Enter a locked locale section. */
442 static inline int
443 enter_locale_section (scm_t_locale locale,
444 scm_t_locale_settings *prev_locale)
445 {
446 int err;
447
448 scm_i_pthread_mutex_lock (&scm_i_locale_mutex);
449
450 err = get_current_locale_settings (prev_locale);
451 if (err)
452 {
453 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
454 return err;
455 }
456
457 err = install_locale (locale);
458 if (err)
459 {
460 leave_locale_section (prev_locale);
461 free_locale_settings (prev_locale);
462 }
463
464 return err;
465 }
466
467 /* Convenient macro to run STATEMENT in the locale context of C_LOCALE. */
468 #define RUN_IN_LOCALE_SECTION(_c_locale, _statement) \
469 do \
470 { \
471 int lsec_err; \
472 scm_t_locale_settings lsec_prev_locale; \
473 \
474 lsec_err = enter_locale_section ((_c_locale), &lsec_prev_locale); \
475 if (lsec_err) \
476 scm_locale_error (FUNC_NAME, lsec_err); \
477 else \
478 { \
479 _statement ; \
480 \
481 leave_locale_section (&lsec_prev_locale); \
482 free_locale_settings (&lsec_prev_locale); \
483 } \
484 } \
485 while (0)
486
487 /* Convert the current locale settings into a locale SMOB. On success, zero
488 is returned and RESULT points to the new SMOB. Otherwise, an error is
489 returned. */
490 static int
491 get_current_locale (SCM *result)
492 {
493 int err = 0;
494 scm_t_locale c_locale;
495 const char *current_locale;
496
497 c_locale = scm_gc_malloc (sizeof (* c_locale), "locale");
498
499
500 scm_i_pthread_mutex_lock (&scm_i_locale_mutex);
501
502 c_locale->category_mask = LC_ALL_MASK;
503 c_locale->base_locale = SCM_UNDEFINED;
504
505 current_locale = setlocale (LC_ALL, NULL);
506 if (current_locale != NULL)
507 {
508 c_locale->locale_name = strdup (current_locale);
509 if (c_locale->locale_name == NULL)
510 err = ENOMEM;
511 }
512 else
513 err = EINVAL;
514
515 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
516
517 if (err)
518 scm_gc_free (c_locale, sizeof (* c_locale), "locale");
519 else
520 SCM_NEWSMOB (*result, scm_tc16_locale_smob_type, c_locale);
521
522 return err;
523 }
524
525
526 #endif /* !USE_GNU_LOCALE_API */
527
528
529 \f
530 /* `make-locale' can take either category lists or single categories (the
531 `LC_*' integer constants). */
532 #define SCM_LIST_OR_INTEGER_P(arg) \
533 (scm_is_integer (arg) || scm_is_true (scm_list_p (arg)))
534
535
536 /* Return the category mask corresponding to CATEGORY (an `LC_' integer
537 constant). */
538 static inline int
539 category_to_category_mask (SCM category,
540 const char *func_name, int pos)
541 {
542 int c_category;
543 int c_category_mask;
544
545 c_category = scm_to_int (category);
546
547 #define SCM_DEFINE_LOCALE_CATEGORY(_name) \
548 case LC_ ## _name: \
549 c_category_mask = SCM_LOCALE_CATEGORY_MASK (_name); \
550 break;
551
552 switch (c_category)
553 {
554 #include "locale-categories.h"
555
556 case LC_ALL:
557 c_category_mask = LC_ALL_MASK;
558 break;
559
560 default:
561 scm_wrong_type_arg_msg (func_name, pos, category,
562 "locale category");
563 }
564
565 #undef SCM_DEFINE_LOCALE_CATEGORY
566
567 return c_category_mask;
568 }
569
570 /* Convert CATEGORIES, a list of locale categories or a single category (an
571 integer), into a category mask. */
572 static int
573 category_list_to_category_mask (SCM categories,
574 const char *func_name, int pos)
575 {
576 int c_category_mask = 0;
577
578 if (scm_is_integer (categories))
579 c_category_mask = category_to_category_mask (categories,
580 func_name, pos);
581 else
582 for (; !scm_is_null (categories); categories = SCM_CDR (categories))
583 {
584 SCM category = SCM_CAR (categories);
585
586 c_category_mask |=
587 category_to_category_mask (category, func_name, pos);
588 }
589
590 return c_category_mask;
591 }
592
593
594 SCM_DEFINE (scm_make_locale, "make-locale", 2, 1, 0,
595 (SCM category_list, SCM locale_name, SCM base_locale),
596 "Return a reference to a data structure representing a set of "
597 "locale datasets. @var{category_list} should be either a list "
598 "of locale categories or a single category as used with "
599 "@code{setlocale} (@pxref{Locales, @code{setlocale}}) and "
600 "@var{locale_name} should be the name of the locale considered "
601 "(e.g., @code{\"sl_SI\"}). Optionally, if @code{base_locale} is "
602 "passed, it should be a locale object denoting settings for "
603 "categories not listed in @var{category_list}.")
604 #define FUNC_NAME s_scm_make_locale
605 {
606 SCM locale = SCM_BOOL_F;
607 int err = 0;
608 int c_category_mask;
609 char *c_locale_name;
610 scm_t_locale c_base_locale, c_locale;
611
612 SCM_MAKE_VALIDATE (1, category_list, LIST_OR_INTEGER_P);
613 SCM_VALIDATE_STRING (2, locale_name);
614 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (3, base_locale, c_base_locale);
615
616 c_category_mask = category_list_to_category_mask (category_list,
617 FUNC_NAME, 1);
618 c_locale_name = scm_to_locale_string (locale_name);
619
620 #ifdef USE_GNU_LOCALE_API
621
622 if (scm_is_eq (base_locale, SCM_VARIABLE_REF (scm_global_locale)))
623 {
624 /* Fetch the current locale and turn in into a `locale_t'. Don't
625 duplicate the resulting `locale_t' because we want it to be consumed
626 by `newlocale ()'. */
627 char *current_locale;
628
629 scm_i_pthread_mutex_lock (&scm_i_locale_mutex);
630
631 current_locale = setlocale (LC_ALL, NULL);
632 c_base_locale = newlocale (LC_ALL_MASK, current_locale, NULL);
633
634 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
635
636 if (c_base_locale == (locale_t) 0)
637 scm_locale_error (FUNC_NAME, errno);
638 }
639 else if (c_base_locale != (locale_t) 0)
640 {
641 /* C_BASE_LOCALE is to be consumed by `newlocale ()' so it needs to be
642 duplicated before. */
643 c_base_locale = duplocale (c_base_locale);
644 if (c_base_locale == (locale_t) 0)
645 {
646 err = errno;
647 goto fail;
648 }
649 }
650
651 c_locale = newlocale (c_category_mask, c_locale_name, c_base_locale);
652
653 free (c_locale_name);
654
655 if (c_locale == (locale_t) 0)
656 {
657 if (scm_is_eq (base_locale, SCM_VARIABLE_REF (scm_global_locale)))
658 /* The base locale object was created lazily and must be freed. */
659 freelocale (c_base_locale);
660
661 scm_locale_error (FUNC_NAME, errno);
662 }
663 else
664 SCM_NEWSMOB (locale, scm_tc16_locale_smob_type, c_locale);
665
666 #else
667
668 c_locale = scm_gc_malloc (sizeof (* c_locale), "locale");
669
670 c_locale->category_mask = c_category_mask;
671 c_locale->locale_name = c_locale_name;
672
673 if (scm_is_eq (base_locale, SCM_VARIABLE_REF (scm_global_locale)))
674 {
675 /* Get the current locale settings and turn them into a locale
676 object. */
677 err = get_current_locale (&base_locale);
678 if (err)
679 goto fail;
680 }
681
682 c_locale->base_locale = base_locale;
683
684 {
685 /* Try out the new locale and raise an exception if it doesn't work. */
686 int err;
687 scm_t_locale_settings prev_locale;
688
689 err = enter_locale_section (c_locale, &prev_locale);
690
691 if (err)
692 goto fail;
693 else
694 {
695 leave_locale_section (&prev_locale);
696 SCM_NEWSMOB (locale, scm_tc16_locale_smob_type, c_locale);
697 }
698 }
699
700 #endif
701
702 return locale;
703
704 fail:
705 #ifndef USE_GNU_LOCALE_API
706 scm_gc_free (c_locale, sizeof (* c_locale), "locale");
707 #endif
708 free (c_locale_name);
709 scm_locale_error (FUNC_NAME, err);
710
711 return SCM_BOOL_F;
712 }
713 #undef FUNC_NAME
714
715 SCM_DEFINE (scm_locale_p, "locale?", 1, 0, 0,
716 (SCM obj),
717 "Return true if @var{obj} is a locale object.")
718 #define FUNC_NAME s_scm_locale_p
719 {
720 return scm_from_bool (SCM_SMOB_PREDICATE (scm_tc16_locale_smob_type, obj));
721 }
722 #undef FUNC_NAME
723
724
725 \f
726 /* Locale-dependent string comparison.
727
728 A similar API can be found in MzScheme starting from version 200:
729 http://download.plt-scheme.org/chronology/mzmr200alpha14.html . */
730
731
732 /* Compare null-terminated strings C_S1 and C_S2 according to LOCALE. Return
733 an integer whose sign is the same as the difference between C_S1 and
734 C_S2. */
735 static inline int
736 compare_strings (const char *c_s1, const char *c_s2, SCM locale,
737 const char *func_name)
738 #define FUNC_NAME func_name
739 {
740 int result;
741 scm_t_locale c_locale;
742
743 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (3, locale, c_locale);
744
745 if (c_locale)
746 {
747 #ifdef USE_GNU_LOCALE_API
748 result = strcoll_l (c_s1, c_s2, c_locale);
749 #else
750 #ifdef HAVE_STRCOLL
751 RUN_IN_LOCALE_SECTION (c_locale, result = strcoll (c_s1, c_s2));
752 #else
753 result = strcmp (c_s1, c_s2);
754 #endif
755 #endif /* !USE_GNU_LOCALE_API */
756 }
757 else
758
759 #ifdef HAVE_STRCOLL
760 result = strcoll (c_s1, c_s2);
761 #else
762 result = strcmp (c_s1, c_s2);
763 #endif
764
765 return result;
766 }
767 #undef FUNC_NAME
768
769 /* Store into DST an upper-case version of SRC. */
770 static inline void
771 str_upcase (register char *dst, register const char *src)
772 {
773 for (; *src != '\0'; src++, dst++)
774 *dst = toupper ((int) *src);
775 *dst = '\0';
776 }
777
778 static inline void
779 str_downcase (register char *dst, register const char *src)
780 {
781 for (; *src != '\0'; src++, dst++)
782 *dst = tolower ((int) *src);
783 *dst = '\0';
784 }
785
786 #ifdef USE_GNU_LOCALE_API
787 static inline void
788 str_upcase_l (register char *dst, register const char *src,
789 scm_t_locale locale)
790 {
791 for (; *src != '\0'; src++, dst++)
792 *dst = toupper_l (*src, locale);
793 *dst = '\0';
794 }
795
796 static inline void
797 str_downcase_l (register char *dst, register const char *src,
798 scm_t_locale locale)
799 {
800 for (; *src != '\0'; src++, dst++)
801 *dst = tolower_l (*src, locale);
802 *dst = '\0';
803 }
804 #endif
805
806
807 /* Compare null-terminated strings C_S1 and C_S2 in a case-independent way
808 according to LOCALE. Return an integer whose sign is the same as the
809 difference between C_S1 and C_S2. */
810 static inline int
811 compare_strings_ci (const char *c_s1, const char *c_s2, SCM locale,
812 const char *func_name)
813 #define FUNC_NAME func_name
814 {
815 int result;
816 scm_t_locale c_locale;
817 char *c_us1, *c_us2;
818
819 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (3, locale, c_locale);
820
821 c_us1 = (char *) alloca (strlen (c_s1) + 1);
822 c_us2 = (char *) alloca (strlen (c_s2) + 1);
823
824 if (c_locale)
825 {
826 #ifdef USE_GNU_LOCALE_API
827 str_upcase_l (c_us1, c_s1, c_locale);
828 str_upcase_l (c_us2, c_s2, c_locale);
829
830 result = strcoll_l (c_us1, c_us2, c_locale);
831 #else
832 int err;
833 scm_t_locale_settings prev_locale;
834
835 err = enter_locale_section (c_locale, &prev_locale);
836 if (err)
837 {
838 scm_locale_error (func_name, err);
839 return 0;
840 }
841
842 str_upcase (c_us1, c_s1);
843 str_upcase (c_us2, c_s2);
844
845 #ifdef HAVE_STRCOLL
846 result = strcoll (c_us1, c_us2);
847 #else
848 result = strcmp (c_us1, c_us2);
849 #endif /* !HAVE_STRCOLL */
850
851 leave_locale_section (&prev_locale);
852 free_locale_settings (&prev_locale);
853 #endif /* !USE_GNU_LOCALE_API */
854 }
855 else
856 {
857 str_upcase (c_us1, c_s1);
858 str_upcase (c_us2, c_s2);
859
860 #ifdef HAVE_STRCOLL
861 result = strcoll (c_us1, c_us2);
862 #else
863 result = strcmp (c_us1, c_us2);
864 #endif
865 }
866
867 return result;
868 }
869 #undef FUNC_NAME
870
871
872 SCM_DEFINE (scm_string_locale_lt, "string-locale<?", 2, 1, 0,
873 (SCM s1, SCM s2, SCM locale),
874 "Compare strings @var{s1} and @var{s2} in a locale-dependent way."
875 "If @var{locale} is provided, it should be locale object (as "
876 "returned by @code{make-locale}) and will be used to perform the "
877 "comparison; otherwise, the current system locale is used.")
878 #define FUNC_NAME s_scm_string_locale_lt
879 {
880 int result;
881 const char *c_s1, *c_s2;
882
883 SCM_VALIDATE_STRING (1, s1);
884 SCM_VALIDATE_STRING (2, s2);
885
886 c_s1 = scm_i_string_chars (s1);
887 c_s2 = scm_i_string_chars (s2);
888
889 result = compare_strings (c_s1, c_s2, locale, FUNC_NAME);
890
891 scm_remember_upto_here_2 (s1, s2);
892
893 return scm_from_bool (result < 0);
894 }
895 #undef FUNC_NAME
896
897 SCM_DEFINE (scm_string_locale_gt, "string-locale>?", 2, 1, 0,
898 (SCM s1, SCM s2, SCM locale),
899 "Compare strings @var{s1} and @var{s2} in a locale-dependent way."
900 "If @var{locale} is provided, it should be locale object (as "
901 "returned by @code{make-locale}) and will be used to perform the "
902 "comparison; otherwise, the current system locale is used.")
903 #define FUNC_NAME s_scm_string_locale_gt
904 {
905 int result;
906 const char *c_s1, *c_s2;
907
908 SCM_VALIDATE_STRING (1, s1);
909 SCM_VALIDATE_STRING (2, s2);
910
911 c_s1 = scm_i_string_chars (s1);
912 c_s2 = scm_i_string_chars (s2);
913
914 result = compare_strings (c_s1, c_s2, locale, FUNC_NAME);
915
916 scm_remember_upto_here_2 (s1, s2);
917
918 return scm_from_bool (result > 0);
919 }
920 #undef FUNC_NAME
921
922 SCM_DEFINE (scm_string_locale_ci_lt, "string-locale-ci<?", 2, 1, 0,
923 (SCM s1, SCM s2, SCM locale),
924 "Compare strings @var{s1} and @var{s2} in a case-insensitive, "
925 "and locale-dependent way. If @var{locale} is provided, it "
926 "should be locale object (as returned by @code{make-locale}) "
927 "and will be used to perform the comparison; otherwise, the "
928 "current system locale is used.")
929 #define FUNC_NAME s_scm_string_locale_ci_lt
930 {
931 int result;
932 const char *c_s1, *c_s2;
933
934 SCM_VALIDATE_STRING (1, s1);
935 SCM_VALIDATE_STRING (2, s2);
936
937 c_s1 = scm_i_string_chars (s1);
938 c_s2 = scm_i_string_chars (s2);
939
940 result = compare_strings_ci (c_s1, c_s2, locale, FUNC_NAME);
941
942 scm_remember_upto_here_2 (s1, s2);
943
944 return scm_from_bool (result < 0);
945 }
946 #undef FUNC_NAME
947
948 SCM_DEFINE (scm_string_locale_ci_gt, "string-locale-ci>?", 2, 1, 0,
949 (SCM s1, SCM s2, SCM locale),
950 "Compare strings @var{s1} and @var{s2} in a case-insensitive, "
951 "and locale-dependent way. If @var{locale} is provided, it "
952 "should be locale object (as returned by @code{make-locale}) "
953 "and will be used to perform the comparison; otherwise, the "
954 "current system locale is used.")
955 #define FUNC_NAME s_scm_string_locale_ci_gt
956 {
957 int result;
958 const char *c_s1, *c_s2;
959
960 SCM_VALIDATE_STRING (1, s1);
961 SCM_VALIDATE_STRING (2, s2);
962
963 c_s1 = scm_i_string_chars (s1);
964 c_s2 = scm_i_string_chars (s2);
965
966 result = compare_strings_ci (c_s1, c_s2, locale, FUNC_NAME);
967
968 scm_remember_upto_here_2 (s1, s2);
969
970 return scm_from_bool (result > 0);
971 }
972 #undef FUNC_NAME
973
974 SCM_DEFINE (scm_string_locale_ci_eq, "string-locale-ci=?", 2, 1, 0,
975 (SCM s1, SCM s2, SCM locale),
976 "Compare strings @var{s1} and @var{s2} in a case-insensitive, "
977 "and locale-dependent way. If @var{locale} is provided, it "
978 "should be locale object (as returned by @code{make-locale}) "
979 "and will be used to perform the comparison; otherwise, the "
980 "current system locale is used.")
981 #define FUNC_NAME s_scm_string_locale_ci_eq
982 {
983 int result;
984 const char *c_s1, *c_s2;
985
986 SCM_VALIDATE_STRING (1, s1);
987 SCM_VALIDATE_STRING (2, s2);
988
989 c_s1 = scm_i_string_chars (s1);
990 c_s2 = scm_i_string_chars (s2);
991
992 result = compare_strings_ci (c_s1, c_s2, locale, FUNC_NAME);
993
994 scm_remember_upto_here_2 (s1, s2);
995
996 return scm_from_bool (result == 0);
997 }
998 #undef FUNC_NAME
999
1000
1001 SCM_DEFINE (scm_char_locale_lt, "char-locale<?", 2, 1, 0,
1002 (SCM c1, SCM c2, SCM locale),
1003 "Return true if character @var{c1} is lower than @var{c2} "
1004 "according to @var{locale} or to the current locale.")
1005 #define FUNC_NAME s_scm_char_locale_lt
1006 {
1007 char c_c1[2], c_c2[2];
1008
1009 SCM_VALIDATE_CHAR (1, c1);
1010 SCM_VALIDATE_CHAR (2, c2);
1011
1012 c_c1[0] = (char)SCM_CHAR (c1); c_c1[1] = '\0';
1013 c_c2[0] = (char)SCM_CHAR (c2); c_c2[1] = '\0';
1014
1015 return scm_from_bool (compare_strings (c_c1, c_c2, locale, FUNC_NAME) < 0);
1016 }
1017 #undef FUNC_NAME
1018
1019 SCM_DEFINE (scm_char_locale_gt, "char-locale>?", 2, 1, 0,
1020 (SCM c1, SCM c2, SCM locale),
1021 "Return true if character @var{c1} is greater than @var{c2} "
1022 "according to @var{locale} or to the current locale.")
1023 #define FUNC_NAME s_scm_char_locale_gt
1024 {
1025 char c_c1[2], c_c2[2];
1026
1027 SCM_VALIDATE_CHAR (1, c1);
1028 SCM_VALIDATE_CHAR (2, c2);
1029
1030 c_c1[0] = (char)SCM_CHAR (c1); c_c1[1] = '\0';
1031 c_c2[0] = (char)SCM_CHAR (c2); c_c2[1] = '\0';
1032
1033 return scm_from_bool (compare_strings (c_c1, c_c2, locale, FUNC_NAME) > 0);
1034 }
1035 #undef FUNC_NAME
1036
1037 SCM_DEFINE (scm_char_locale_ci_lt, "char-locale-ci<?", 2, 1, 0,
1038 (SCM c1, SCM c2, SCM locale),
1039 "Return true if character @var{c1} is lower than @var{c2}, "
1040 "in a case insensitive way according to @var{locale} or to "
1041 "the current locale.")
1042 #define FUNC_NAME s_scm_char_locale_ci_lt
1043 {
1044 int result;
1045 char c_c1[2], c_c2[2];
1046
1047 SCM_VALIDATE_CHAR (1, c1);
1048 SCM_VALIDATE_CHAR (2, c2);
1049
1050 c_c1[0] = (char)SCM_CHAR (c1); c_c1[1] = '\0';
1051 c_c2[0] = (char)SCM_CHAR (c2); c_c2[1] = '\0';
1052
1053 result = compare_strings_ci (c_c1, c_c2, locale, FUNC_NAME);
1054
1055 return scm_from_bool (result < 0);
1056 }
1057 #undef FUNC_NAME
1058
1059 SCM_DEFINE (scm_char_locale_ci_gt, "char-locale-ci>?", 2, 1, 0,
1060 (SCM c1, SCM c2, SCM locale),
1061 "Return true if character @var{c1} is greater than @var{c2}, "
1062 "in a case insensitive way according to @var{locale} or to "
1063 "the current locale.")
1064 #define FUNC_NAME s_scm_char_locale_ci_gt
1065 {
1066 int result;
1067 char c_c1[2], c_c2[2];
1068
1069 SCM_VALIDATE_CHAR (1, c1);
1070 SCM_VALIDATE_CHAR (2, c2);
1071
1072 c_c1[0] = (char)SCM_CHAR (c1); c_c1[1] = '\0';
1073 c_c2[0] = (char)SCM_CHAR (c2); c_c2[1] = '\0';
1074
1075 result = compare_strings_ci (c_c1, c_c2, locale, FUNC_NAME);
1076
1077 return scm_from_bool (result > 0);
1078 }
1079 #undef FUNC_NAME
1080
1081 SCM_DEFINE (scm_char_locale_ci_eq, "char-locale-ci=?", 2, 1, 0,
1082 (SCM c1, SCM c2, SCM locale),
1083 "Return true if character @var{c1} is equal to @var{c2}, "
1084 "in a case insensitive way according to @var{locale} or to "
1085 "the current locale.")
1086 #define FUNC_NAME s_scm_char_locale_ci_eq
1087 {
1088 int result;
1089 char c_c1[2], c_c2[2];
1090
1091 SCM_VALIDATE_CHAR (1, c1);
1092 SCM_VALIDATE_CHAR (2, c2);
1093
1094 c_c1[0] = (char)SCM_CHAR (c1); c_c1[1] = '\0';
1095 c_c2[0] = (char)SCM_CHAR (c2); c_c2[1] = '\0';
1096
1097 result = compare_strings_ci (c_c1, c_c2, locale, FUNC_NAME);
1098
1099 return scm_from_bool (result == 0);
1100 }
1101 #undef FUNC_NAME
1102
1103
1104 \f
1105 /* Locale-dependent alphabetic character mapping. */
1106
1107 SCM_DEFINE (scm_char_locale_downcase, "char-locale-downcase", 1, 1, 0,
1108 (SCM chr, SCM locale),
1109 "Return the lowercase character that corresponds to @var{chr} "
1110 "according to either @var{locale} or the current locale.")
1111 #define FUNC_NAME s_scm_char_locale_downcase
1112 {
1113 char c_chr;
1114 int c_result;
1115 scm_t_locale c_locale;
1116
1117 SCM_VALIDATE_CHAR (1, chr);
1118 c_chr = SCM_CHAR (chr);
1119
1120 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1121
1122 if (c_locale != NULL)
1123 {
1124 #ifdef USE_GNU_LOCALE_API
1125 c_result = tolower_l ((int) c_chr, c_locale);
1126 #else
1127 RUN_IN_LOCALE_SECTION (c_locale, c_result = tolower ((int) c_chr));
1128 #endif
1129 }
1130 else
1131 c_result = tolower ((int) c_chr);
1132
1133 return (SCM_MAKE_CHAR (c_result));
1134 }
1135 #undef FUNC_NAME
1136
1137 SCM_DEFINE (scm_char_locale_upcase, "char-locale-upcase", 1, 1, 0,
1138 (SCM chr, SCM locale),
1139 "Return the uppercase character that corresponds to @var{chr} "
1140 "according to either @var{locale} or the current locale.")
1141 #define FUNC_NAME s_scm_char_locale_upcase
1142 {
1143 char c_chr;
1144 int c_result;
1145 scm_t_locale c_locale;
1146
1147 SCM_VALIDATE_CHAR (1, chr);
1148 c_chr = SCM_CHAR (chr);
1149
1150 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1151
1152 if (c_locale != NULL)
1153 {
1154 #ifdef USE_GNU_LOCALE_API
1155 c_result = toupper_l ((int) c_chr, c_locale);
1156 #else
1157 RUN_IN_LOCALE_SECTION (c_locale, c_result = toupper ((int) c_chr));
1158 #endif
1159 }
1160 else
1161 c_result = toupper ((int) c_chr);
1162
1163 return (SCM_MAKE_CHAR (c_result));
1164 }
1165 #undef FUNC_NAME
1166
1167 SCM_DEFINE (scm_string_locale_upcase, "string-locale-upcase", 1, 1, 0,
1168 (SCM str, SCM locale),
1169 "Return a new string that is the uppercase version of "
1170 "@var{str} according to either @var{locale} or the current "
1171 "locale.")
1172 #define FUNC_NAME s_scm_string_locale_upcase
1173 {
1174 const char *c_str;
1175 char *c_ustr;
1176 scm_t_locale c_locale;
1177
1178 SCM_VALIDATE_STRING (1, str);
1179 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1180
1181 c_str = scm_i_string_chars (str);
1182 c_ustr = (char *) alloca (strlen (c_str) + 1);
1183
1184 if (c_locale)
1185 {
1186 #ifdef USE_GNU_LOCALE_API
1187 str_upcase_l (c_ustr, c_str, c_locale);
1188 #else
1189 RUN_IN_LOCALE_SECTION (c_locale, str_upcase (c_ustr, c_str));
1190 #endif
1191 }
1192 else
1193 str_upcase (c_ustr, c_str);
1194
1195 scm_remember_upto_here (str);
1196
1197 return (scm_from_locale_string (c_ustr));
1198 }
1199 #undef FUNC_NAME
1200
1201 SCM_DEFINE (scm_string_locale_downcase, "string-locale-downcase", 1, 1, 0,
1202 (SCM str, SCM locale),
1203 "Return a new string that is the down-case version of "
1204 "@var{str} according to either @var{locale} or the current "
1205 "locale.")
1206 #define FUNC_NAME s_scm_string_locale_downcase
1207 {
1208 const char *c_str;
1209 char *c_lstr;
1210 scm_t_locale c_locale;
1211
1212 SCM_VALIDATE_STRING (1, str);
1213 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1214
1215 c_str = scm_i_string_chars (str);
1216 c_lstr = (char *) alloca (strlen (c_str) + 1);
1217
1218 if (c_locale)
1219 {
1220 #ifdef USE_GNU_LOCALE_API
1221 str_downcase_l (c_lstr, c_str, c_locale);
1222 #else
1223 RUN_IN_LOCALE_SECTION (c_locale, str_downcase (c_lstr, c_str));
1224 #endif
1225 }
1226 else
1227 str_downcase (c_lstr, c_str);
1228
1229 scm_remember_upto_here (str);
1230
1231 return (scm_from_locale_string (c_lstr));
1232 }
1233 #undef FUNC_NAME
1234
1235 /* Note: We don't provide mutative versions of `string-locale-(up|down)case'
1236 because, in some languages, a single downcase character maps to a couple
1237 of uppercase characters. Read the SRFI-13 document for a detailed
1238 discussion about this. */
1239
1240
1241 \f
1242 /* Locale-dependent number parsing. */
1243
1244 SCM_DEFINE (scm_locale_string_to_integer, "locale-string->integer",
1245 1, 2, 0, (SCM str, SCM base, SCM locale),
1246 "Convert string @var{str} into an integer according to either "
1247 "@var{locale} (a locale object as returned by @code{make-locale}) "
1248 "or the current process locale. Return two values: an integer "
1249 "(on success) or @code{#f}, and the number of characters read "
1250 "from @var{str} (@code{0} on failure).")
1251 #define FUNC_NAME s_scm_locale_string_to_integer
1252 {
1253 SCM result;
1254 long c_result;
1255 int c_base;
1256 const char *c_str;
1257 char *c_endptr;
1258 scm_t_locale c_locale;
1259
1260 SCM_VALIDATE_STRING (1, str);
1261 c_str = scm_i_string_chars (str);
1262
1263 if (base != SCM_UNDEFINED)
1264 SCM_VALIDATE_INT_COPY (2, base, c_base);
1265 else
1266 c_base = 10;
1267
1268 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (3, locale, c_locale);
1269
1270 if (c_locale != NULL)
1271 {
1272 #ifdef USE_GNU_LOCALE_API
1273 c_result = strtol_l (c_str, &c_endptr, c_base, c_locale);
1274 #else
1275 RUN_IN_LOCALE_SECTION (c_locale,
1276 c_result = strtol (c_str, &c_endptr, c_base));
1277 #endif
1278 }
1279 else
1280 c_result = strtol (c_str, &c_endptr, c_base);
1281
1282 scm_remember_upto_here (str);
1283
1284 if (c_endptr == c_str)
1285 result = SCM_BOOL_F;
1286 else
1287 result = scm_from_long (c_result);
1288
1289 return (scm_values (scm_list_2 (result, scm_from_long (c_endptr - c_str))));
1290 }
1291 #undef FUNC_NAME
1292
1293 SCM_DEFINE (scm_locale_string_to_inexact, "locale-string->inexact",
1294 1, 1, 0, (SCM str, SCM locale),
1295 "Convert string @var{str} into an inexact number according to "
1296 "either @var{locale} (a locale object as returned by "
1297 "@code{make-locale}) or the current process locale. Return "
1298 "two values: an inexact number (on success) or @code{#f}, and "
1299 "the number of characters read from @var{str} (@code{0} on "
1300 "failure).")
1301 #define FUNC_NAME s_scm_locale_string_to_inexact
1302 {
1303 SCM result;
1304 double c_result;
1305 const char *c_str;
1306 char *c_endptr;
1307 scm_t_locale c_locale;
1308
1309 SCM_VALIDATE_STRING (1, str);
1310 c_str = scm_i_string_chars (str);
1311
1312 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1313
1314 if (c_locale != NULL)
1315 {
1316 #ifdef USE_GNU_LOCALE_API
1317 c_result = strtod_l (c_str, &c_endptr, c_locale);
1318 #else
1319 RUN_IN_LOCALE_SECTION (c_locale,
1320 c_result = strtod (c_str, &c_endptr));
1321 #endif
1322 }
1323 else
1324 c_result = strtod (c_str, &c_endptr);
1325
1326 scm_remember_upto_here (str);
1327
1328 if (c_endptr == c_str)
1329 result = SCM_BOOL_F;
1330 else
1331 result = scm_from_double (c_result);
1332
1333 return (scm_values (scm_list_2 (result, scm_from_long (c_endptr - c_str))));
1334 }
1335 #undef FUNC_NAME
1336
1337 \f
1338 /* Language information, aka. `nl_langinfo ()'. */
1339
1340 /* FIXME: Issues related to `nl-langinfo'.
1341
1342 1. The `CODESET' value is not normalized. This is a secondary issue, but
1343 still a practical issue. See
1344 http://www.cl.cam.ac.uk/~mgk25/ucs/norm_charmap.c for codeset
1345 normalization.
1346
1347 2. `nl_langinfo ()' is not available on Windows.
1348
1349 3. `nl_langinfo ()' may return strings encoded in a locale different from
1350 the current one, thereby defeating `scm_from_locale_string ()'.
1351 Example: support the current locale is "Latin-1" and one asks:
1352
1353 (nl-langinfo DAY_1 (make-locale LC_ALL "eo_EO.UTF-8"))
1354
1355 The result will be a UTF-8 string. However, `scm_from_locale_string',
1356 which expects a Latin-1 string, won't be able to make much sense of the
1357 returned string. Thus, we'd need an `scm_from_string ()' variant where
1358 the locale (or charset) is explicitly passed. */
1359
1360
1361 SCM_DEFINE (scm_nl_langinfo, "nl-langinfo", 1, 1, 0,
1362 (SCM item, SCM locale),
1363 "Return a string denoting locale information for @var{item} "
1364 "in the current locale or that specified by @var{locale}. "
1365 "The semantics and arguments are the same as those of the "
1366 "X/Open @code{nl_langinfo} function (@pxref{The Elegant and "
1367 "Fast Way, @code{nl_langinfo},, libc, The GNU C Library "
1368 "Reference Manual}).")
1369 #define FUNC_NAME s_scm_nl_langinfo
1370 {
1371 #ifdef HAVE_NL_LANGINFO
1372 SCM result;
1373 nl_item c_item;
1374 char *c_result;
1375 scm_t_locale c_locale;
1376
1377 SCM_VALIDATE_INT_COPY (2, item, c_item);
1378 SCM_VALIDATE_OPTIONAL_LOCALE_COPY (2, locale, c_locale);
1379
1380 /* Sadly, `nl_langinfo ()' returns a pointer to a static string. According
1381 to SuS v2, that static string may be modified by subsequent calls to
1382 `nl_langinfo ()' as well as by calls to `setlocale ()'. Thus, we must
1383 acquire the locale mutex before doing invoking `nl_langinfo ()'. See
1384 http://opengroup.org/onlinepubs/007908799/xsh/nl_langinfo.html for
1385 details. */
1386
1387 scm_i_pthread_mutex_lock (&scm_i_locale_mutex);
1388 if (c_locale != NULL)
1389 {
1390 #ifdef USE_GNU_LOCALE_API
1391 c_result = nl_langinfo_l (c_item, c_locale);
1392 #else
1393 /* We can't use `RUN_IN_LOCALE_SECTION ()' here because the locale
1394 mutex is already taken. */
1395 int lsec_err;
1396 scm_t_locale_settings lsec_prev_locale;
1397
1398 lsec_err = get_current_locale_settings (&lsec_prev_locale);
1399 if (lsec_err)
1400 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
1401 else
1402 {
1403 lsec_err = install_locale (c_locale);
1404 if (lsec_err)
1405 {
1406 leave_locale_section (&lsec_prev_locale);
1407 free_locale_settings (&lsec_prev_locale);
1408 }
1409 }
1410
1411 if (lsec_err)
1412 scm_locale_error (FUNC_NAME, lsec_err);
1413 else
1414 {
1415 c_result = nl_langinfo (c_item);
1416
1417 restore_locale_settings (&lsec_prev_locale);
1418 free_locale_settings (&lsec_prev_locale);
1419 }
1420 #endif
1421 }
1422 else
1423 c_result = nl_langinfo (c_item);
1424
1425 c_result = strdup (c_result);
1426 scm_i_pthread_mutex_unlock (&scm_i_locale_mutex);
1427
1428 if (c_result == NULL)
1429 result = SCM_BOOL_F;
1430 else
1431 {
1432 switch (c_item)
1433 {
1434 #if (defined GROUPING) && (defined MON_GROUPING)
1435 case GROUPING:
1436 case MON_GROUPING:
1437 {
1438 char *p;
1439
1440 /* In this cases, the result is to be interpreted as a list of
1441 numbers. If the last item is `CHARS_MAX', it has the special
1442 meaning "no more grouping". */
1443 result = SCM_EOL;
1444 for (p = c_result; (*p != '\0') && (*p != CHAR_MAX); p++)
1445 result = scm_cons (SCM_I_MAKINUM ((int) *p), result);
1446
1447 {
1448 SCM last_pair = result;
1449
1450 result = scm_reverse_x (result, SCM_EOL);
1451
1452 if (*p != CHAR_MAX)
1453 {
1454 /* Cyclic grouping information. */
1455 if (last_pair != SCM_EOL)
1456 SCM_SETCDR (last_pair, result);
1457 }
1458 }
1459
1460 free (c_result);
1461 break;
1462 }
1463 #endif
1464
1465 #if (defined FRAC_DIGITS) && (defined INT_FRAC_DIGITS)
1466 case FRAC_DIGITS:
1467 case INT_FRAC_DIGITS:
1468 /* This is to be interpreted as a single integer. */
1469 if (*c_result == CHAR_MAX)
1470 /* Unspecified. */
1471 result = SCM_BOOL_F;
1472 else
1473 result = SCM_I_MAKINUM (*c_result);
1474
1475 free (c_result);
1476 break;
1477 #endif
1478
1479 #if (defined P_CS_PRECEDES) && (defined INT_N_CS_PRECEDES)
1480 case P_CS_PRECEDES:
1481 case N_CS_PRECEDES:
1482 case INT_P_CS_PRECEDES:
1483 case INT_N_CS_PRECEDES:
1484 #if (defined P_SEP_BY_SPACE) && (defined N_SEP_BY_SPACE)
1485 case P_SEP_BY_SPACE:
1486 case N_SEP_BY_SPACE:
1487 #endif
1488 /* This is to be interpreted as a boolean. */
1489 result = scm_from_bool (*c_result);
1490
1491 free (c_result);
1492 break;
1493 #endif
1494
1495 #if (defined P_SIGN_POSN) && (defined INT_N_SIGN_POSN)
1496 case P_SIGN_POSN:
1497 case N_SIGN_POSN:
1498 case INT_P_SIGN_POSN:
1499 case INT_N_SIGN_POSN:
1500 /* See `(libc) Sign of Money Amount' for the interpretation of the
1501 return value here. */
1502 switch (*c_result)
1503 {
1504 case 0:
1505 result = scm_from_locale_symbol ("parenthesize");
1506 break;
1507
1508 case 1:
1509 result = scm_from_locale_symbol ("sign-before");
1510 break;
1511
1512 case 2:
1513 result = scm_from_locale_symbol ("sign-after");
1514 break;
1515
1516 case 3:
1517 result = scm_from_locale_symbol ("sign-before-currency-symbol");
1518 break;
1519
1520 case 4:
1521 result = scm_from_locale_symbol ("sign-after-currency-symbol");
1522 break;
1523
1524 default:
1525 result = scm_from_locale_symbol ("unspecified");
1526 }
1527 break;
1528 #endif
1529
1530 default:
1531 /* FIXME: `locale_string ()' is not appropriate here because of
1532 encoding issues (see comment above). */
1533 result = scm_take_locale_string (c_result);
1534 }
1535 }
1536
1537 return result;
1538 #else
1539 scm_syserror_msg (FUNC_NAME, "`nl-langinfo' not supported on your system",
1540 SCM_EOL, ENOSYS);
1541
1542 return SCM_BOOL_F;
1543 #endif
1544 }
1545 #undef FUNC_NAME
1546
1547 /* Define the `nl_item' constants. */
1548 static inline void
1549 define_langinfo_items (void)
1550 {
1551 #if (defined HAVE_NL_TYPES_H) && (defined HAVE_LANGINFO_H)
1552
1553 #define DEFINE_NLITEM_CONSTANT(_item) \
1554 scm_c_define (# _item, scm_from_int (_item))
1555
1556 DEFINE_NLITEM_CONSTANT (CODESET);
1557
1558 /* Abbreviated days of the week. */
1559 DEFINE_NLITEM_CONSTANT (ABDAY_1);
1560 DEFINE_NLITEM_CONSTANT (ABDAY_2);
1561 DEFINE_NLITEM_CONSTANT (ABDAY_3);
1562 DEFINE_NLITEM_CONSTANT (ABDAY_4);
1563 DEFINE_NLITEM_CONSTANT (ABDAY_5);
1564 DEFINE_NLITEM_CONSTANT (ABDAY_6);
1565 DEFINE_NLITEM_CONSTANT (ABDAY_7);
1566
1567 /* Long-named days of the week. */
1568 DEFINE_NLITEM_CONSTANT (DAY_1); /* Sunday */
1569 DEFINE_NLITEM_CONSTANT (DAY_2); /* Monday */
1570 DEFINE_NLITEM_CONSTANT (DAY_3); /* Tuesday */
1571 DEFINE_NLITEM_CONSTANT (DAY_4); /* Wednesday */
1572 DEFINE_NLITEM_CONSTANT (DAY_5); /* Thursday */
1573 DEFINE_NLITEM_CONSTANT (DAY_6); /* Friday */
1574 DEFINE_NLITEM_CONSTANT (DAY_7); /* Saturday */
1575
1576 /* Abbreviated month names. */
1577 DEFINE_NLITEM_CONSTANT (ABMON_1); /* Jan */
1578 DEFINE_NLITEM_CONSTANT (ABMON_2);
1579 DEFINE_NLITEM_CONSTANT (ABMON_3);
1580 DEFINE_NLITEM_CONSTANT (ABMON_4);
1581 DEFINE_NLITEM_CONSTANT (ABMON_5);
1582 DEFINE_NLITEM_CONSTANT (ABMON_6);
1583 DEFINE_NLITEM_CONSTANT (ABMON_7);
1584 DEFINE_NLITEM_CONSTANT (ABMON_8);
1585 DEFINE_NLITEM_CONSTANT (ABMON_9);
1586 DEFINE_NLITEM_CONSTANT (ABMON_10);
1587 DEFINE_NLITEM_CONSTANT (ABMON_11);
1588 DEFINE_NLITEM_CONSTANT (ABMON_12);
1589
1590 /* Long month names. */
1591 DEFINE_NLITEM_CONSTANT (MON_1); /* January */
1592 DEFINE_NLITEM_CONSTANT (MON_2);
1593 DEFINE_NLITEM_CONSTANT (MON_3);
1594 DEFINE_NLITEM_CONSTANT (MON_4);
1595 DEFINE_NLITEM_CONSTANT (MON_5);
1596 DEFINE_NLITEM_CONSTANT (MON_6);
1597 DEFINE_NLITEM_CONSTANT (MON_7);
1598 DEFINE_NLITEM_CONSTANT (MON_8);
1599 DEFINE_NLITEM_CONSTANT (MON_9);
1600 DEFINE_NLITEM_CONSTANT (MON_10);
1601 DEFINE_NLITEM_CONSTANT (MON_11);
1602 DEFINE_NLITEM_CONSTANT (MON_12);
1603
1604 DEFINE_NLITEM_CONSTANT (AM_STR); /* Ante meridiem string. */
1605 DEFINE_NLITEM_CONSTANT (PM_STR); /* Post meridiem string. */
1606
1607 DEFINE_NLITEM_CONSTANT (D_T_FMT); /* Date and time format for strftime. */
1608 DEFINE_NLITEM_CONSTANT (D_FMT); /* Date format for strftime. */
1609 DEFINE_NLITEM_CONSTANT (T_FMT); /* Time format for strftime. */
1610 DEFINE_NLITEM_CONSTANT (T_FMT_AMPM);/* 12-hour time format for strftime. */
1611
1612 DEFINE_NLITEM_CONSTANT (ERA); /* Alternate era. */
1613 DEFINE_NLITEM_CONSTANT (ERA_D_FMT); /* Date in alternate era format. */
1614 DEFINE_NLITEM_CONSTANT (ERA_D_T_FMT); /* Date and time in alternate era
1615 format. */
1616 DEFINE_NLITEM_CONSTANT (ERA_T_FMT); /* Time in alternate era format. */
1617
1618 DEFINE_NLITEM_CONSTANT (ALT_DIGITS); /* Alternate symbols for digits. */
1619 DEFINE_NLITEM_CONSTANT (RADIXCHAR);
1620 DEFINE_NLITEM_CONSTANT (THOUSEP);
1621
1622 #ifdef YESEXPR
1623 DEFINE_NLITEM_CONSTANT (YESEXPR);
1624 #endif
1625 #ifdef NOEXPR
1626 DEFINE_NLITEM_CONSTANT (NOEXPR);
1627 #endif
1628
1629 #ifdef CRNCYSTR /* currency symbol */
1630 DEFINE_NLITEM_CONSTANT (CRNCYSTR);
1631 #endif
1632
1633 /* GNU extensions. */
1634
1635 #ifdef ERA_YEAR
1636 DEFINE_NLITEM_CONSTANT (ERA_YEAR); /* Year in alternate era format. */
1637 #endif
1638
1639 /* LC_MONETARY category: formatting of monetary quantities.
1640 These items each correspond to a member of `struct lconv',
1641 defined in <locale.h>. */
1642 #ifdef INT_CURR_SYMBOL
1643 DEFINE_NLITEM_CONSTANT (INT_CURR_SYMBOL);
1644 #endif
1645 #ifdef MON_DECIMAL_POINT
1646 DEFINE_NLITEM_CONSTANT (MON_DECIMAL_POINT);
1647 #endif
1648 #ifdef MON_THOUSANDS_SEP
1649 DEFINE_NLITEM_CONSTANT (MON_THOUSANDS_SEP);
1650 #endif
1651 #ifdef MON_GROUPING
1652 DEFINE_NLITEM_CONSTANT (MON_GROUPING);
1653 #endif
1654 #ifdef POSITIVE_SIGN
1655 DEFINE_NLITEM_CONSTANT (POSITIVE_SIGN);
1656 #endif
1657 #ifdef NEGATIVE_SIGN
1658 DEFINE_NLITEM_CONSTANT (NEGATIVE_SIGN);
1659 #endif
1660 #ifdef GROUPING
1661 DEFINE_NLITEM_CONSTANT (GROUPING);
1662 #endif
1663 #ifdef INT_FRAC_DIGITS
1664 DEFINE_NLITEM_CONSTANT (INT_FRAC_DIGITS);
1665 #endif
1666 #ifdef FRAC_DIGITS
1667 DEFINE_NLITEM_CONSTANT (FRAC_DIGITS);
1668 #endif
1669 #ifdef P_CS_PRECEDES
1670 DEFINE_NLITEM_CONSTANT (P_CS_PRECEDES);
1671 #endif
1672 #ifdef P_SEP_BY_SPACE
1673 DEFINE_NLITEM_CONSTANT (P_SEP_BY_SPACE);
1674 #endif
1675 #ifdef N_CS_PRECEDES
1676 DEFINE_NLITEM_CONSTANT (N_CS_PRECEDES);
1677 #endif
1678 #ifdef N_SEP_BY_SPACE
1679 DEFINE_NLITEM_CONSTANT (N_SEP_BY_SPACE);
1680 #endif
1681 #ifdef P_SIGN_POSN
1682 DEFINE_NLITEM_CONSTANT (P_SIGN_POSN);
1683 #endif
1684 #ifdef N_SIGN_POSN
1685 DEFINE_NLITEM_CONSTANT (N_SIGN_POSN);
1686 #endif
1687 #ifdef INT_P_CS_PRECEDES
1688 DEFINE_NLITEM_CONSTANT (INT_P_CS_PRECEDES);
1689 #endif
1690 #ifdef INT_P_SEP_BY_SPACE
1691 DEFINE_NLITEM_CONSTANT (INT_P_SEP_BY_SPACE);
1692 #endif
1693 #ifdef INT_N_CS_PRECEDES
1694 DEFINE_NLITEM_CONSTANT (INT_N_CS_PRECEDES);
1695 #endif
1696 #ifdef INT_N_SEP_BY_SPACE
1697 DEFINE_NLITEM_CONSTANT (INT_N_SEP_BY_SPACE);
1698 #endif
1699 #ifdef INT_P_SIGN_POSN
1700 DEFINE_NLITEM_CONSTANT (INT_P_SIGN_POSN);
1701 #endif
1702 #ifdef INT_N_SIGN_POSN
1703 DEFINE_NLITEM_CONSTANT (INT_N_SIGN_POSN);
1704 #endif
1705
1706 #undef DEFINE_NLITEM_CONSTANT
1707
1708 #endif /* HAVE_NL_TYPES_H */
1709 }
1710
1711 \f
1712 void
1713 scm_init_i18n ()
1714 {
1715 SCM global_locale_smob;
1716
1717 #ifdef HAVE_NL_LANGINFO
1718 scm_add_feature ("nl-langinfo");
1719 define_langinfo_items ();
1720 #endif
1721
1722 #include "libguile/i18n.x"
1723
1724 #ifndef USE_GNU_LOCALE_API
1725 scm_set_smob_mark (scm_tc16_locale_smob_type, smob_locale_mark);
1726 #endif
1727
1728 /* Initialize the global locale object with a special `locale' SMOB. */
1729 SCM_NEWSMOB (global_locale_smob, scm_tc16_locale_smob_type, NULL);
1730 SCM_VARIABLE_SET (scm_global_locale, global_locale_smob);
1731 }
1732
1733
1734 /*
1735 Local Variables:
1736 c-file-style: "gnu"
1737 End:
1738 */