1 /* Copyright (C) 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
10 This program 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
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
33 #include <stdio_ext.h>
39 #include <sys/param.h>
42 #include "../../crypt/md5.h"
43 #include "../localeinfo.h"
44 #include "../locarchive.h"
45 #include "localedef.h"
47 /* Define the hash function. We define the function as static inline.
48 We must change the name so as not to conflict with simple-hash.h. */
49 #define compute_hashval static inline archive_hashval
50 #define hashval_t uint32_t
52 #undef compute_hashval
54 extern const char *output_prefix
;
56 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
58 static const char *locnames
[] =
60 #define DEFINE_CATEGORY(category, category_name, items, a) \
61 [category] = category_name,
62 #include "categories.def"
63 #undef DEFINE_CATEGORY
67 /* Size of the initial archive header. */
68 #define INITIAL_NUM_NAMES 900
69 #define INITIAL_SIZE_STRINGS 7500
70 #define INITIAL_NUM_LOCREC 420
71 #define INITIAL_NUM_SUMS 2000
75 create_archive (const char *archivefname
, struct locarhandle
*ah
)
78 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
79 struct locarhead head
;
83 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
85 /* Create a temporary file in the correct directory. */
88 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
90 /* Create the initial content of the archive. */
91 head
.magic
= AR_MAGIC
;
93 head
.namehash_offset
= sizeof (struct locarhead
);
94 head
.namehash_used
= 0;
95 head
.namehash_size
= next_prime (INITIAL_NUM_NAMES
);
97 head
.string_offset
= (head
.namehash_offset
98 + head
.namehash_size
* sizeof (struct namehashent
));
100 head
.string_size
= INITIAL_SIZE_STRINGS
;
102 head
.locrectab_offset
= head
.string_offset
+ head
.string_size
;
103 head
.locrectab_used
= 0;
104 head
.locrectab_size
= INITIAL_NUM_LOCREC
;
106 head
.sumhash_offset
= (head
.locrectab_offset
107 + head
.locrectab_size
* sizeof (struct locrecent
));
108 head
.sumhash_used
= 0;
109 head
.sumhash_size
= next_prime (INITIAL_NUM_SUMS
);
111 total
= head
.sumhash_offset
+ head
.sumhash_size
* sizeof (struct sumhashent
);
113 /* Write out the header and create room for the other data structures. */
114 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
118 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
121 if (ftruncate64 (fd
, total
) != 0)
125 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
128 /* Map the header and all the administration data structures. */
129 p
= mmap64 (NULL
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
134 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
137 /* Now try to rename it. We don't use the rename function since
138 this would overwrite a file which has been created in
140 if (link (fname
, archivefname
) == -1)
144 /* We cannot use the just created file. */
148 if (errval
== EEXIST
)
150 /* There is already an archive. Must have been a localedef run
151 which happened in parallel. Simply open this file then. */
152 open_archive (ah
, false);
156 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
159 /* Remove the temporary name. */
162 /* Make the file globally readable. */
163 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
166 unlink (archivefname
);
167 error (EXIT_FAILURE
, errval
,
168 _("cannot change mode of new locale archive"));
177 /* This structure and qsort comparator function are used below to sort an
178 old archive's locrec table in order of data position in the file. */
182 struct locrecent
*locrec
;
186 oldlocrecentcmp (const void *a
, const void *b
)
188 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
189 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
190 uint32_t start_a
= -1, end_a
= 0;
191 uint32_t start_b
= -1, end_b
= 0;
194 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
197 if (la
->record
[cnt
].offset
< start_a
)
198 start_a
= la
->record
[cnt
].offset
;
199 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
200 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
202 assert (start_a
!= (uint32_t)-1);
205 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
208 if (lb
->record
[cnt
].offset
< start_b
)
209 start_b
= lb
->record
[cnt
].offset
;
210 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
211 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
213 assert (start_b
!= (uint32_t)-1);
216 if (start_a
!= start_b
)
217 return (int)start_a
- (int)start_b
;
218 return (int)end_a
- (int)end_b
;
222 /* forward decls for below */
223 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
224 locale_data_t data
, bool replace
);
225 static void add_alias (struct locarhandle
*ah
, const char *alias
,
226 bool replace
, const char *oldname
,
227 uint32_t *locrec_offset_p
);
230 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
234 struct locarhead newhead
;
237 unsigned int cnt
, loccnt
;
238 struct namehashent
*oldnamehashtab
;
239 struct locrecent
*oldlocrectab
;
240 struct locarhandle new_ah
;
241 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
242 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
243 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
246 memcpy (archivefname
, output_prefix
, prefix_len
);
247 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
248 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
250 /* Not all of the old file has to be mapped. Change this now this
251 we will have to access the whole content. */
252 if (fstat64 (ah
->fd
, &st
) != 0
253 || (ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
254 MAP_SHARED
, ah
->fd
, 0)) == MAP_FAILED
)
255 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
256 ah
->len
= st
.st_size
;
258 /* Create a temporary file in the correct directory. */
259 fd
= mkstemp (fname
);
261 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
263 /* Copy the existing head information. */
266 /* Create the new archive header. The sizes of the various tables
267 should be double from what is currently used. */
268 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
269 newhead
.namehash_size
);
271 printf ("name: size: %u, used: %d, new: size: %u\n",
272 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
274 newhead
.string_offset
= (newhead
.namehash_offset
275 + (newhead
.namehash_size
276 * sizeof (struct namehashent
)));
277 /* Keep the string table size aligned to 4 bytes, so that
278 all the struct { uint32_t } types following are happy. */
279 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
280 newhead
.string_size
);
282 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
283 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
284 newhead
.locrectab_size
);
286 newhead
.sumhash_offset
= (newhead
.locrectab_offset
287 + (newhead
.locrectab_size
288 * sizeof (struct locrecent
)));
289 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
290 newhead
.sumhash_size
);
292 total
= (newhead
.sumhash_offset
293 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
295 /* The new file is empty now. */
296 newhead
.namehash_used
= 0;
297 newhead
.string_used
= 0;
298 newhead
.locrectab_used
= 0;
299 newhead
.sumhash_used
= 0;
301 /* Write out the header and create room for the other data structures. */
302 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
307 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
310 if (ftruncate64 (fd
, total
) != 0)
314 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
317 /* Map the header and all the administration data structures. */
318 p
= mmap64 (NULL
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
323 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
326 /* Lock the new file. */
327 if (lockf64 (fd
, F_LOCK
, total
) != 0)
331 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
338 /* Walk through the hash name hash table to find out what data is
339 still referenced and transfer it into the new file. */
340 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
341 + head
->namehash_offset
);
342 oldlocrectab
= (struct locrecent
*) ((char *) ah
->addr
343 + head
->locrectab_offset
);
345 /* Sort the old locrec table in order of data position. */
346 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
347 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
348 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
350 oldlocrecarray
[loccnt
].cnt
= cnt
;
351 oldlocrecarray
[loccnt
++].locrec
352 = (struct locrecent
*) ((char *) ah
->addr
353 + oldnamehashtab
[cnt
].locrec_offset
);
355 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
358 uint32_t last_locrec_offset
= 0;
359 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
361 /* Insert this entry in the new hash table. */
362 locale_data_t old_data
;
364 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
366 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
369 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
371 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
373 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
377 if (cnt
> 0 && oldlocrecarray
[cnt
- 1].locrec
== oldlocrec
)
381 + oldnamehashtab
[oldlocrecarray
[cnt
- 1].cnt
].name_offset
);
385 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
386 0, oldname
, &last_locrec_offset
);
393 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
395 if (last_locrec_offset
== 0)
396 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
399 /* Make the file globally readable. */
400 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
404 error (EXIT_FAILURE
, errval
,
405 _("cannot change mode of resized locale archive"));
408 /* Rename the new file. */
409 if (rename (fname
, archivefname
) != 0)
413 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
416 /* Close the old file. */
419 /* Add the information for the new one. */
425 open_archive (struct locarhandle
*ah
, bool readonly
)
430 struct locarhead head
;
432 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
433 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
436 memcpy (archivefname
, output_prefix
, prefix_len
);
437 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
441 /* Open the archive. We must have exclusive write access. */
442 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
445 /* Maybe the file does not yet exist. */
450 static const struct locarhead nullhead
=
453 .namehash_offset
= 0,
457 ah
->addr
= (void *) &nullhead
;
461 create_archive (archivefname
, ah
);
466 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
470 if (fstat64 (fd
, &st
) < 0)
471 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
474 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
478 if (retry
++ < max_locarchive_open_retry
)
482 /* Wait for a bit. */
484 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
485 (void) nanosleep (&req
, NULL
);
490 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
494 /* One more check. Maybe another process replaced the archive file
495 with a new, larger one since we opened the file. */
496 if (stat64 (archivefname
, &st2
) == -1
497 || st
.st_dev
!= st2
.st_dev
498 || st
.st_ino
!= st2
.st_ino
)
500 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
505 /* Leave the loop. */
509 /* Read the header. */
510 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
512 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
513 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
517 ah
->len
= (head
.sumhash_offset
518 + head
.sumhash_size
* sizeof (struct sumhashent
));
520 /* Now we know how large the administrative information part is.
522 ah
->addr
= mmap64 (NULL
, ah
->len
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
524 if (ah
->addr
== MAP_FAILED
)
526 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
527 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
533 close_archive (struct locarhandle
*ah
)
537 munmap (ah
->addr
, ah
->len
);
542 #include "../../intl/explodename.c"
543 #include "../../intl/l10nflist.c"
545 static struct namehashent
*
546 insert_name (struct locarhandle
*ah
,
547 const char *name
, size_t name_len
, bool replace
)
549 const struct locarhead
*const head
= ah
->addr
;
550 struct namehashent
*namehashtab
551 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
552 unsigned int insert_idx
, idx
, incr
;
554 /* Hash value of the locale name. */
555 uint32_t hval
= archive_hashval (name
, name_len
);
558 idx
= hval
% head
->namehash_size
;
559 incr
= 1 + hval
% (head
->namehash_size
- 2);
561 /* If the name_offset field is zero this means this is a
562 deleted entry and therefore no entry can be found. */
563 while (namehashtab
[idx
].name_offset
!= 0)
565 if (namehashtab
[idx
].hashval
== hval
567 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
569 /* Found the entry. */
570 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
573 error (0, 0, _("locale '%s' already exists"), name
);
580 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
582 error (0, 0, "hash collision (%u) %s, %s",
583 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
586 /* Remember the first place we can insert the new entry. */
587 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
591 if (idx
>= head
->namehash_size
)
592 idx
-= head
->namehash_size
;
595 /* Add as early as possible. */
596 if (insert_idx
!= -1)
599 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
600 return &namehashtab
[idx
];
604 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
605 const char *oldname
, uint32_t *locrec_offset_p
)
607 uint32_t locrec_offset
= *locrec_offset_p
;
608 struct locarhead
*head
= ah
->addr
;
609 const size_t name_len
= strlen (alias
);
610 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
612 if (namehashent
== NULL
&& ! replace
)
615 if (namehashent
->name_offset
== 0)
617 /* We are adding a new hash entry for this alias.
618 Determine whether we have to resize the file. */
619 if (head
->string_used
+ name_len
+ 1 > head
->string_size
620 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
622 /* The current archive is not large enough. */
623 enlarge_archive (ah
, head
);
625 /* The locrecent might have moved, so we have to look up
626 the old name afresh. */
627 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
628 assert (namehashent
->name_offset
!= 0);
629 assert (namehashent
->locrec_offset
!= 0);
630 *locrec_offset_p
= namehashent
->locrec_offset
;
632 /* Tail call to try the whole thing again. */
633 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
637 /* Add the name string. */
638 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
639 alias
, name_len
+ 1);
640 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
641 head
->string_used
+= name_len
+ 1;
643 ++head
->namehash_used
;
646 if (namehashent
->locrec_offset
!= 0)
648 /* Replacing an existing entry.
649 Mark that we are no longer using the old locrecent. */
650 struct locrecent
*locrecent
651 = (struct locrecent
*) ((char *) ah
->addr
652 + namehashent
->locrec_offset
);
656 /* Point this entry at the locrecent installed for the main name. */
657 namehashent
->locrec_offset
= locrec_offset
;
660 static int /* qsort comparator used below */
661 cmpcategorysize (const void *a
, const void *b
)
663 if (*(const void **) a
== NULL
)
665 if (*(const void **) b
== NULL
)
667 return ((*(const struct locale_category_data
**) a
)->size
668 - (*(const struct locale_category_data
**) b
)->size
);
671 /* Check the content of the archive for duplicates. Add the content
672 of the files if necessary. Returns the locrec_offset. */
674 add_locale (struct locarhandle
*ah
,
675 const char *name
, locale_data_t data
, bool replace
)
677 /* First look for the name. If it already exists and we are not
678 supposed to replace it don't do anything. If it does not exist
679 we have to allocate a new locale record. */
680 size_t name_len
= strlen (name
);
681 uint32_t file_offsets
[__LC_LAST
];
682 unsigned int num_new_offsets
= 0;
683 struct sumhashent
*sumhashtab
;
685 unsigned int cnt
, idx
;
686 struct locarhead
*head
;
687 struct namehashent
*namehashent
;
689 struct locrecent
*locrecent
;
692 struct locale_category_data
*size_order
[__LC_LAST
];
693 const size_t pagesz
= getpagesize ();
697 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
698 + head
->sumhash_offset
);
700 memset (file_offsets
, 0, sizeof (file_offsets
));
702 size_order
[LC_ALL
] = NULL
;
703 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
705 size_order
[cnt
] = &data
[cnt
];
707 /* Sort the array in ascending order of data size. */
708 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
711 data
[LC_ALL
].size
= 0;
712 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
713 if (size_order
[cnt
] != NULL
)
715 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
716 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
718 /* This category makes the small-categories block
719 stop being small, so this is the end of the road. */
721 size_order
[cnt
++] = NULL
;
722 while (cnt
< __LC_LAST
);
725 data
[LC_ALL
].size
+= rounded_size
;
726 small_mask
|= 1 << (size_order
[cnt
] - data
);
729 /* Copy the data for all the small categories into the LC_ALL
732 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
733 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
735 ptr
= data
[LC_ALL
].addr
;
736 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
737 if (small_mask
& (1 << cnt
))
739 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
740 ptr
+= (data
[cnt
].size
+ 15) & -16;
742 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
744 /* For each locale category data set determine whether the same data
745 is already somewhere in the archive. */
746 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
747 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
751 /* Compute the hash value of the checksum to determine a
752 starting point for the search in the MD5 hash value
754 hval
= archive_hashval (data
[cnt
].sum
, 16);
756 idx
= hval
% head
->sumhash_size
;
757 incr
= 1 + hval
% (head
->sumhash_size
- 2);
759 while (sumhashtab
[idx
].file_offset
!= 0)
761 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
764 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
770 if (idx
>= head
->sumhash_size
)
771 idx
-= head
->sumhash_size
;
775 /* Find a slot for the locale name in the hash table. */
776 namehashent
= insert_name (ah
, name
, name_len
, replace
);
777 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
780 /* Determine whether we have to resize the file. */
781 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
782 || (namehashent
->locrec_offset
== 0
783 && (head
->locrectab_used
== head
->locrectab_size
784 || head
->string_used
+ name_len
+ 1 > head
->string_size
785 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
787 /* The current archive is not large enough. */
788 enlarge_archive (ah
, head
);
789 return add_locale (ah
, name
, data
, replace
);
792 /* Add the locale data which is not yet in the archive. */
793 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
794 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
795 && file_offsets
[cnt
] == 0)
797 /* The data for this section is not yet available in the
798 archive. Append it. */
802 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
803 if (lastpos
== (off64_t
) -1)
804 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
806 /* If block of small categories would cross page boundary,
807 align it unless it immediately follows a large category. */
808 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
809 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
811 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
813 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
814 char *zeros
= alloca (sz
);
816 memset (zeros
, 0, sz
);
817 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
818 error (EXIT_FAILURE
, errno
,
819 _("cannot add to locale archive"));
824 /* Align all data to a 16 byte boundary. */
825 if ((lastpos
& 15) != 0)
827 static const char zeros
[15] = { 0, };
829 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
830 != 16 - (lastpos
& 15))
831 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
833 lastpos
+= 16 - (lastpos
& 15);
836 /* Remember the position. */
837 file_offsets
[cnt
] = lastpos
;
838 lastoffset
= lastpos
+ data
[cnt
].size
;
840 /* Write the data. */
841 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
843 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
845 /* Add the hash value to the hash table. */
846 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
848 idx
= md5hval
% head
->sumhash_size
;
849 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
851 while (sumhashtab
[idx
].file_offset
!= 0)
854 if (idx
>= head
->sumhash_size
)
855 idx
-= head
->sumhash_size
;
858 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
859 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
861 ++head
->sumhash_used
;
864 lastoffset
= file_offsets
[LC_ALL
];
865 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
866 if (small_mask
& (1 << cnt
))
868 file_offsets
[cnt
] = lastoffset
;
869 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
872 if (namehashent
->name_offset
== 0)
874 /* Add the name string. */
875 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
877 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
878 head
->string_used
+= name_len
+ 1;
879 ++head
->namehash_used
;
882 if (namehashent
->locrec_offset
== 0)
884 /* Allocate a name location record. */
885 namehashent
->locrec_offset
= (head
->locrectab_offset
886 + (head
->locrectab_used
++
887 * sizeof (struct locrecent
)));
888 locrecent
= (struct locrecent
*) ((char *) ah
->addr
889 + namehashent
->locrec_offset
);
894 /* If there are other aliases pointing to this locrecent,
895 we still need a new one. If not, reuse the old one. */
897 locrecent
= (struct locrecent
*) ((char *) ah
->addr
898 + namehashent
->locrec_offset
);
899 if (locrecent
->refs
> 1)
902 namehashent
->locrec_offset
= (head
->locrectab_offset
903 + (head
->locrectab_used
++
904 * sizeof (struct locrecent
)));
905 locrecent
= (struct locrecent
*) ((char *) ah
->addr
906 + namehashent
->locrec_offset
);
911 /* Fill in the table with the locations of the locale data. */
912 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
914 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
915 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
918 return namehashent
->locrec_offset
;
922 /* Check the content of the archive for duplicates. Add the content
923 of the files if necessary. Add all the names, possibly overwriting
926 add_locale_to_archive (ah
, name
, data
, replace
)
927 struct locarhandle
*ah
;
932 char *normalized_name
= NULL
;
933 uint32_t locrec_offset
;
935 /* First analyze the name to decide how to archive it. */
936 const char *language
;
937 const char *modifier
;
938 const char *territory
;
940 const char *normalized_codeset
;
941 int mask
= _nl_explode_name (strdupa (name
),
942 &language
, &modifier
, &territory
,
943 &codeset
, &normalized_codeset
);
945 if (mask
& XPG_NORM_CODESET
)
946 /* This name contains a codeset in unnormalized form.
947 We will store it in the archive with a normalized name. */
948 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
949 language
, territory
== NULL
? "" : "_", territory
?: "",
950 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
951 modifier
== NULL
? "" : "@", modifier
?: "");
953 /* This call does the main work. */
954 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
955 if (locrec_offset
== 0)
957 free (normalized_name
);
958 if (mask
& XPG_NORM_CODESET
)
959 free ((char *) normalized_codeset
);
963 if ((mask
& XPG_CODESET
) == 0)
965 /* This name lacks a codeset, so determine the locale's codeset and
966 add an alias for its name with normalized codeset appended. */
971 unsigned int nstrings
;
972 unsigned int strindex
[0];
973 } *filedata
= data
[LC_CTYPE
].addr
;
974 codeset
= (char *) filedata
975 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
976 char *normalized_codeset_name
= NULL
;
978 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
979 mask
|= XPG_NORM_CODESET
;
981 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
982 language
, territory
== NULL
? "" : "_", territory
?: "",
984 modifier
== NULL
? "" : "@", modifier
?: "");
986 add_alias (ah
, normalized_codeset_name
, replace
,
987 normalized_name
?: name
, &locrec_offset
);
988 free (normalized_codeset_name
);
991 /* Now read the locale.alias files looking for lines whose
992 right hand side matches our name after normalization. */
993 if (alias_file
!= NULL
)
996 fp
= fopen (alias_file
, "rm");
998 error (1, errno
, _("locale alias file `%s' not found"),
1001 /* No threads present. */
1002 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
1004 while (! feof_unlocked (fp
))
1006 /* It is a reasonable approach to use a fix buffer here
1008 a) we are only interested in the first two fields
1009 b) these fields must be usable as file names and so must
1016 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1021 /* Ignore leading white space. */
1022 while (isspace (cp
[0]) && cp
[0] != '\n')
1025 /* A leading '#' signals a comment line. */
1026 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1029 while (cp
[0] != '\0' && !isspace (cp
[0]))
1031 /* Terminate alias name. */
1035 /* Now look for the beginning of the value. */
1036 while (isspace (cp
[0]))
1042 while (cp
[0] != '\0' && !isspace (cp
[0]))
1044 /* Terminate value. */
1047 /* This has to be done to make the following
1048 test for the end of line possible. We are
1049 looking for the terminating '\n' which do not
1054 else if (cp
[0] != '\0')
1057 /* Does this alias refer to our locale? We will
1058 normalize the right hand side and compare the
1059 elements of the normalized form. */
1061 const char *rhs_language
;
1062 const char *rhs_modifier
;
1063 const char *rhs_territory
;
1064 const char *rhs_codeset
;
1065 const char *rhs_normalized_codeset
;
1066 int rhs_mask
= _nl_explode_name (value
,
1071 &rhs_normalized_codeset
);
1072 if (!strcmp (language
, rhs_language
)
1073 && ((rhs_mask
& XPG_CODESET
)
1074 /* He has a codeset, it must match normalized. */
1075 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1076 ? normalized_codeset
: codeset
,
1077 (rhs_mask
& XPG_NORM_CODESET
)
1078 ? rhs_normalized_codeset
: rhs_codeset
)
1079 /* He has no codeset, we must also have none. */
1080 : (mask
& XPG_CODESET
) == 0)
1081 /* Codeset (or lack thereof) matches. */
1082 && !strcmp (territory
?: "", rhs_territory
?: "")
1083 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1084 /* We have a winner. */
1085 add_alias (ah
, alias
, replace
,
1086 normalized_name
?: name
, &locrec_offset
);
1087 if (rhs_mask
& XPG_NORM_CODESET
)
1088 free ((char *) rhs_normalized_codeset
);
1093 /* Possibly not the whole line fits into the buffer.
1094 Ignore the rest of the line. */
1095 while (strchr (cp
, '\n') == NULL
)
1098 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1099 /* Make sure the inner loop will be left. The outer
1100 loop will exit at the `feof' test. */
1108 free (normalized_name
);
1110 if (mask
& XPG_NORM_CODESET
)
1111 free ((char *) normalized_codeset
);
1118 add_locales_to_archive (nlist
, list
, replace
)
1123 struct locarhandle ah
;
1126 /* Open the archive. This call never returns if we cannot
1127 successfully open the archive. */
1128 open_archive (&ah
, false);
1132 const char *fname
= *list
++;
1133 size_t fnamelen
= strlen (fname
);
1142 printf (_("Adding %s\n"), fname
);
1144 /* First see whether this really is a directory and whether it
1145 contains all the require locale category files. */
1146 if (stat64 (fname
, &st
) < 0)
1148 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1152 if (!S_ISDIR (st
.st_mode
))
1154 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1158 dirp
= opendir (fname
);
1161 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1162 fname
, strerror (errno
));
1167 while ((d
= readdir64 (dirp
)) != NULL
)
1169 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1171 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1173 unsigned char d_type
;
1175 /* We have an object of the required name. If it's
1176 a directory we have to look at a file with the
1177 prefix "SYS_". Otherwise we have found what we
1179 #ifdef _DIRENT_HAVE_D_TYPE
1182 if (d_type
!= DT_REG
)
1185 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1187 #ifdef _DIRENT_HAVE_D_TYPE
1188 if (d_type
== DT_UNKNOWN
)
1191 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1194 if (stat64 (fullname
, &st
) == -1)
1195 /* We cannot stat the file, ignore it. */
1198 d_type
= IFTODT (st
.st_mode
);
1201 if (d_type
== DT_DIR
)
1203 /* We have to do more tests. The file is a
1204 directory and it therefore must contain a
1205 regular file with the same name except a
1207 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1208 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1211 if (stat64 (fullname
, &st
) == -1)
1212 /* There is no SYS_* file or we cannot
1216 d_type
= IFTODT (st
.st_mode
);
1220 /* If we found a regular file (eventually after
1221 following a symlink) we are successful. */
1222 if (d_type
== DT_REG
)
1230 if (seen
!= __LC_LAST
- 1)
1232 /* We don't have all locale category files. Ignore the name. */
1233 error (0, 0, _("incomplete set of locale files in \"%s\""),
1238 /* Add the files to the archive. To do this we first compute
1239 sizes and the MD5 sums of all the files. */
1240 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1243 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1246 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1247 fd
= open64 (fullname
, O_RDONLY
);
1248 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1250 /* Cannot read the file. */
1256 if (S_ISDIR (st
.st_mode
))
1260 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1261 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1264 fd
= open64 (fullname
, O_RDONLY
);
1265 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1266 || !S_ISREG (st
.st_mode
))
1275 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1277 if (data
[cnt
].addr
== MAP_FAILED
)
1279 /* Cannot map it. */
1284 data
[cnt
].size
= st
.st_size
;
1285 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1287 /* We don't need the file descriptor anymore. */
1291 if (cnt
!= __LC_LAST
)
1295 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1297 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1302 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1304 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1306 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1310 close_archive (&ah
);
1317 delete_locales_from_archive (nlist
, list
)
1321 struct locarhandle ah
;
1322 struct locarhead
*head
;
1323 struct namehashent
*namehashtab
;
1325 /* Open the archive. This call never returns if we cannot
1326 successfully open the archive. */
1327 open_archive (&ah
, false);
1330 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1331 + head
->namehash_offset
);
1335 const char *locname
= *list
++;
1340 /* Search for this locale in the archive. */
1341 hval
= archive_hashval (locname
, strlen (locname
));
1343 idx
= hval
% head
->namehash_size
;
1344 incr
= 1 + hval
% (head
->namehash_size
- 2);
1346 /* If the name_offset field is zero this means this is no
1347 deleted entry and therefore no entry can be found. */
1348 while (namehashtab
[idx
].name_offset
!= 0)
1350 if (namehashtab
[idx
].hashval
== hval
1351 && (strcmp (locname
,
1352 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1355 /* Found the entry. Now mark it as removed by zero-ing
1356 the reference to the locale record. */
1357 namehashtab
[idx
].locrec_offset
= 0;
1362 if (idx
>= head
->namehash_size
)
1363 idx
-= head
->namehash_size
;
1366 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1367 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1370 close_archive (&ah
);
1379 uint32_t locrec_offset
;
1385 const unsigned char *sum
;
1386 uint32_t file_offset
;
1392 nameentcmp (const void *a
, const void *b
)
1394 return strcmp (((const struct nameent
*) a
)->name
,
1395 ((const struct nameent
*) b
)->name
);
1400 dataentcmp (const void *a
, const void *b
)
1402 if (((const struct dataent
*) a
)->file_offset
1403 < ((const struct dataent
*) b
)->file_offset
)
1406 if (((const struct dataent
*) a
)->file_offset
1407 > ((const struct dataent
*) b
)->file_offset
)
1415 show_archive_content (int verbose
)
1417 struct locarhandle ah
;
1418 struct locarhead
*head
;
1419 struct namehashent
*namehashtab
;
1420 struct nameent
*names
;
1423 /* Open the archive. This call never returns if we cannot
1424 successfully open the archive. */
1425 open_archive (&ah
, true);
1429 names
= (struct nameent
*) xmalloc (head
->namehash_used
1430 * sizeof (struct nameent
));
1432 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1433 + head
->namehash_offset
);
1434 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1435 if (namehashtab
[cnt
].locrec_offset
!= 0)
1437 assert (used
< head
->namehash_used
);
1438 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1439 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1442 /* Sort the names. */
1443 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1447 struct dataent
*files
;
1448 struct sumhashent
*sumhashtab
;
1451 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1452 * sizeof (struct dataent
));
1454 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1455 + head
->sumhash_offset
);
1456 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1457 if (sumhashtab
[cnt
].file_offset
!= 0)
1459 assert (sumused
< head
->sumhash_used
);
1460 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1461 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1462 files
[sumused
++].nlink
= 0;
1465 /* Sort by file locations. */
1466 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1468 /* Compute nlink fields. */
1469 for (cnt
= 0; cnt
< used
; ++cnt
)
1471 struct locrecent
*locrec
;
1474 locrec
= (struct locrecent
*) ((char *) ah
.addr
1475 + names
[cnt
].locrec_offset
);
1476 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1477 if (locrec
->record
[LC_ALL
].offset
!= 0
1479 || (locrec
->record
[idx
].offset
1480 < locrec
->record
[LC_ALL
].offset
)
1481 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1482 > (locrec
->record
[LC_ALL
].offset
1483 + locrec
->record
[LC_ALL
].len
)))
1486 struct dataent
*data
, dataent
;
1488 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1489 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1490 sizeof (struct dataent
),
1492 assert (data
!= NULL
);
1498 for (cnt
= 0; cnt
< used
; ++cnt
)
1500 struct locrecent
*locrec
;
1503 locrec
= (struct locrecent
*) ((char *) ah
.addr
1504 + names
[cnt
].locrec_offset
);
1505 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1508 struct dataent
*data
, dataent
;
1510 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1511 if (locrec
->record
[LC_ALL
].offset
!= 0
1512 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1513 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1514 <= (locrec
->record
[LC_ALL
].offset
1515 + locrec
->record
[LC_ALL
].len
)))
1516 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1518 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1519 sizeof (struct dataent
),
1521 printf ("%6d %7x %3d%c ",
1522 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1524 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1526 for (i
= 0; i
< 16; i
+= 4)
1527 printf ("%02x%02x%02x%02x",
1528 data
->sum
[i
], data
->sum
[i
+ 1],
1529 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1530 printf (" %s/%s\n", names
[cnt
].name
,
1531 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1537 for (cnt
= 0; cnt
< used
; ++cnt
)
1538 puts (names
[cnt
].name
);
1540 close_archive (&ah
);
1542 exit (EXIT_SUCCESS
);