1 /* Copyright (C) 2002, 2003, 2005 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 version 2 as
7 published by the Free Software Foundation.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
32 #include <stdio_ext.h>
38 #include <sys/param.h>
41 #include "../../crypt/md5.h"
42 #include "../localeinfo.h"
43 #include "../locarchive.h"
44 #include "localedef.h"
46 /* Define the hash function. We define the function as static inline.
47 We must change the name so as not to conflict with simple-hash.h. */
48 #define compute_hashval static inline archive_hashval
49 #define hashval_t uint32_t
51 #undef compute_hashval
53 extern const char *output_prefix
;
55 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
57 static const char *locnames
[] =
59 #define DEFINE_CATEGORY(category, category_name, items, a) \
60 [category] = category_name,
61 #include "categories.def"
62 #undef DEFINE_CATEGORY
66 /* Size of the initial archive header. */
67 #define INITIAL_NUM_NAMES 450
68 #define INITIAL_SIZE_STRINGS 3500
69 #define INITIAL_NUM_LOCREC 350
70 #define INITIAL_NUM_SUMS 2000
74 create_archive (const char *archivefname
, struct locarhandle
*ah
)
77 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
78 struct locarhead head
;
82 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
84 /* Create a temporary file in the correct directory. */
87 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
89 /* Create the initial content of the archive. */
90 head
.magic
= AR_MAGIC
;
91 head
.namehash_offset
= sizeof (struct locarhead
);
92 head
.namehash_used
= 0;
93 head
.namehash_size
= next_prime (INITIAL_NUM_NAMES
);
95 head
.string_offset
= (head
.namehash_offset
96 + head
.namehash_size
* sizeof (struct namehashent
));
98 head
.string_size
= INITIAL_SIZE_STRINGS
;
100 head
.locrectab_offset
= head
.string_offset
+ head
.string_size
;
101 head
.locrectab_used
= 0;
102 head
.locrectab_size
= INITIAL_NUM_LOCREC
;
104 head
.sumhash_offset
= (head
.locrectab_offset
105 + head
.locrectab_size
* sizeof (struct locrecent
));
106 head
.sumhash_used
= 0;
107 head
.sumhash_size
= next_prime (INITIAL_NUM_SUMS
);
109 total
= head
.sumhash_offset
+ head
.sumhash_size
* sizeof (struct sumhashent
);
111 /* Write out the header and create room for the other data structures. */
112 if (TEMP_FAILURE_RETRY (write (fd
, &head
, sizeof (head
))) != sizeof (head
))
116 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
119 if (ftruncate64 (fd
, total
) != 0)
123 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
126 /* Map the header and all the administration data structures. */
127 p
= mmap64 (NULL
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
132 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
135 /* Now try to rename it. We don't use the rename function since
136 this would overwrite a file which has been created in
138 if (link (fname
, archivefname
) == -1)
142 /* We cannot use the just created file. */
146 if (errval
== EEXIST
)
148 /* There is already an archive. Must have been a localedef run
149 which happened in parallel. Simply open this file then. */
150 open_archive (ah
, false);
154 error (EXIT_FAILURE
, errval
, _("failed to create new locale archive"));
157 /* Remove the temporary name. */
160 /* Make the file globally readable. */
161 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
164 unlink (archivefname
);
165 error (EXIT_FAILURE
, errval
,
166 _("cannot change mode of new locale archive"));
175 /* This structure and qsort comparator function are used below to sort an
176 old archive's locrec table in order of data position in the file. */
180 struct locrecent
*locrec
;
184 oldlocrecentcmp (const void *a
, const void *b
)
186 struct locrecent
*la
= ((const struct oldlocrecent
*) a
)->locrec
;
187 struct locrecent
*lb
= ((const struct oldlocrecent
*) b
)->locrec
;
188 uint32_t start_a
= -1, end_a
= 0;
189 uint32_t start_b
= -1, end_b
= 0;
192 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
195 if (la
->record
[cnt
].offset
< start_a
)
196 start_a
= la
->record
[cnt
].offset
;
197 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
198 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
200 assert (start_a
!= (uint32_t)-1);
203 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
206 if (lb
->record
[cnt
].offset
< start_b
)
207 start_b
= lb
->record
[cnt
].offset
;
208 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
209 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
211 assert (start_b
!= (uint32_t)-1);
214 if (start_a
!= start_b
)
215 return (int)start_a
- (int)start_b
;
216 return (int)end_a
- (int)end_b
;
220 /* forward decl for below */
221 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
222 locale_data_t data
, bool replace
);
225 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
229 struct locarhead newhead
;
232 unsigned int cnt
, loccnt
;
233 struct namehashent
*oldnamehashtab
;
234 struct locrecent
*oldlocrectab
;
235 struct locarhandle new_ah
;
236 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
237 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
238 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
241 memcpy (archivefname
, output_prefix
, prefix_len
);
242 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
243 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
245 /* Not all of the old file has to be mapped. Change this now this
246 we will have to access the whole content. */
247 if (fstat64 (ah
->fd
, &st
) != 0
248 || (ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
249 MAP_SHARED
, ah
->fd
, 0)) == MAP_FAILED
)
250 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
251 ah
->len
= st
.st_size
;
253 /* Create a temporary file in the correct directory. */
254 fd
= mkstemp (fname
);
256 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
258 /* Copy the existing head information. */
261 /* Create the new archive header. The sizes of the various tables
262 should be double from what is currently used. */
263 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
264 newhead
.namehash_size
);
266 printf ("name: size: %u, used: %d, new: size: %u\n",
267 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
269 newhead
.string_offset
= (newhead
.namehash_offset
270 + (newhead
.namehash_size
271 * sizeof (struct namehashent
)));
272 /* Keep the string table size aligned to 4 bytes, so that
273 all the struct { uint32_t } types following are happy. */
274 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
275 newhead
.string_size
);
277 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
278 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
279 newhead
.locrectab_size
);
281 newhead
.sumhash_offset
= (newhead
.locrectab_offset
282 + (newhead
.locrectab_size
283 * sizeof (struct locrecent
)));
284 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
285 newhead
.sumhash_size
);
287 total
= (newhead
.sumhash_offset
288 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
290 /* The new file is empty now. */
291 newhead
.namehash_used
= 0;
292 newhead
.string_used
= 0;
293 newhead
.locrectab_used
= 0;
294 newhead
.sumhash_used
= 0;
296 /* Write out the header and create room for the other data structures. */
297 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
302 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
305 if (ftruncate64 (fd
, total
) != 0)
309 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
312 /* Map the header and all the administration data structures. */
313 p
= mmap64 (NULL
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
318 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
321 /* Lock the new file. */
322 if (lockf64 (fd
, F_LOCK
, total
) != 0)
326 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
333 /* Walk through the hash name hash table to find out what data is
334 still referenced and transfer it into the new file. */
335 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
336 + head
->namehash_offset
);
337 oldlocrectab
= (struct locrecent
*) ((char *) ah
->addr
338 + head
->locrectab_offset
);
340 /* Sort the old locrec table in order of data position. */
341 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
342 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
343 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
345 oldlocrecarray
[loccnt
].cnt
= cnt
;
346 oldlocrecarray
[loccnt
++].locrec
347 = (struct locrecent
*) ((char *) ah
->addr
348 + oldnamehashtab
[cnt
].locrec_offset
);
350 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
353 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
355 /* Insert this entry in the new hash table. */
356 locale_data_t old_data
;
358 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
360 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
363 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
365 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
367 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
371 if (add_locale (&new_ah
,
373 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
375 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
378 /* Make the file globally readable. */
379 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
383 error (EXIT_FAILURE
, errval
,
384 _("cannot change mode of resized locale archive"));
387 /* Rename the new file. */
388 if (rename (fname
, archivefname
) != 0)
392 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
395 /* Close the old file. */
398 /* Add the information for the new one. */
404 open_archive (struct locarhandle
*ah
, bool readonly
)
409 struct locarhead head
;
411 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
412 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
415 memcpy (archivefname
, output_prefix
, prefix_len
);
416 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
420 /* Open the archive. We must have exclusive write access. */
421 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
424 /* Maybe the file does not yet exist. */
429 static const struct locarhead nullhead
=
432 .namehash_offset
= 0,
436 ah
->addr
= (void *) &nullhead
;
440 create_archive (archivefname
, ah
);
445 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
449 if (fstat64 (fd
, &st
) < 0)
450 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
453 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
457 if (retry
++ < max_locarchive_open_retry
)
461 /* Wait for a bit. */
463 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
464 (void) nanosleep (&req
, NULL
);
469 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
473 /* One more check. Maybe another process replaced the archive file
474 with a new, larger one since we opened the file. */
475 if (stat64 (archivefname
, &st2
) == -1
476 || st
.st_dev
!= st2
.st_dev
477 || st
.st_ino
!= st2
.st_ino
)
479 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
484 /* Leave the loop. */
488 /* Read the header. */
489 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
491 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
492 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
496 ah
->len
= (head
.sumhash_offset
497 + head
.sumhash_size
* sizeof (struct sumhashent
));
499 /* Now we know how large the administrative information part is.
501 ah
->addr
= mmap64 (NULL
, ah
->len
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
503 if (ah
->addr
== MAP_FAILED
)
505 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
506 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
512 close_archive (struct locarhandle
*ah
)
516 munmap (ah
->addr
, ah
->len
);
521 #include "../../intl/explodename.c"
522 #include "../../intl/l10nflist.c"
524 static struct namehashent
*
525 insert_name (struct locarhandle
*ah
,
526 const char *name
, size_t name_len
, bool replace
)
528 const struct locarhead
*const head
= ah
->addr
;
529 struct namehashent
*namehashtab
530 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
531 unsigned int insert_idx
, idx
, incr
;
533 /* Hash value of the locale name. */
534 uint32_t hval
= archive_hashval (name
, name_len
);
537 idx
= hval
% head
->namehash_size
;
538 incr
= 1 + hval
% (head
->namehash_size
- 2);
540 /* If the name_offset field is zero this means this is a
541 deleted entry and therefore no entry can be found. */
542 while (namehashtab
[idx
].name_offset
!= 0)
544 if (namehashtab
[idx
].hashval
== hval
546 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
548 /* Found the entry. */
549 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
552 error (0, 0, _("locale '%s' already exists"), name
);
559 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
561 error (0, 0, "hash collision (%u) %s, %s",
562 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
565 /* Remember the first place we can insert the new entry. */
566 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
570 if (idx
>= head
->namehash_size
)
571 idx
-= head
->namehash_size
;
574 /* Add as early as possible. */
575 if (insert_idx
!= -1)
578 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
579 return &namehashtab
[idx
];
583 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
584 const char *oldname
, uint32_t *locrec_offset_p
)
586 uint32_t locrec_offset
= *locrec_offset_p
;
587 struct locarhead
*head
= ah
->addr
;
588 const size_t name_len
= strlen (alias
);
589 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
591 if (namehashent
== NULL
&& ! replace
)
594 if (namehashent
->name_offset
== 0)
596 /* We are adding a new hash entry for this alias.
597 Determine whether we have to resize the file. */
598 if (head
->string_used
+ name_len
+ 1 > head
->string_size
599 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
601 /* The current archive is not large enough. */
602 enlarge_archive (ah
, head
);
604 /* The locrecent might have moved, so we have to look up
605 the old name afresh. */
606 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
607 assert (namehashent
->name_offset
!= 0);
608 assert (namehashent
->locrec_offset
!= 0);
609 *locrec_offset_p
= namehashent
->locrec_offset
;
611 /* Tail call to try the whole thing again. */
612 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
616 /* Add the name string. */
617 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
618 alias
, name_len
+ 1);
619 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
620 head
->string_used
+= name_len
+ 1;
622 ++head
->namehash_used
;
625 if (namehashent
->locrec_offset
!= 0)
627 /* Replacing an existing entry.
628 Mark that we are no longer using the old locrecent. */
629 struct locrecent
*locrecent
630 = (struct locrecent
*) ((char *) ah
->addr
631 + namehashent
->locrec_offset
);
635 /* Point this entry at the locrecent installed for the main name. */
636 namehashent
->locrec_offset
= locrec_offset
;
639 static int /* qsort comparator used below */
640 cmpcategorysize (const void *a
, const void *b
)
642 if (*(const void **) a
== NULL
)
644 if (*(const void **) b
== NULL
)
646 return ((*(const struct locale_category_data
**) a
)->size
647 - (*(const struct locale_category_data
**) b
)->size
);
650 /* Check the content of the archive for duplicates. Add the content
651 of the files if necessary. Returns the locrec_offset. */
653 add_locale (struct locarhandle
*ah
,
654 const char *name
, locale_data_t data
, bool replace
)
656 /* First look for the name. If it already exists and we are not
657 supposed to replace it don't do anything. If it does not exist
658 we have to allocate a new locale record. */
659 size_t name_len
= strlen (name
);
660 uint32_t file_offsets
[__LC_LAST
];
661 unsigned int num_new_offsets
= 0;
662 struct sumhashent
*sumhashtab
;
664 unsigned int cnt
, idx
;
665 struct locarhead
*head
;
666 struct namehashent
*namehashent
;
668 struct locrecent
*locrecent
;
671 struct locale_category_data
*size_order
[__LC_LAST
];
672 const size_t pagesz
= getpagesize ();
676 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
677 + head
->sumhash_offset
);
679 memset (file_offsets
, 0, sizeof (file_offsets
));
681 size_order
[LC_ALL
] = NULL
;
682 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
684 size_order
[cnt
] = &data
[cnt
];
686 /* Sort the array in ascending order of data size. */
687 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
690 data
[LC_ALL
].size
= 0;
691 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
692 if (size_order
[cnt
] != NULL
)
694 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
695 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
697 /* This category makes the small-categories block
698 stop being small, so this is the end of the road. */
700 size_order
[cnt
++] = NULL
;
701 while (cnt
< __LC_LAST
);
704 data
[LC_ALL
].size
+= rounded_size
;
705 small_mask
|= 1 << (size_order
[cnt
] - data
);
708 /* Copy the data for all the small categories into the LC_ALL
711 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
712 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
714 ptr
= data
[LC_ALL
].addr
;
715 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
716 if (small_mask
& (1 << cnt
))
718 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
719 ptr
+= (data
[cnt
].size
+ 15) & -16;
721 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
723 /* For each locale category data set determine whether the same data
724 is already somewhere in the archive. */
725 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
726 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
730 /* Compute the hash value of the checksum to determine a
731 starting point for the search in the MD5 hash value
733 hval
= archive_hashval (data
[cnt
].sum
, 16);
735 idx
= hval
% head
->sumhash_size
;
736 incr
= 1 + hval
% (head
->sumhash_size
- 2);
738 while (sumhashtab
[idx
].file_offset
!= 0)
740 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
743 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
749 if (idx
>= head
->sumhash_size
)
750 idx
-= head
->sumhash_size
;
754 /* Find a slot for the locale name in the hash table. */
755 namehashent
= insert_name (ah
, name
, name_len
, replace
);
756 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
759 /* Determine whether we have to resize the file. */
760 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
761 || (namehashent
->locrec_offset
== 0
762 && (head
->locrectab_used
== head
->locrectab_size
763 || head
->string_used
+ name_len
+ 1 > head
->string_size
764 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
766 /* The current archive is not large enough. */
767 enlarge_archive (ah
, head
);
768 return add_locale (ah
, name
, data
, replace
);
771 /* Add the locale data which is not yet in the archive. */
772 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
773 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
774 && file_offsets
[cnt
] == 0)
776 /* The data for this section is not yet available in the
777 archive. Append it. */
781 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
782 if (lastpos
== (off64_t
) -1)
783 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
785 /* If block of small categories would cross page boundary,
786 align it unless it immediately follows a large category. */
787 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
788 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
790 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
792 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
793 char *zeros
= alloca (sz
);
795 memset (zeros
, 0, sz
);
796 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
797 error (EXIT_FAILURE
, errno
,
798 _("cannot add to locale archive"));
803 /* Align all data to a 16 byte boundary. */
804 if ((lastpos
& 15) != 0)
806 static const char zeros
[15] = { 0, };
808 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
809 != 16 - (lastpos
& 15))
810 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
812 lastpos
+= 16 - (lastpos
& 15);
815 /* Remember the position. */
816 file_offsets
[cnt
] = lastpos
;
817 lastoffset
= lastpos
+ data
[cnt
].size
;
819 /* Write the data. */
820 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
822 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
824 /* Add the hash value to the hash table. */
825 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
827 idx
= md5hval
% head
->sumhash_size
;
828 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
830 while (sumhashtab
[idx
].file_offset
!= 0)
833 if (idx
>= head
->sumhash_size
)
834 idx
-= head
->sumhash_size
;
837 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
838 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
840 ++head
->sumhash_used
;
843 lastoffset
= file_offsets
[LC_ALL
];
844 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
845 if (small_mask
& (1 << cnt
))
847 file_offsets
[cnt
] = lastoffset
;
848 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
851 if (namehashent
->name_offset
== 0)
853 /* Add the name string. */
854 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
856 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
857 head
->string_used
+= name_len
+ 1;
858 ++head
->namehash_used
;
861 if (namehashent
->locrec_offset
== 0)
863 /* Allocate a name location record. */
864 namehashent
->locrec_offset
= (head
->locrectab_offset
865 + (head
->locrectab_used
++
866 * sizeof (struct locrecent
)));
867 locrecent
= (struct locrecent
*) ((char *) ah
->addr
868 + namehashent
->locrec_offset
);
873 /* If there are other aliases pointing to this locrecent,
874 we still need a new one. If not, reuse the old one. */
876 locrecent
= (struct locrecent
*) ((char *) ah
->addr
877 + namehashent
->locrec_offset
);
878 if (locrecent
->refs
> 1)
881 namehashent
->locrec_offset
= (head
->locrectab_offset
882 + (head
->locrectab_used
++
883 * sizeof (struct locrecent
)));
884 locrecent
= (struct locrecent
*) ((char *) ah
->addr
885 + namehashent
->locrec_offset
);
890 /* Fill in the table with the locations of the locale data. */
891 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
893 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
894 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
897 return namehashent
->locrec_offset
;
901 /* Check the content of the archive for duplicates. Add the content
902 of the files if necessary. Add all the names, possibly overwriting
905 add_locale_to_archive (ah
, name
, data
, replace
)
906 struct locarhandle
*ah
;
911 char *normalized_name
= NULL
;
912 uint32_t locrec_offset
;
914 /* First analyze the name to decide how to archive it. */
915 const char *language
;
916 const char *modifier
;
917 const char *territory
;
919 const char *normalized_codeset
;
920 int mask
= _nl_explode_name (strdupa (name
),
921 &language
, &modifier
, &territory
,
922 &codeset
, &normalized_codeset
);
924 if (mask
& XPG_NORM_CODESET
)
925 /* This name contains a codeset in unnormalized form.
926 We will store it in the archive with a normalized name. */
927 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
928 language
, territory
== NULL
? "" : "_", territory
?: "",
929 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
930 modifier
== NULL
? "" : "@", modifier
?: "");
932 /* This call does the main work. */
933 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
934 if (locrec_offset
== 0)
936 free (normalized_name
);
937 if (mask
& XPG_NORM_CODESET
)
938 free ((char *) normalized_codeset
);
942 if ((mask
& XPG_CODESET
) == 0)
944 /* This name lacks a codeset, so determine the locale's codeset and
945 add an alias for its name with normalized codeset appended. */
950 unsigned int nstrings
;
951 unsigned int strindex
[0];
952 } *filedata
= data
[LC_CTYPE
].addr
;
953 codeset
= (char *) filedata
954 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
955 char *normalized_codeset_name
= NULL
;
957 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
958 mask
|= XPG_NORM_CODESET
;
960 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
961 language
, territory
== NULL
? "" : "_", territory
?: "",
963 modifier
== NULL
? "" : "@", modifier
?: "");
965 add_alias (ah
, normalized_codeset_name
, replace
,
966 normalized_name
?: name
, &locrec_offset
);
967 free (normalized_codeset_name
);
970 /* Now read the locale.alias files looking for lines whose
971 right hand side matches our name after normalization. */
972 if (alias_file
!= NULL
)
975 fp
= fopen (alias_file
, "rm");
977 error (1, errno
, _("locale alias file `%s' not found"),
980 /* No threads present. */
981 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
983 while (! feof_unlocked (fp
))
985 /* It is a reasonable approach to use a fix buffer here
987 a) we are only interested in the first two fields
988 b) these fields must be usable as file names and so must
995 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1000 /* Ignore leading white space. */
1001 while (isspace (cp
[0]) && cp
[0] != '\n')
1004 /* A leading '#' signals a comment line. */
1005 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1008 while (cp
[0] != '\0' && !isspace (cp
[0]))
1010 /* Terminate alias name. */
1014 /* Now look for the beginning of the value. */
1015 while (isspace (cp
[0]))
1021 while (cp
[0] != '\0' && !isspace (cp
[0]))
1023 /* Terminate value. */
1026 /* This has to be done to make the following
1027 test for the end of line possible. We are
1028 looking for the terminating '\n' which do not
1033 else if (cp
[0] != '\0')
1036 /* Does this alias refer to our locale? We will
1037 normalize the right hand side and compare the
1038 elements of the normalized form. */
1040 const char *rhs_language
;
1041 const char *rhs_modifier
;
1042 const char *rhs_territory
;
1043 const char *rhs_codeset
;
1044 const char *rhs_normalized_codeset
;
1045 int rhs_mask
= _nl_explode_name (value
,
1050 &rhs_normalized_codeset
);
1051 if (!strcmp (language
, rhs_language
)
1052 && ((rhs_mask
& XPG_CODESET
)
1053 /* He has a codeset, it must match normalized. */
1054 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1055 ? normalized_codeset
: codeset
,
1056 (rhs_mask
& XPG_NORM_CODESET
)
1057 ? rhs_normalized_codeset
: rhs_codeset
)
1058 /* He has no codeset, we must also have none. */
1059 : (mask
& XPG_CODESET
) == 0)
1060 /* Codeset (or lack thereof) matches. */
1061 && !strcmp (territory
?: "", rhs_territory
?: "")
1062 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1063 /* We have a winner. */
1064 add_alias (ah
, alias
, replace
,
1065 normalized_name
?: name
, &locrec_offset
);
1066 if (rhs_mask
& XPG_NORM_CODESET
)
1067 free ((char *) rhs_normalized_codeset
);
1072 /* Possibly not the whole line fits into the buffer.
1073 Ignore the rest of the line. */
1074 while (strchr (cp
, '\n') == NULL
)
1077 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1078 /* Make sure the inner loop will be left. The outer
1079 loop will exit at the `feof' test. */
1087 free (normalized_name
);
1089 if (mask
& XPG_NORM_CODESET
)
1090 free ((char *) normalized_codeset
);
1097 add_locales_to_archive (nlist
, list
, replace
)
1102 struct locarhandle ah
;
1105 /* Open the archive. This call never returns if we cannot
1106 successfully open the archive. */
1107 open_archive (&ah
, false);
1111 const char *fname
= *list
++;
1112 size_t fnamelen
= strlen (fname
);
1121 printf (_("Adding %s\n"), fname
);
1123 /* First see whether this really is a directory and whether it
1124 contains all the require locale category files. */
1125 if (stat64 (fname
, &st
) < 0)
1127 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1131 if (!S_ISDIR (st
.st_mode
))
1133 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1137 dirp
= opendir (fname
);
1140 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1141 fname
, strerror (errno
));
1146 while ((d
= readdir64 (dirp
)) != NULL
)
1148 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1150 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1152 unsigned char d_type
;
1154 /* We have an object of the required name. If it's
1155 a directory we have to look at a file with the
1156 prefix "SYS_". Otherwise we have found what we
1158 #ifdef _DIRENT_HAVE_D_TYPE
1161 if (d_type
!= DT_REG
)
1164 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1166 #ifdef _DIRENT_HAVE_D_TYPE
1167 if (d_type
== DT_UNKNOWN
)
1170 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1173 if (stat64 (fullname
, &st
) == -1)
1174 /* We cannot stat the file, ignore it. */
1177 d_type
= IFTODT (st
.st_mode
);
1180 if (d_type
== DT_DIR
)
1182 /* We have to do more tests. The file is a
1183 directory and it therefore must contain a
1184 regular file with the same name except a
1186 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1187 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1190 if (stat64 (fullname
, &st
) == -1)
1191 /* There is no SYS_* file or we cannot
1195 d_type
= IFTODT (st
.st_mode
);
1199 /* If we found a regular file (eventually after
1200 following a symlink) we are successful. */
1201 if (d_type
== DT_REG
)
1209 if (seen
!= __LC_LAST
- 1)
1211 /* We don't have all locale category files. Ignore the name. */
1212 error (0, 0, _("incomplete set of locale files in \"%s\""),
1217 /* Add the files to the archive. To do this we first compute
1218 sizes and the MD5 sums of all the files. */
1219 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1222 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1225 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1226 fd
= open64 (fullname
, O_RDONLY
);
1227 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1229 /* Cannot read the file. */
1235 if (S_ISDIR (st
.st_mode
))
1239 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1240 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1243 fd
= open64 (fullname
, O_RDONLY
);
1244 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1245 || !S_ISREG (st
.st_mode
))
1254 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1256 if (data
[cnt
].addr
== MAP_FAILED
)
1258 /* Cannot map it. */
1263 data
[cnt
].size
= st
.st_size
;
1264 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1266 /* We don't need the file descriptor anymore. */
1270 if (cnt
!= __LC_LAST
)
1274 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1276 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1281 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1283 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1285 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1289 close_archive (&ah
);
1296 delete_locales_from_archive (nlist
, list
)
1300 struct locarhandle ah
;
1301 struct locarhead
*head
;
1302 struct namehashent
*namehashtab
;
1304 /* Open the archive. This call never returns if we cannot
1305 successfully open the archive. */
1306 open_archive (&ah
, false);
1309 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1310 + head
->namehash_offset
);
1314 const char *locname
= *list
++;
1319 /* Search for this locale in the archive. */
1320 hval
= archive_hashval (locname
, strlen (locname
));
1322 idx
= hval
% head
->namehash_size
;
1323 incr
= 1 + hval
% (head
->namehash_size
- 2);
1325 /* If the name_offset field is zero this means this is no
1326 deleted entry and therefore no entry can be found. */
1327 while (namehashtab
[idx
].name_offset
!= 0)
1329 if (namehashtab
[idx
].hashval
== hval
1330 && (strcmp (locname
,
1331 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1334 /* Found the entry. Now mark it as removed by zero-ing
1335 the reference to the locale record. */
1336 namehashtab
[idx
].locrec_offset
= 0;
1341 if (idx
>= head
->namehash_size
)
1342 idx
-= head
->namehash_size
;
1345 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1346 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1349 close_archive (&ah
);
1358 uint32_t locrec_offset
;
1364 const unsigned char *sum
;
1365 uint32_t file_offset
;
1371 nameentcmp (const void *a
, const void *b
)
1373 return strcmp (((const struct nameent
*) a
)->name
,
1374 ((const struct nameent
*) b
)->name
);
1379 dataentcmp (const void *a
, const void *b
)
1381 if (((const struct dataent
*) a
)->file_offset
1382 < ((const struct dataent
*) b
)->file_offset
)
1385 if (((const struct dataent
*) a
)->file_offset
1386 > ((const struct dataent
*) b
)->file_offset
)
1394 show_archive_content (int verbose
)
1396 struct locarhandle ah
;
1397 struct locarhead
*head
;
1398 struct namehashent
*namehashtab
;
1399 struct nameent
*names
;
1402 /* Open the archive. This call never returns if we cannot
1403 successfully open the archive. */
1404 open_archive (&ah
, true);
1408 names
= (struct nameent
*) xmalloc (head
->namehash_used
1409 * sizeof (struct nameent
));
1411 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1412 + head
->namehash_offset
);
1413 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1414 if (namehashtab
[cnt
].locrec_offset
!= 0)
1416 assert (used
< head
->namehash_used
);
1417 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1418 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1421 /* Sort the names. */
1422 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1426 struct dataent
*files
;
1427 struct sumhashent
*sumhashtab
;
1430 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1431 * sizeof (struct sumhashent
));
1433 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1434 + head
->sumhash_offset
);
1435 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1436 if (sumhashtab
[cnt
].file_offset
!= 0)
1438 assert (sumused
< head
->sumhash_used
);
1439 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1440 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1441 files
[sumused
++].nlink
= 0;
1444 /* Sort by file locations. */
1445 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1447 /* Compute nlink fields. */
1448 for (cnt
= 0; cnt
< used
; ++cnt
)
1450 struct locrecent
*locrec
;
1453 locrec
= (struct locrecent
*) ((char *) ah
.addr
1454 + names
[cnt
].locrec_offset
);
1455 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1456 if (locrec
->record
[LC_ALL
].offset
!= 0
1458 || (locrec
->record
[idx
].offset
1459 < locrec
->record
[LC_ALL
].offset
)
1460 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1461 > (locrec
->record
[LC_ALL
].offset
1462 + locrec
->record
[LC_ALL
].len
)))
1465 struct dataent
*data
, dataent
;
1467 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1468 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1469 sizeof (struct dataent
),
1471 assert (data
!= NULL
);
1477 for (cnt
= 0; cnt
< used
; ++cnt
)
1479 struct locrecent
*locrec
;
1482 locrec
= (struct locrecent
*) ((char *) ah
.addr
1483 + names
[cnt
].locrec_offset
);
1484 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1487 struct dataent
*data
, dataent
;
1489 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1490 if (locrec
->record
[LC_ALL
].offset
!= 0
1491 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1492 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1493 <= (locrec
->record
[LC_ALL
].offset
1494 + locrec
->record
[LC_ALL
].len
)))
1495 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1497 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1498 sizeof (struct dataent
),
1500 printf ("%6d %7x %3d%c ",
1501 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1503 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1505 for (i
= 0; i
< 16; i
+= 4)
1506 printf ("%02x%02x%02x%02x",
1507 data
->sum
[i
], data
->sum
[i
+ 1],
1508 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1509 printf (" %s/%s\n", names
[cnt
].name
,
1510 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1516 for (cnt
= 0; cnt
< used
; ++cnt
)
1517 puts (names
[cnt
].name
);
1519 close_archive (&ah
);
1521 exit (EXIT_SUCCESS
);