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