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