1 /* Copyright (C) 1996-2001, 2002, 2003, 2004 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
30 #include <sys/param.h>
33 #include "../../crypt/md5.h"
34 #include "localedef.h"
36 #include "simple-hash.h"
38 #include "locfile-kw.h"
41 /* Temporary storage of the locale data before writing it to the archive. */
42 static locale_data_t to_archive
;
46 locfile_read (struct localedef_t
*result
, const struct charmap_t
*charmap
)
48 const char *filename
= result
->name
;
49 const char *repertoire_name
= result
->repertoire_name
;
50 int locale_mask
= result
->needed
& ~result
->avail
;
51 struct linereader
*ldfile
;
52 int not_here
= ALL_LOCALES
;
54 /* If no repertoire name was specified use the global one. */
55 if (repertoire_name
== NULL
)
56 repertoire_name
= repertoire_global
;
58 /* Open the locale definition file. */
59 ldfile
= lr_open (filename
, locfile_hash
);
62 if (filename
!= NULL
&& filename
[0] != '/')
64 char *i18npath
= getenv ("I18NPATH");
65 if (i18npath
!= NULL
&& *i18npath
!= '\0')
67 const size_t pathlen
= strlen (i18npath
);
68 char i18npathbuf
[pathlen
+ 1];
69 char path
[strlen (filename
) + 1 + pathlen
70 + sizeof ("/locales/") - 1];
72 i18npath
= memcpy (i18npathbuf
, i18npath
, pathlen
+ 1);
75 && (next
= strsep (&i18npath
, ":")) != NULL
)
77 stpcpy (stpcpy (stpcpy (path
, next
), "/locales/"), filename
);
79 ldfile
= lr_open (path
, locfile_hash
);
83 stpcpy (stpcpy (path
, next
), filename
);
85 ldfile
= lr_open (path
, locfile_hash
);
90 /* Test in the default directory. */
93 char path
[strlen (filename
) + 1 + sizeof (LOCSRCDIR
)];
95 stpcpy (stpcpy (stpcpy (path
, LOCSRCDIR
), "/"), filename
);
96 ldfile
= lr_open (path
, locfile_hash
);
104 /* Parse locale definition file and store result in RESULT. */
107 struct token
*now
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
108 enum token_t nowtok
= now
->tok
;
111 if (nowtok
== tok_eof
)
114 if (nowtok
== tok_eol
)
115 /* Ignore empty lines. */
120 case tok_escape_char
:
121 case tok_comment_char
:
122 /* We need an argument. */
123 arg
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
125 if (arg
->tok
!= tok_ident
)
127 SYNTAX_ERROR (_("bad argument"));
131 if (arg
->val
.str
.lenmb
!= 1)
133 lr_error (ldfile
, _("\
134 argument to `%s' must be a single character"),
135 nowtok
== tok_escape_char
136 ? "escape_char" : "comment_char");
138 lr_ignore_rest (ldfile
, 0);
142 if (nowtok
== tok_escape_char
)
143 ldfile
->escape_char
= *arg
->val
.str
.startmb
;
145 ldfile
->comment_char
= *arg
->val
.str
.startmb
;
148 case tok_repertoiremap
:
149 /* We need an argument. */
150 arg
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
152 if (arg
->tok
!= tok_ident
)
154 SYNTAX_ERROR (_("bad argument"));
158 if (repertoire_name
== NULL
)
160 repertoire_name
= memcpy (xmalloc (arg
->val
.str
.lenmb
+ 1),
161 arg
->val
.str
.startmb
,
163 ((char *) repertoire_name
)[arg
->val
.str
.lenmb
] = '\0';
168 ctype_read (ldfile
, result
, charmap
, repertoire_name
,
169 (locale_mask
& CTYPE_LOCALE
) == 0);
170 result
->avail
|= locale_mask
& CTYPE_LOCALE
;
171 not_here
^= CTYPE_LOCALE
;
175 collate_read (ldfile
, result
, charmap
, repertoire_name
,
176 (locale_mask
& COLLATE_LOCALE
) == 0);
177 result
->avail
|= locale_mask
& COLLATE_LOCALE
;
178 not_here
^= COLLATE_LOCALE
;
181 case tok_lc_monetary
:
182 monetary_read (ldfile
, result
, charmap
, repertoire_name
,
183 (locale_mask
& MONETARY_LOCALE
) == 0);
184 result
->avail
|= locale_mask
& MONETARY_LOCALE
;
185 not_here
^= MONETARY_LOCALE
;
189 numeric_read (ldfile
, result
, charmap
, repertoire_name
,
190 (locale_mask
& NUMERIC_LOCALE
) == 0);
191 result
->avail
|= locale_mask
& NUMERIC_LOCALE
;
192 not_here
^= NUMERIC_LOCALE
;
196 time_read (ldfile
, result
, charmap
, repertoire_name
,
197 (locale_mask
& TIME_LOCALE
) == 0);
198 result
->avail
|= locale_mask
& TIME_LOCALE
;
199 not_here
^= TIME_LOCALE
;
202 case tok_lc_messages
:
203 messages_read (ldfile
, result
, charmap
, repertoire_name
,
204 (locale_mask
& MESSAGES_LOCALE
) == 0);
205 result
->avail
|= locale_mask
& MESSAGES_LOCALE
;
206 not_here
^= MESSAGES_LOCALE
;
210 paper_read (ldfile
, result
, charmap
, repertoire_name
,
211 (locale_mask
& PAPER_LOCALE
) == 0);
212 result
->avail
|= locale_mask
& PAPER_LOCALE
;
213 not_here
^= PAPER_LOCALE
;
217 name_read (ldfile
, result
, charmap
, repertoire_name
,
218 (locale_mask
& NAME_LOCALE
) == 0);
219 result
->avail
|= locale_mask
& NAME_LOCALE
;
220 not_here
^= NAME_LOCALE
;
224 address_read (ldfile
, result
, charmap
, repertoire_name
,
225 (locale_mask
& ADDRESS_LOCALE
) == 0);
226 result
->avail
|= locale_mask
& ADDRESS_LOCALE
;
227 not_here
^= ADDRESS_LOCALE
;
230 case tok_lc_telephone
:
231 telephone_read (ldfile
, result
, charmap
, repertoire_name
,
232 (locale_mask
& TELEPHONE_LOCALE
) == 0);
233 result
->avail
|= locale_mask
& TELEPHONE_LOCALE
;
234 not_here
^= TELEPHONE_LOCALE
;
237 case tok_lc_measurement
:
238 measurement_read (ldfile
, result
, charmap
, repertoire_name
,
239 (locale_mask
& MEASUREMENT_LOCALE
) == 0);
240 result
->avail
|= locale_mask
& MEASUREMENT_LOCALE
;
241 not_here
^= MEASUREMENT_LOCALE
;
244 case tok_lc_identification
:
245 identification_read (ldfile
, result
, charmap
, repertoire_name
,
246 (locale_mask
& IDENTIFICATION_LOCALE
) == 0);
247 result
->avail
|= locale_mask
& IDENTIFICATION_LOCALE
;
248 not_here
^= IDENTIFICATION_LOCALE
;
253 syntax error: not inside a locale definition section"));
257 /* The rest of the line must be empty. */
258 lr_ignore_rest (ldfile
, 1);
261 /* We read all of the file. */
264 /* Mark the categories which are not contained in the file. We assume
265 them to be available and the default data will be used. */
266 result
->avail
|= not_here
;
272 /* Semantic checking of locale specifications. */
274 static void (*const check_funcs
[]) (struct localedef_t
*,
275 const struct charmap_t
*) =
277 [LC_CTYPE
] = ctype_finish
,
278 [LC_COLLATE
] = collate_finish
,
279 [LC_MESSAGES
] = messages_finish
,
280 [LC_MONETARY
] = monetary_finish
,
281 [LC_NUMERIC
] = numeric_finish
,
282 [LC_TIME
] = time_finish
,
283 [LC_PAPER
] = paper_finish
,
284 [LC_NAME
] = name_finish
,
285 [LC_ADDRESS
] = address_finish
,
286 [LC_TELEPHONE
] = telephone_finish
,
287 [LC_MEASUREMENT
] = measurement_finish
,
288 [LC_IDENTIFICATION
] = identification_finish
292 check_all_categories (struct localedef_t
*definitions
,
293 const struct charmap_t
*charmap
)
297 for (cnt
= 0; cnt
< sizeof (check_funcs
) / sizeof (check_funcs
[0]); ++cnt
)
298 if (check_funcs
[cnt
] != NULL
)
299 check_funcs
[cnt
] (definitions
, charmap
);
303 /* Writing the locale data files. All files use the same output_path. */
305 static void (*const write_funcs
[]) (struct localedef_t
*,
306 const struct charmap_t
*, const char *) =
308 [LC_CTYPE
] = ctype_output
,
309 [LC_COLLATE
] = collate_output
,
310 [LC_MESSAGES
] = messages_output
,
311 [LC_MONETARY
] = monetary_output
,
312 [LC_NUMERIC
] = numeric_output
,
313 [LC_TIME
] = time_output
,
314 [LC_PAPER
] = paper_output
,
315 [LC_NAME
] = name_output
,
316 [LC_ADDRESS
] = address_output
,
317 [LC_TELEPHONE
] = telephone_output
,
318 [LC_MEASUREMENT
] = measurement_output
,
319 [LC_IDENTIFICATION
] = identification_output
324 write_all_categories (struct localedef_t
*definitions
,
325 const struct charmap_t
*charmap
, const char *locname
,
326 const char *output_path
)
330 for (cnt
= 0; cnt
< sizeof (write_funcs
) / sizeof (write_funcs
[0]); ++cnt
)
331 if (write_funcs
[cnt
] != NULL
)
332 write_funcs
[cnt
] (definitions
, charmap
, output_path
);
336 /* The data has to be added to the archive. Do this now. */
337 struct locarhandle ah
;
339 /* Open the archive. This call never returns if we cannot
340 successfully open the archive. */
341 open_archive (&ah
, false);
343 if (add_locale_to_archive (&ah
, locname
, to_archive
, true) != 0)
344 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
352 /* Return a NULL terminated list of the directories next to output_path
353 that have the same owner, group, permissions and device as output_path. */
355 siblings_uncached (const char *output_path
)
359 struct stat output_stat
;
364 /* Remove trailing slashes and trailing pathname component. */
365 len
= strlen (output_path
);
366 base
= (char *) alloca (len
);
367 memcpy (base
, output_path
, len
);
369 while (p
> base
&& p
[-1] == '/')
375 while (p
> base
&& p
[-1] != '/');
381 /* Get the properties of output_path. */
382 if (lstat (output_path
, &output_stat
) < 0 || !S_ISDIR (output_stat
.st_mode
))
385 /* Iterate through the directories in base directory. */
386 dirp
= opendir (base
);
393 struct dirent64
*other_dentry
;
394 const char *other_name
;
396 struct stat other_stat
;
398 other_dentry
= readdir64 (dirp
);
399 if (other_dentry
== NULL
)
402 other_name
= other_dentry
->d_name
;
403 if (strcmp (other_name
, ".") == 0 || strcmp (other_name
, "..") == 0)
406 other_path
= (char *) xmalloc (len
+ 1 + strlen (other_name
) + 2);
407 memcpy (other_path
, base
, len
);
408 other_path
[len
] = '/';
409 strcpy (other_path
+ len
+ 1, other_name
);
411 if (lstat (other_path
, &other_stat
) >= 0
412 && S_ISDIR (other_stat
.st_mode
)
413 && other_stat
.st_uid
== output_stat
.st_uid
414 && other_stat
.st_gid
== output_stat
.st_gid
415 && other_stat
.st_mode
== output_stat
.st_mode
416 && other_stat
.st_dev
== output_stat
.st_dev
)
418 /* Found a subdirectory. Add a trailing slash and store it. */
419 p
= other_path
+ len
+ 1 + strlen (other_name
);
422 elems
= (const char **) xrealloc ((char *) elems
,
423 (nelems
+ 2) * sizeof (char **));
424 elems
[nelems
++] = other_path
;
432 elems
[nelems
] = NULL
;
437 /* Return a NULL terminated list of the directories next to output_path
438 that have the same owner, group, permissions and device as output_path.
439 Cache the result for future calls. */
441 siblings (const char *output_path
)
443 static const char *last_output_path
;
444 static const char **last_result
;
446 if (output_path
!= last_output_path
)
448 if (last_result
!= NULL
)
452 for (p
= last_result
; *p
!= NULL
; p
++)
457 last_output_path
= output_path
;
458 last_result
= siblings_uncached (output_path
);
464 /* Read as many bytes from a file descriptor as possible. */
466 full_read (int fd
, void *bufarea
, size_t nbyte
)
468 char *buf
= (char *) bufarea
;
472 ssize_t retval
= read (fd
, buf
, nbyte
);
481 else if (errno
!= EINTR
)
484 return buf
- (char *) bufarea
;
488 /* Compare the contents of two regular files of the same size. Return 0
489 if they are equal, 1 if they are different, or -1 if an error occurs. */
491 compare_files (const char *filename1
, const char *filename2
, size_t size
,
497 fd1
= open (filename1
, O_RDONLY
);
500 fd2
= open (filename2
, O_RDONLY
);
503 char *buf1
= (char *) xmalloc (2 * blocksize
);
504 char *buf2
= buf1
+ blocksize
;
509 size_t bytes
= (size
< blocksize
? size
: blocksize
);
511 if (full_read (fd1
, buf1
, bytes
) < (ssize_t
) bytes
)
516 if (full_read (fd2
, buf2
, bytes
) < (ssize_t
) bytes
)
521 if (memcmp (buf1
, buf2
, bytes
) != 0)
538 /* Write a locale file, with contents given by N_ELEM and VEC. */
540 write_locale_data (const char *output_path
, int catidx
, const char *category
,
541 size_t n_elem
, struct iovec
*vec
)
543 size_t cnt
, step
, maxiov
;
546 const char **other_paths
;
550 /* The data will be added to the archive. For now we simply
551 generate the image which will be written. First determine
556 to_archive
[catidx
].size
= 0;
557 for (cnt
= 0; cnt
< n_elem
; ++cnt
)
558 to_archive
[catidx
].size
+= vec
[cnt
].iov_len
;
560 /* Allocate the memory for it. */
561 to_archive
[catidx
].addr
= xmalloc (to_archive
[catidx
].size
);
564 for (cnt
= 0, endp
= to_archive
[catidx
].addr
; cnt
< n_elem
; ++cnt
)
565 endp
= mempcpy (endp
, vec
[cnt
].iov_base
, vec
[cnt
].iov_len
);
567 /* Compute the MD5 sum for the data. */
568 __md5_buffer (to_archive
[catidx
].addr
, to_archive
[catidx
].size
,
569 to_archive
[catidx
].sum
);
574 fname
= xmalloc (strlen (output_path
) + 2 * strlen (category
) + 7);
576 /* Normally we write to the directory pointed to by the OUTPUT_PATH.
577 But for LC_MESSAGES we have to take care for the translation
578 data. This means we need to have a directory LC_MESSAGES in
579 which we place the file under the name SYS_LC_MESSAGES. */
580 sprintf (fname
, "%s%s", output_path
, category
);
582 if (strcmp (category
, "LC_MESSAGES") == 0)
586 if (stat (fname
, &st
) < 0)
588 if (mkdir (fname
, 0777) >= 0)
594 else if (!S_ISREG (st
.st_mode
))
601 /* Create the locale file with nlinks == 1; this avoids crashing processes
602 which currently use the locale and damaging files belonging to other
607 fd
= creat (fname
, 0666);
612 int save_err
= errno
;
616 sprintf (fname
, "%1$s%2$s/SYS_%2$s", output_path
, category
);
618 fd
= creat (fname
, 0666);
626 WITH_CUR_LOCALE (error (0, save_err
, _("\
627 cannot open output file `%s' for category `%s'"), fname
, category
));
636 maxiov
= sysconf (_SC_UIO_MAXIOV
);
639 /* Write the data using writev. But we must take care for the
640 limitation of the implementation. */
641 for (cnt
= 0; cnt
< n_elem
; cnt
+= step
)
645 step
= MIN (maxiov
, step
);
647 if (writev (fd
, &vec
[cnt
], step
) < 0)
650 WITH_CUR_LOCALE (error (0, errno
, _("\
651 failure while writing data for category `%s'"), category
));
658 /* Compare the file with the locale data files for the same category in
659 other locales, and see if we can reuse it, to save disk space. */
660 other_paths
= siblings (output_path
);
661 if (other_paths
!= NULL
)
663 struct stat fname_stat
;
665 if (lstat (fname
, &fname_stat
) >= 0
666 && S_ISREG (fname_stat
.st_mode
))
668 const char *fname_tail
= fname
+ strlen (output_path
);
669 const char **other_p
;
674 for (other_p
= other_paths
; *other_p
; other_p
++)
676 seen_inodes
= (ino_t
*) xmalloc (seen_count
* sizeof (ino_t
));
679 for (other_p
= other_paths
; *other_p
; other_p
++)
681 const char *other_path
= *other_p
;
682 size_t other_path_len
= strlen (other_path
);
684 struct stat other_fname_stat
;
687 (char *) xmalloc (other_path_len
+ strlen (fname_tail
) + 1);
688 memcpy (other_fname
, other_path
, other_path_len
);
689 strcpy (other_fname
+ other_path_len
, fname_tail
);
691 if (lstat (other_fname
, &other_fname_stat
) >= 0
692 && S_ISREG (other_fname_stat
.st_mode
)
693 /* Consider only files on the same device.
694 Otherwise hard linking won't work anyway. */
695 && other_fname_stat
.st_dev
== fname_stat
.st_dev
696 /* Consider only files with the same permissions.
697 Otherwise there are security risks. */
698 && other_fname_stat
.st_uid
== fname_stat
.st_uid
699 && other_fname_stat
.st_gid
== fname_stat
.st_gid
700 && other_fname_stat
.st_mode
== fname_stat
.st_mode
701 /* Don't compare fname with itself. */
702 && other_fname_stat
.st_ino
!= fname_stat
.st_ino
703 /* Files must have the same size, otherwise they
704 cannot be the same. */
705 && other_fname_stat
.st_size
== fname_stat
.st_size
)
707 /* Skip this file if we have already read it (under a
711 for (i
= seen_count
- 1; i
>= 0; i
--)
712 if (seen_inodes
[i
] == other_fname_stat
.st_ino
)
716 /* Now compare fname and other_fname for real. */
719 #ifdef _STATBUF_ST_BLKSIZE
720 blocksize
= MAX (fname_stat
.st_blksize
,
721 other_fname_stat
.st_blksize
);
722 if (blocksize
> 8 * 1024)
723 blocksize
= 8 * 1024;
725 blocksize
= 8 * 1024;
728 if (compare_files (fname
, other_fname
,
729 fname_stat
.st_size
, blocksize
) == 0)
731 /* Found! other_fname is identical to fname. */
732 /* Link other_fname to fname. But use a temporary
733 file, in case hard links don't work on the
734 particular filesystem. */
736 (char *) xmalloc (strlen (fname
) + 4 + 1);
738 strcpy (stpcpy (tmp_fname
, fname
), ".tmp");
740 if (link (other_fname
, tmp_fname
) >= 0)
743 if (rename (tmp_fname
, fname
) < 0)
746 WITH_CUR_LOCALE (error (0, errno
, _("\
747 cannot create output file `%s' for category `%s'"), fname
, category
));
756 /* Don't compare with this file a second time. */
757 seen_inodes
[seen_count
++] = other_fname_stat
.st_ino
;
770 /* General handling of `copy'. */
772 handle_copy (struct linereader
*ldfile
, const struct charmap_t
*charmap
,
773 const char *repertoire_name
, struct localedef_t
*result
,
774 enum token_t token
, int locale
, const char *locale_name
,
780 now
= lr_token (ldfile
, charmap
, result
, NULL
, verbose
);
781 if (now
->tok
!= tok_string
)
782 lr_error (ldfile
, _("expect string argument for `copy'"));
783 else if (!ignore_content
)
785 if (now
->val
.str
.startmb
== NULL
)
786 lr_error (ldfile
, _("\
787 locale name should consist only of portable characters"));
790 (void) add_to_readlist (locale
, now
->val
.str
.startmb
,
791 repertoire_name
, 1, NULL
);
792 result
->copy_name
[locale
] = now
->val
.str
.startmb
;
796 lr_ignore_rest (ldfile
, now
->tok
== tok_string
);
798 /* The rest of the line must be empty and the next keyword must be
800 while ((now
= lr_token (ldfile
, charmap
, result
, NULL
, verbose
))->tok
801 != tok_end
&& now
->tok
!= tok_eof
)
805 lr_error (ldfile
, _("\
806 no other keyword shall be specified when `copy' is used"));
810 lr_ignore_rest (ldfile
, 0);
813 if (now
->tok
!= tok_eof
)
815 /* Handle `END xxx'. */
816 now
= lr_token (ldfile
, charmap
, result
, NULL
, verbose
);
818 if (now
->tok
!= token
)
819 lr_error (ldfile
, _("\
820 `%1$s' definition does not end with `END %1$s'"), locale_name
);
822 lr_ignore_rest (ldfile
, now
->tok
== token
);
825 /* When we come here we reached the end of the file. */
826 lr_error (ldfile
, _("%s: premature end of file"), locale_name
);