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