1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
36 #include "linereader.h"
37 #include "localeinfo.h"
41 /* Uncomment the following line in the production version. */
42 /* #define NDEBUG 1 */
45 /* Define the lookup function. */
46 #include "locfile-kw.h"
49 /* Some useful macros. */
50 #define MIN(a, b) (__extension__ ({ typeof (a) _a = (a); \
51 typeof (b) _b = (b); \
52 _a < _b ? _a : _b; }))
55 void *xmalloc (size_t __n
);
56 char *xstrdup (const char *__str
);
59 locfile_read (const char *filename
, struct charset_t
*charset
)
61 struct linereader
*ldfile
;
62 struct localedef_t
*result
;
64 enum token_t expected_tok
= tok_none
;
65 const char *expected_str
= NULL
;
66 enum token_t ctype_tok_sym
= tok_none
;
67 const char *ctype_tok_str
= NULL
;
68 int copy_category
= 0;
71 /* Allocate space for result. */
72 result
= (struct localedef_t
*) xmalloc (sizeof (struct localedef_t
));
73 memset (result
, '\0', sizeof (struct localedef_t
));
75 ldfile
= lr_open (filename
, locfile_hash
);
78 if (filename
[0] != '/')
80 char *i18npath
= __secure_getenv ("I18NPATH");
81 if (i18npath
!= NULL
&& *i18npath
!= '\0')
83 char path
[strlen (filename
) + 1 + strlen (i18npath
) + 1];
85 i18npath
= strdupa (i18npath
);
89 && (next
= strsep (&i18npath
, ":")) != NULL
)
91 stpcpy (stpcpy (stpcpy (path
, next
), "/"), filename
);
93 ldfile
= lr_open (path
, locfile_hash
);
97 /* Test in the default directory. */
100 char path
[strlen (filename
) + 1 + sizeof (LOCSRCDIR
)];
102 stpcpy (stpcpy (stpcpy (path
, LOCSRCDIR
), "/"), filename
);
103 ldfile
= lr_open (path
, locfile_hash
);
114 #define HANDLE_COPY(category, token, string) \
115 if (nowtok == tok_copy) \
117 copy_category = category; \
118 expected_tok = token; \
119 expected_str = string; \
125 #define LOCALE_PROLOG(token, string) \
126 if (nowtok == tok_eol) \
127 /* Ignore empty lines. */ \
129 if (nowtok == tok_end) \
131 expected_tok = token; \
132 expected_str = string; \
136 if (nowtok == tok_copy) \
140 #define READ_STRING(fn, errlabel) \
143 arg = lr_token (ldfile, charset); \
144 if (arg->tok != tok_string) \
146 fn (ldfile, result, nowtok, arg, charset); \
147 lr_ignore_rest (ldfile, 1); \
151 #define READ_STRING_LIST(fn, errlabel) \
154 arg = lr_token (ldfile, charset); \
155 while (arg->tok == tok_string) \
157 fn (ldfile, result, nowtok, arg, charset); \
158 arg = lr_token (ldfile, charset); \
159 if (arg->tok != tok_semicolon) \
161 arg = lr_token (ldfile, charset); \
163 if (arg->tok != tok_eol) \
168 #define READ_NUMBER(fn, errlabel) \
171 arg = lr_token (ldfile, charset); \
172 if (arg->tok != tok_minus1 && arg->tok != tok_number) \
174 fn (ldfile, result, nowtok, arg, charset); \
175 lr_ignore_rest (ldfile, 1); \
179 #define READ_NUMBER_LIST(fn, errlabel) \
182 arg = lr_token (ldfile, charset); \
183 while (arg->tok == tok_minus1 || arg->tok == tok_number) \
185 fn (ldfile, result, nowtok, arg, charset); \
186 arg = lr_token (ldfile, charset); \
187 if (arg->tok != tok_semicolon) \
189 arg = lr_token (ldfile, charset); \
191 if (arg->tok != tok_eol) \
196 #define SYNTAX_ERROR(string) \
197 lr_error (ldfile, string); \
198 lr_ignore_rest (ldfile, 0);
201 /* Parse locale definition file and store result in RESULT. */
206 struct token
*now
= lr_token (ldfile
, charset
);
207 enum token_t nowtok
= now
->tok
;
210 if (nowtok
== tok_eof
)
216 /* The beginning. We expect the special declarations, EOL or
217 the start of any locale. */
218 if (nowtok
== tok_eol
)
219 /* Ignore empty lines. */
224 case tok_escape_char
:
225 case tok_comment_char
:
226 /* We need an argument. */
227 arg
= lr_token (ldfile
, charset
);
229 if (arg
->tok
!= tok_ident
)
231 SYNTAX_ERROR (_("bad argument"));
235 if (arg
->val
.str
.len
!= 1)
237 lr_error (ldfile
, _("\
238 argument to `%s' must be a single character"),
239 nowtok
== tok_escape_char
? "escape_char"
242 lr_ignore_rest (ldfile
, 0);
246 if (nowtok
== tok_escape_char
)
247 ldfile
->escape_char
= *arg
->val
.str
.start
;
249 ldfile
->comment_char
= *arg
->val
.str
.start
;
260 case tok_lc_monetary
:
272 case tok_lc_messages
:
278 syntax error: not inside a locale definition section"));
281 lr_ignore_rest (ldfile
, 1);
285 HANDLE_COPY (LC_CTYPE
, tok_lc_ctype
, "LC_CYTPE");
287 ctype_startup (ldfile
, result
, charset
);
291 /* Here we accept all the character classes, tolower/toupper,
292 and following ANSI C:1995 self-defined classes. */
293 LOCALE_PROLOG (tok_lc_ctype
, "LC_CTYPE");
295 if (nowtok
== tok_charclass
)
297 READ_STRING_LIST (ctype_class_new
, bad_new_charclass
);
301 syntax error in definition of new character class"));
305 if (nowtok
== tok_charconv
)
307 READ_STRING_LIST (ctype_map_new
, bad_new_charconv
);
311 syntax error in definition of new character map"));
315 if (nowtok
== tok_upper
|| nowtok
== tok_lower
316 || nowtok
== tok_alpha
|| nowtok
== tok_digit
317 || nowtok
== tok_alnum
|| nowtok
== tok_space
318 || nowtok
== tok_cntrl
|| nowtok
== tok_punct
319 || nowtok
== tok_graph
|| nowtok
== tok_print
320 || nowtok
== tok_xdigit
|| nowtok
== tok_blank
)
322 ctype_tok_sym
= nowtok
;
323 ctype_tok_str
= NULL
;
328 if (nowtok
== tok_toupper
|| nowtok
== tok_tolower
)
330 ctype_tok_sym
= nowtok
;
331 ctype_tok_str
= NULL
;
336 if (nowtok
!= tok_ident
)
339 /* We possibly have a self-defined character class. */
340 if (ctype_is_charclass (ldfile
, result
, now
->val
.str
.start
))
342 ctype_tok_sym
= nowtok
;
343 ctype_tok_str
= now
->val
.str
.start
;
348 /* ...or a self-defined character map. */
349 if (ctype_is_charconv (ldfile
, result
, now
->val
.str
.start
))
351 ctype_tok_sym
= nowtok
;
352 ctype_tok_str
= now
->val
.str
.start
;
357 SYNTAX_ERROR (_("syntax error in definition of LC_CTYPE category"));
361 /* Handle `END xxx'. */
362 if (nowtok
!= expected_tok
)
363 lr_error (ldfile
, _("\
364 `%1$s' definition does not end with `END %1$s'"), expected_str
);
366 lr_ignore_rest (ldfile
, nowtok
== expected_tok
);
371 /* Here we expect a semicolon separated list of bsymbols. The
372 bit to be set in the word is given in CHARCLASS_BIT. */
375 ctype_class_start (ldfile
, result
, ctype_tok_sym
, ctype_tok_str
,
378 while (arg
->tok
!= tok_eol
)
380 /* Any token other than a bsymbol is an error. */
381 if (arg
->tok
!= tok_bsymbol
)
385 syntax error in character class definition"));
389 /* Lookup value for token and write into array. */
390 ctype_class_from (ldfile
, result
, arg
, charset
);
392 arg
= lr_token (ldfile
, charset
);
393 if (arg
->tok
== tok_semicolon
)
394 arg
= lr_token (ldfile
, charset
);
395 else if (arg
->tok
!= tok_eol
)
398 /* Look for ellipsis. */
399 if (arg
->tok
== tok_ellipsis
)
401 arg
= lr_token (ldfile
, charset
);
402 if (arg
->tok
!= tok_semicolon
)
405 arg
= lr_token (ldfile
, charset
);
406 if (arg
->tok
!= tok_bsymbol
)
409 /* Write range starting at LAST to ARG->VAL. */
410 ctype_class_to (ldfile
, result
, arg
, charset
);
412 arg
= lr_token (ldfile
, charset
);
413 if (arg
->tok
== tok_semicolon
)
414 arg
= lr_token (ldfile
, charset
);
415 else if (arg
->tok
!= tok_eol
)
420 /* Mark class as already seen. */
421 ctype_class_end (ldfile
, result
);
427 /* Here we expect a list of character mappings. Note: the
428 first opening brace is already matched. */
429 ctype_map_start (ldfile
, result
, ctype_tok_sym
, ctype_tok_str
,
434 /* Match ( bsymbol , bsymbol ) */
435 if (now
->tok
!= tok_open_brace
)
438 now
= lr_token (ldfile
, charset
);
439 if (now
->tok
!= tok_bsymbol
)
443 syntax error in character conversion definition"));
448 /* Lookup arg and assign to FROM. */
449 ctype_map_from (ldfile
, result
, now
, charset
);
451 now
= lr_token (ldfile
, charset
);
452 if (now
->tok
!= tok_comma
)
455 now
= lr_token (ldfile
, charset
);
456 if (now
->tok
!= tok_bsymbol
)
459 /* Lookup arg and assign to TO. */
460 ctype_map_to (ldfile
, result
, now
, charset
);
462 now
= lr_token (ldfile
, charset
);
463 if (now
->tok
!= tok_close_brace
)
466 now
= lr_token (ldfile
, charset
);
467 if (now
->tok
== tok_eol
)
472 if (now
->tok
!= tok_semicolon
)
475 now
= lr_token (ldfile
, charset
);
478 ctype_map_end (ldfile
, result
);
483 /* We have seen `copy'. First match the argument. */
486 if (nowtok
!= tok_string
)
487 lr_error (ldfile
, _("expect string argument for `copy'"));
489 def_to_process (now
->val
.str
.start
, 1 << copy_category
);
491 lr_ignore_rest (ldfile
, nowtok
== tok_string
);
493 /* The rest of the line must be empty
494 and the next keyword must be `END xxx'. */
496 while (lr_token (ldfile
, charset
)->tok
!= tok_end
)
501 lr_error (ldfile
, _("\
502 no other keyword shall be specified when `copy' is used"));
506 lr_ignore_rest (ldfile
, 0);
514 HANDLE_COPY (LC_COLLATE
, tok_lc_collate
, "LC_COLLATE");
516 collate_startup (ldfile
, result
, charset
);
520 /* Process the LC_COLLATE section. We expect `END LC_COLLATE'
521 any of the collation specifications, or any bsymbol. */
522 LOCALE_PROLOG (tok_lc_collate
, "LC_COLLATE");
524 if (nowtok
== tok_order_start
)
530 if (nowtok
!= tok_collating_element
531 && nowtok
!= tok_collating_symbol
)
534 lr_error (ldfile
, _("\
535 syntax error in collation definition"));
536 lr_ignore_rest (ldfile
, 0);
541 arg
= lr_token (ldfile
, charset
);
542 if (arg
->tok
!= tok_bsymbol
)
544 lr_error (ldfile
, _("\
545 collation symbol expected after `%s'"),
546 nowtok
== tok_collating_element
547 ? "collating-element" : "collating-symbol");
548 lr_ignore_rest (ldfile
, 0);
552 if (nowtok
== tok_collating_element
)
554 /* Save to-value as new name. */
555 collate_element_to (ldfile
, result
, arg
, charset
);
557 arg
= lr_token (ldfile
, charset
);
558 if (arg
->tok
!= tok_from
)
560 lr_error (ldfile
, _("\
561 `from' expected after first argument to `collating-element'"));
562 lr_ignore_rest (ldfile
, 0);
566 arg
= lr_token (ldfile
, charset
);
567 if (arg
->tok
!= tok_string
)
569 lr_error (ldfile
, _("\
570 from-value of `collating-element' must be a string"));
571 lr_ignore_rest (ldfile
, 0);
575 /* Enter new collating element. */
576 collate_element_from (ldfile
, result
, arg
, charset
);
579 /* Enter new collating symbol into table. */
580 collate_symbol (ldfile
, result
, arg
, charset
);
582 lr_ignore_rest (ldfile
, 1);
586 /* We parse the rest of the line containing `order_start'.
587 In any case we continue with parsing the symbols. */
591 while (now
->tok
!= tok_eol
)
593 int collation_method
= 0;
599 if (now
->tok
== tok_forward
)
600 collation_method
|= sort_forward
;
601 else if (now
->tok
== tok_backward
)
602 collation_method
|= sort_backward
;
603 else if (now
->tok
== tok_position
)
604 collation_method
|= sort_position
;
607 lr_error (ldfile
, _("unknown collation directive"));
608 lr_ignore_rest (ldfile
, 0);
612 now
= lr_token (ldfile
, charset
);
614 while (now
->tok
== tok_comma
615 && ((now
= lr_token (ldfile
, charset
)) != tok_none
));
617 /* Check for consistency: forward and backwards are
618 mutually exclusive. */
619 if ((collation_method
& sort_forward
) != 0
620 && (collation_method
& sort_backward
) != 0)
622 lr_error (ldfile
, _("\
623 sorting order `forward' and `backward' are mutually exclusive"));
624 /* The recover clear the backward flag. */
625 collation_method
&= ~sort_backward
;
628 /* ??? I don't know whether this is correct but while
629 thinking about the `strcoll' functions I found that I
630 need a direction when performing position depended
631 collation. So I assume here that implicitly the
632 direction `forward' is given when `position' alone is
633 written. --drepper */
634 if (collation_method
== sort_position
)
635 collation_method
|= sort_forward
;
637 /* Enter info about next collation order. */
638 collate_new_order (ldfile
, result
, collation_method
);
640 if (now
->tok
!= tok_eol
&& now
->tok
!= tok_semicolon
)
642 lr_error (ldfile
, _("\
643 syntax error in `order_start' directive"));
644 lr_ignore_rest (ldfile
, 0);
648 if (now
->tok
== tok_semicolon
)
649 now
= lr_token (ldfile
, charset
);
652 /* If no argument to `order_start' is given, one `forward'
653 argument is implicitly assumed. */
655 collate_new_order (ldfile
, result
, sort_forward
);
658 /* We now know about all sorting rules. */
659 collate_build_arrays (ldfile
, result
);
664 /* We read one symbol a line until `order_end' is found. */
666 static int last_correct
= 1;
668 if (nowtok
== tok_order_end
)
671 lr_ignore_rest (ldfile
, 1);
675 /* Ignore empty lines. */
676 if (nowtok
== tok_eol
)
679 if (nowtok
!= tok_bsymbol
&& nowtok
!= tok_undefined
680 && nowtok
!= tok_ellipsis
)
682 if (last_correct
== 1)
684 lr_error (ldfile
, _("\
685 syntax error in collating order definition"));
688 lr_ignore_rest (ldfile
, 0);
695 /* Remember current token. */
696 if (collate_order_elem (ldfile
, result
, now
, charset
) < 0)
700 /* Read optional arguments. */
701 arg
= lr_token (ldfile
, charset
);
702 while (arg
->tok
!= tok_eol
)
704 if (arg
->tok
!= tok_ignore
&& arg
->tok
!= tok_ellipsis
705 && arg
->tok
!= tok_bsymbol
&& arg
->tok
!= tok_string
)
708 if (arg
->tok
== tok_ignore
|| arg
->tok
== tok_ellipsis
709 || arg
->tok
== tok_string
)
711 /* Call handler for simple weights. */
712 if (collate_simple_weight (ldfile
, result
, arg
, charset
)
716 arg
= lr_token (ldfile
, charset
);
722 int ok
= collate_weight_bsymbol (ldfile
, result
, arg
,
727 arg
= lr_token (ldfile
, charset
);
729 while (arg
->tok
== tok_bsymbol
);
731 /* Are there more weights? */
732 if (arg
->tok
!= tok_semicolon
)
735 /* Yes, prepare next weight. */
736 if (collate_next_weight (ldfile
, result
) < 0)
739 arg
= lr_token (ldfile
, charset
);
742 if (arg
->tok
!= tok_eol
)
744 SYNTAX_ERROR (_("syntax error in order specification"));
747 collate_end_weight (ldfile
, result
);
753 /* Following to the `order_end' keyword we don't expect
754 anything but the `END'. */
755 if (nowtok
== tok_eol
)
758 if (nowtok
!= tok_end
)
761 expected_tok
= tok_lc_collate
;
762 expected_str
= "LC_COLLATE";
765 ldfile
->translate_strings
= 1;
769 HANDLE_COPY (LC_MONETARY
, tok_lc_monetary
, "LC_MONETARY");
771 monetary_startup (ldfile
, result
, charset
);
775 LOCALE_PROLOG (tok_lc_monetary
, "LC_MONETARY");
779 case tok_int_curr_symbol
:
780 case tok_currency_symbol
:
781 case tok_mon_decimal_point
:
782 case tok_mon_thousands_sep
:
783 case tok_positive_sign
:
784 case tok_negative_sign
:
785 READ_STRING (monetary_add
, bad_monetary
);
788 case tok_int_frac_digits
:
789 case tok_frac_digits
:
790 case tok_p_cs_precedes
:
791 case tok_p_sep_by_space
:
792 case tok_n_cs_precedes
:
793 case tok_n_sep_by_space
:
794 case tok_p_sign_posn
:
795 case tok_n_sign_posn
:
796 READ_NUMBER (monetary_add
, bad_monetary
);
799 case tok_mon_grouping
:
800 /* We have a semicolon separated list of integers. */
801 READ_NUMBER_LIST (monetary_add
, bad_monetary
);
806 SYNTAX_ERROR (_("syntax error in monetary locale definition"));
811 HANDLE_COPY (LC_NUMERIC
, tok_lc_numeric
, "LC_NUMERIC");
813 numeric_startup (ldfile
, result
, charset
);
817 LOCALE_PROLOG (tok_lc_numeric
, "LC_NUMERIC");
821 case tok_decimal_point
:
822 case tok_thousands_sep
:
823 READ_STRING (numeric_add
, bad_numeric
);
827 /* We have a semicolon separated list of integers. */
828 READ_NUMBER_LIST (numeric_add
, bad_numeric
);
833 SYNTAX_ERROR (_("syntax error in numeric locale definition"));
838 HANDLE_COPY (LC_TIME
, tok_lc_time
, "LC_TIME");
840 time_startup (ldfile
, result
, charset
);
844 LOCALE_PROLOG (tok_lc_time
, "LC_TIME");
855 READ_STRING_LIST (time_add
, bad_time
);
863 case tok_era_d_t_fmt
:
866 READ_STRING (time_add
, bad_time
);
871 SYNTAX_ERROR (_("syntax error in time locale definition"));
876 HANDLE_COPY (LC_MESSAGES
, tok_lc_messages
, "LC_MESSAGES");
878 messages_startup (ldfile
, result
, charset
);
882 LOCALE_PROLOG (tok_lc_messages
, "LC_MESSAGES");
890 READ_STRING (messages_add
, bad_message
);
895 SYNTAX_ERROR (_("syntax error in message locale definition"));
900 error (5, 0, _("%s: error in state machine"), __FILE__
);
907 /* We read all of the file. */
910 /* Let's see what information is available. */
911 for (cnt
= LC_CTYPE
; cnt
<= LC_MESSAGES
; ++cnt
)
912 if (result
->categories
[cnt
].generic
!= NULL
)
913 result
->avail
|= 1 << cnt
;
920 check_all_categories (struct localedef_t
*locale
, struct charset_t
*charset
)
922 /* Call the finishing functions for all locales. */
923 if ((locale
->avail
& (1 << LC_CTYPE
)) != 0
924 && (locale
->binary
& (1 << LC_CTYPE
)) == 0)
925 ctype_finish (locale
, charset
);
926 if ((locale
->avail
& (1 << LC_COLLATE
)) != 0
927 && (locale
->binary
& (1 << LC_COLLATE
)) == 0)
928 collate_finish (locale
, charset
);
929 if ((locale
->avail
& (1 << LC_MONETARY
)) != 0
930 && (locale
->binary
& (1 << LC_MONETARY
)) == 0)
931 monetary_finish (locale
);
932 if ((locale
->avail
& (1 << LC_NUMERIC
)) != 0
933 && (locale
->binary
& (1 << LC_NUMERIC
)) == 0)
934 numeric_finish (locale
);
935 if ((locale
->avail
& (1 << LC_TIME
)) != 0
936 && (locale
->binary
& (1 << LC_TIME
)) == 0)
937 time_finish (locale
);
938 if ((locale
->avail
& (1 << LC_MESSAGES
)) != 0
939 && (locale
->binary
& (1 << LC_MESSAGES
)) == 0)
940 messages_finish (locale
);
945 write_all_categories (struct localedef_t
*locale
, struct charset_t
*charset
,
946 const char *output_path
)
948 /* Call all functions to write locale data. */
949 if ((locale
->avail
& (1 << LC_CTYPE
)) != 0)
950 ctype_output (locale
, charset
, output_path
);
951 if ((locale
->avail
& (1 << LC_COLLATE
)) != 0)
952 collate_output (locale
, charset
, output_path
);
953 if ((locale
->avail
& (1 << LC_MONETARY
)) != 0)
954 monetary_output (locale
, output_path
);
955 if ((locale
->avail
& (1 << LC_NUMERIC
)) != 0)
956 numeric_output (locale
, output_path
);
957 if ((locale
->avail
& (1 << LC_TIME
)) != 0)
958 time_output (locale
, output_path
);
959 if ((locale
->avail
& (1 << LC_MESSAGES
)) != 0)
960 messages_output (locale
, output_path
);
965 write_locale_data (const char *output_path
, const char *category
,
966 size_t n_elem
, struct iovec
*vec
)
968 size_t cnt
, step
, maxiov
;
972 fname
= malloc (strlen (output_path
) + 2 * strlen (category
) + 6);
974 error (5, errno
, _("memory exhausted"));
976 /* Normally we write to the directory pointed to by the OUTPUT_PATH.
977 But for LC_MESSAGES we have to take care for the translation
978 data. This means we need to have a directory LC_MESSAGES in
979 which we place the file under the name SYS_LC_MESSAGES. */
980 sprintf (fname
, "%s%s", output_path
, category
);
981 if (strcmp (category
, "LC_MESSAGES") == 0)
985 if (stat (fname
, &st
) < 0)
987 if (mkdir (fname
, 0777) < 0)
988 fd
= creat (fname
, 0666);
995 else if (S_ISREG (st
.st_mode
))
996 fd
= creat (fname
, 0666);
1004 fd
= creat (fname
, 0666);
1008 int save_err
= errno
;
1010 if (errno
== EISDIR
)
1012 sprintf (fname
, "%1$s%2$s/SYS_%2$s", output_path
, category
);
1013 fd
= creat (fname
, 0666);
1018 if (fd
== -1 && !be_quiet
)
1020 error (0, save_err
, _("\
1021 cannot open output file `%s' for category `%s'"),
1029 maxiov
= UIO_MAXIOV
;
1031 maxiov
= sysconf (_SC_UIO_MAXIOV
);
1034 /* Write the data using writev. But we must take care for the
1035 limitation of the implementation. */
1036 for (cnt
= 0; cnt
< n_elem
; cnt
+= step
)
1038 step
= n_elem
- cnt
;
1040 step
= MIN (maxiov
, step
);
1042 if (writev (fd
, &vec
[cnt
], step
) < 0 && !be_quiet
)
1044 error (0, errno
, _("failure while writing data for category `%s'"),