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