1 /* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
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
34 #include <stdio_ext.h>
40 #include <sys/param.h>
43 #include "../../crypt/md5.h"
44 #include "../localeinfo.h"
45 #include "../locarchive.h"
46 #include "localedef.h"
48 /* Define the hash function. We define the function as static inline.
49 We must change the name so as not to conflict with simple-hash.h. */
50 #define compute_hashval static inline archive_hashval
51 #define hashval_t uint32_t
53 #undef compute_hashval
55 extern const char *output_prefix
;
57 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
59 static const char *locnames
[] =
61 #define DEFINE_CATEGORY(category, category_name, items, a) \
62 [category] = category_name,
63 #include "categories.def"
64 #undef DEFINE_CATEGORY
68 /* Size of the initial archive header. */
69 #define INITIAL_NUM_NAMES 450
70 #define INITIAL_SIZE_STRINGS 3500
71 #define INITIAL_NUM_LOCREC 350
72 #define INITIAL_NUM_SUMS 2000
76 create_archive (const char *archivefname
, struct locarhandle
*ah
)
79 char fname
[strlen (archivefname
) + sizeof (".XXXXXX")];
80 struct locarhead head
;
84 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
86 /* Create a temporary file in the correct directory. */
89 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
91 /* Create the initial content of the archive. */
92 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 decl for below */
223 static uint32_t add_locale (struct locarhandle
*ah
, const char *name
,
224 locale_data_t data
, bool replace
);
227 enlarge_archive (struct locarhandle
*ah
, const struct locarhead
*head
)
231 struct locarhead newhead
;
234 unsigned int cnt
, loccnt
;
235 struct namehashent
*oldnamehashtab
;
236 struct locrecent
*oldlocrectab
;
237 struct locarhandle new_ah
;
238 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
239 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
240 char fname
[prefix_len
+ sizeof (ARCHIVE_NAME
) + sizeof (".XXXXXX") - 1];
243 memcpy (archivefname
, output_prefix
, prefix_len
);
244 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
245 strcpy (stpcpy (fname
, archivefname
), ".XXXXXX");
247 /* Not all of the old file has to be mapped. Change this now this
248 we will have to access the whole content. */
249 if (fstat64 (ah
->fd
, &st
) != 0
250 || (ah
->addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
| PROT_WRITE
,
251 MAP_SHARED
, ah
->fd
, 0)) == MAP_FAILED
)
252 error (EXIT_FAILURE
, errno
, _("cannot map locale archive file"));
253 ah
->len
= st
.st_size
;
255 /* Create a temporary file in the correct directory. */
256 fd
= mkstemp (fname
);
258 error (EXIT_FAILURE
, errno
, _("cannot create temporary file"));
260 /* Copy the existing head information. */
263 /* Create the new archive header. The sizes of the various tables
264 should be double from what is currently used. */
265 newhead
.namehash_size
= MAX (next_prime (2 * newhead
.namehash_used
),
266 newhead
.namehash_size
);
268 printf ("name: size: %u, used: %d, new: size: %u\n",
269 head
->namehash_size
, head
->namehash_used
, newhead
.namehash_size
);
271 newhead
.string_offset
= (newhead
.namehash_offset
272 + (newhead
.namehash_size
273 * sizeof (struct namehashent
)));
274 /* Keep the string table size aligned to 4 bytes, so that
275 all the struct { uint32_t } types following are happy. */
276 newhead
.string_size
= MAX ((2 * newhead
.string_used
+ 3) & -4,
277 newhead
.string_size
);
279 newhead
.locrectab_offset
= newhead
.string_offset
+ newhead
.string_size
;
280 newhead
.locrectab_size
= MAX (2 * newhead
.locrectab_used
,
281 newhead
.locrectab_size
);
283 newhead
.sumhash_offset
= (newhead
.locrectab_offset
284 + (newhead
.locrectab_size
285 * sizeof (struct locrecent
)));
286 newhead
.sumhash_size
= MAX (next_prime (2 * newhead
.sumhash_used
),
287 newhead
.sumhash_size
);
289 total
= (newhead
.sumhash_offset
290 + newhead
.sumhash_size
* sizeof (struct sumhashent
));
292 /* The new file is empty now. */
293 newhead
.namehash_used
= 0;
294 newhead
.string_used
= 0;
295 newhead
.locrectab_used
= 0;
296 newhead
.sumhash_used
= 0;
298 /* Write out the header and create room for the other data structures. */
299 if (TEMP_FAILURE_RETRY (write (fd
, &newhead
, sizeof (newhead
)))
304 error (EXIT_FAILURE
, errval
, _("cannot initialize archive file"));
307 if (ftruncate64 (fd
, total
) != 0)
311 error (EXIT_FAILURE
, errval
, _("cannot resize archive file"));
314 /* Map the header and all the administration data structures. */
315 p
= mmap64 (NULL
, total
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
320 error (EXIT_FAILURE
, errval
, _("cannot map archive header"));
323 /* Lock the new file. */
324 if (lockf64 (fd
, F_LOCK
, total
) != 0)
328 error (EXIT_FAILURE
, errval
, _("cannot lock new archive"));
335 /* Walk through the hash name hash table to find out what data is
336 still referenced and transfer it into the new file. */
337 oldnamehashtab
= (struct namehashent
*) ((char *) ah
->addr
338 + head
->namehash_offset
);
339 oldlocrectab
= (struct locrecent
*) ((char *) ah
->addr
340 + head
->locrectab_offset
);
342 /* Sort the old locrec table in order of data position. */
343 struct oldlocrecent oldlocrecarray
[head
->namehash_size
];
344 for (cnt
= 0, loccnt
= 0; cnt
< head
->namehash_size
; ++cnt
)
345 if (oldnamehashtab
[cnt
].locrec_offset
!= 0)
347 oldlocrecarray
[loccnt
].cnt
= cnt
;
348 oldlocrecarray
[loccnt
++].locrec
349 = (struct locrecent
*) ((char *) ah
->addr
350 + oldnamehashtab
[cnt
].locrec_offset
);
352 qsort (oldlocrecarray
, loccnt
, sizeof (struct oldlocrecent
),
355 for (cnt
= 0; cnt
< loccnt
; ++cnt
)
357 /* Insert this entry in the new hash table. */
358 locale_data_t old_data
;
360 struct locrecent
*oldlocrec
= oldlocrecarray
[cnt
].locrec
;
362 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
365 old_data
[idx
].size
= oldlocrec
->record
[idx
].len
;
367 = ((char *) ah
->addr
+ oldlocrec
->record
[idx
].offset
);
369 __md5_buffer (old_data
[idx
].addr
, old_data
[idx
].size
,
373 if (add_locale (&new_ah
,
375 + oldnamehashtab
[oldlocrecarray
[cnt
].cnt
].name_offset
),
377 error (EXIT_FAILURE
, 0, _("cannot extend locale archive file"));
380 /* Make the file globally readable. */
381 if (fchmod (fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) == -1)
385 error (EXIT_FAILURE
, errval
,
386 _("cannot change mode of resized locale archive"));
389 /* Rename the new file. */
390 if (rename (fname
, archivefname
) != 0)
394 error (EXIT_FAILURE
, errval
, _("cannot rename new archive"));
397 /* Close the old file. */
400 /* Add the information for the new one. */
406 open_archive (struct locarhandle
*ah
, bool readonly
)
411 struct locarhead head
;
413 size_t prefix_len
= output_prefix
? strlen (output_prefix
) : 0;
414 char archivefname
[prefix_len
+ sizeof (ARCHIVE_NAME
)];
417 memcpy (archivefname
, output_prefix
, prefix_len
);
418 strcpy (archivefname
+ prefix_len
, ARCHIVE_NAME
);
422 /* Open the archive. We must have exclusive write access. */
423 fd
= open64 (archivefname
, readonly
? O_RDONLY
: O_RDWR
);
426 /* Maybe the file does not yet exist. */
431 static const struct locarhead nullhead
=
434 .namehash_offset
= 0,
438 ah
->addr
= (void *) &nullhead
;
442 create_archive (archivefname
, ah
);
447 error (EXIT_FAILURE
, errno
, _("cannot open locale archive \"%s\""),
451 if (fstat64 (fd
, &st
) < 0)
452 error (EXIT_FAILURE
, errno
, _("cannot stat locale archive \"%s\""),
455 if (!readonly
&& lockf64 (fd
, F_LOCK
, sizeof (struct locarhead
)) == -1)
459 if (retry
++ < max_locarchive_open_retry
)
463 /* Wait for a bit. */
465 req
.tv_nsec
= 1000000 * (random () % 500 + 1);
466 (void) nanosleep (&req
, NULL
);
471 error (EXIT_FAILURE
, errno
, _("cannot lock locale archive \"%s\""),
475 /* One more check. Maybe another process replaced the archive file
476 with a new, larger one since we opened the file. */
477 if (stat64 (archivefname
, &st2
) == -1
478 || st
.st_dev
!= st2
.st_dev
479 || st
.st_ino
!= st2
.st_ino
)
481 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
486 /* Leave the loop. */
490 /* Read the header. */
491 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
493 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
494 error (EXIT_FAILURE
, errno
, _("cannot read archive header"));
498 ah
->len
= (head
.sumhash_offset
499 + head
.sumhash_size
* sizeof (struct sumhashent
));
501 /* Now we know how large the administrative information part is.
503 ah
->addr
= mmap64 (NULL
, ah
->len
, PROT_READ
| (readonly
? 0 : PROT_WRITE
),
505 if (ah
->addr
== MAP_FAILED
)
507 (void) lockf64 (fd
, F_ULOCK
, sizeof (struct locarhead
));
508 error (EXIT_FAILURE
, errno
, _("cannot map archive header"));
514 close_archive (struct locarhandle
*ah
)
518 munmap (ah
->addr
, ah
->len
);
523 #include "../../intl/explodename.c"
524 #include "../../intl/l10nflist.c"
526 static struct namehashent
*
527 insert_name (struct locarhandle
*ah
,
528 const char *name
, size_t name_len
, bool replace
)
530 const struct locarhead
*const head
= ah
->addr
;
531 struct namehashent
*namehashtab
532 = (struct namehashent
*) ((char *) ah
->addr
+ head
->namehash_offset
);
533 unsigned int insert_idx
, idx
, incr
;
535 /* Hash value of the locale name. */
536 uint32_t hval
= archive_hashval (name
, name_len
);
539 idx
= hval
% head
->namehash_size
;
540 incr
= 1 + hval
% (head
->namehash_size
- 2);
542 /* If the name_offset field is zero this means this is a
543 deleted entry and therefore no entry can be found. */
544 while (namehashtab
[idx
].name_offset
!= 0)
546 if (namehashtab
[idx
].hashval
== hval
548 (char *) ah
->addr
+ namehashtab
[idx
].name_offset
) == 0)
550 /* Found the entry. */
551 if (namehashtab
[idx
].locrec_offset
!= 0 && ! replace
)
554 error (0, 0, _("locale '%s' already exists"), name
);
561 if (namehashtab
[idx
].hashval
== hval
&& ! be_quiet
)
563 error (0, 0, "hash collision (%u) %s, %s",
564 hval
, name
, (char *) ah
->addr
+ namehashtab
[idx
].name_offset
);
567 /* Remember the first place we can insert the new entry. */
568 if (namehashtab
[idx
].locrec_offset
== 0 && insert_idx
== -1)
572 if (idx
>= head
->namehash_size
)
573 idx
-= head
->namehash_size
;
576 /* Add as early as possible. */
577 if (insert_idx
!= -1)
580 namehashtab
[idx
].hashval
= hval
; /* no-op if replacing an old entry. */
581 return &namehashtab
[idx
];
585 add_alias (struct locarhandle
*ah
, const char *alias
, bool replace
,
586 const char *oldname
, uint32_t *locrec_offset_p
)
588 uint32_t locrec_offset
= *locrec_offset_p
;
589 struct locarhead
*head
= ah
->addr
;
590 const size_t name_len
= strlen (alias
);
591 struct namehashent
*namehashent
= insert_name (ah
, alias
, strlen (alias
),
593 if (namehashent
== NULL
&& ! replace
)
596 if (namehashent
->name_offset
== 0)
598 /* We are adding a new hash entry for this alias.
599 Determine whether we have to resize the file. */
600 if (head
->string_used
+ name_len
+ 1 > head
->string_size
601 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)
603 /* The current archive is not large enough. */
604 enlarge_archive (ah
, head
);
606 /* The locrecent might have moved, so we have to look up
607 the old name afresh. */
608 namehashent
= insert_name (ah
, oldname
, strlen (oldname
), true);
609 assert (namehashent
->name_offset
!= 0);
610 assert (namehashent
->locrec_offset
!= 0);
611 *locrec_offset_p
= namehashent
->locrec_offset
;
613 /* Tail call to try the whole thing again. */
614 add_alias (ah
, alias
, replace
, oldname
, locrec_offset_p
);
618 /* Add the name string. */
619 memcpy (ah
->addr
+ head
->string_offset
+ head
->string_used
,
620 alias
, name_len
+ 1);
621 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
622 head
->string_used
+= name_len
+ 1;
624 ++head
->namehash_used
;
627 if (namehashent
->locrec_offset
!= 0)
629 /* Replacing an existing entry.
630 Mark that we are no longer using the old locrecent. */
631 struct locrecent
*locrecent
632 = (struct locrecent
*) ((char *) ah
->addr
633 + namehashent
->locrec_offset
);
637 /* Point this entry at the locrecent installed for the main name. */
638 namehashent
->locrec_offset
= locrec_offset
;
641 static int /* qsort comparator used below */
642 cmpcategorysize (const void *a
, const void *b
)
644 if (*(const void **) a
== NULL
)
646 if (*(const void **) b
== NULL
)
648 return ((*(const struct locale_category_data
**) a
)->size
649 - (*(const struct locale_category_data
**) b
)->size
);
652 /* Check the content of the archive for duplicates. Add the content
653 of the files if necessary. Returns the locrec_offset. */
655 add_locale (struct locarhandle
*ah
,
656 const char *name
, locale_data_t data
, bool replace
)
658 /* First look for the name. If it already exists and we are not
659 supposed to replace it don't do anything. If it does not exist
660 we have to allocate a new locale record. */
661 size_t name_len
= strlen (name
);
662 uint32_t file_offsets
[__LC_LAST
];
663 unsigned int num_new_offsets
= 0;
664 struct sumhashent
*sumhashtab
;
666 unsigned int cnt
, idx
;
667 struct locarhead
*head
;
668 struct namehashent
*namehashent
;
670 struct locrecent
*locrecent
;
673 struct locale_category_data
*size_order
[__LC_LAST
];
674 const size_t pagesz
= getpagesize ();
678 sumhashtab
= (struct sumhashent
*) ((char *) ah
->addr
679 + head
->sumhash_offset
);
681 memset (file_offsets
, 0, sizeof (file_offsets
));
683 size_order
[LC_ALL
] = NULL
;
684 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
686 size_order
[cnt
] = &data
[cnt
];
688 /* Sort the array in ascending order of data size. */
689 qsort (size_order
, __LC_LAST
, sizeof size_order
[0], cmpcategorysize
);
692 data
[LC_ALL
].size
= 0;
693 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
694 if (size_order
[cnt
] != NULL
)
696 const size_t rounded_size
= (size_order
[cnt
]->size
+ 15) & -16;
697 if (data
[LC_ALL
].size
+ rounded_size
> 2 * pagesz
)
699 /* This category makes the small-categories block
700 stop being small, so this is the end of the road. */
702 size_order
[cnt
++] = NULL
;
703 while (cnt
< __LC_LAST
);
706 data
[LC_ALL
].size
+= rounded_size
;
707 small_mask
|= 1 << (size_order
[cnt
] - data
);
710 /* Copy the data for all the small categories into the LC_ALL
713 data
[LC_ALL
].addr
= alloca (data
[LC_ALL
].size
);
714 memset (data
[LC_ALL
].addr
, 0, data
[LC_ALL
].size
);
716 ptr
= data
[LC_ALL
].addr
;
717 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
718 if (small_mask
& (1 << cnt
))
720 memcpy (ptr
, data
[cnt
].addr
, data
[cnt
].size
);
721 ptr
+= (data
[cnt
].size
+ 15) & -16;
723 __md5_buffer (data
[LC_ALL
].addr
, data
[LC_ALL
].size
, data
[LC_ALL
].sum
);
725 /* For each locale category data set determine whether the same data
726 is already somewhere in the archive. */
727 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
728 if (small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
732 /* Compute the hash value of the checksum to determine a
733 starting point for the search in the MD5 hash value
735 hval
= archive_hashval (data
[cnt
].sum
, 16);
737 idx
= hval
% head
->sumhash_size
;
738 incr
= 1 + hval
% (head
->sumhash_size
- 2);
740 while (sumhashtab
[idx
].file_offset
!= 0)
742 if (memcmp (data
[cnt
].sum
, sumhashtab
[idx
].sum
, 16) == 0)
745 file_offsets
[cnt
] = sumhashtab
[idx
].file_offset
;
751 if (idx
>= head
->sumhash_size
)
752 idx
-= head
->sumhash_size
;
756 /* Find a slot for the locale name in the hash table. */
757 namehashent
= insert_name (ah
, name
, name_len
, replace
);
758 if (namehashent
== NULL
) /* Already exists and !REPLACE. */
761 /* Determine whether we have to resize the file. */
762 if (100 * (head
->sumhash_used
+ num_new_offsets
) > 75 * head
->sumhash_size
763 || (namehashent
->locrec_offset
== 0
764 && (head
->locrectab_used
== head
->locrectab_size
765 || head
->string_used
+ name_len
+ 1 > head
->string_size
766 || 100 * head
->namehash_used
> 75 * head
->namehash_size
)))
768 /* The current archive is not large enough. */
769 enlarge_archive (ah
, head
);
770 return add_locale (ah
, name
, data
, replace
);
773 /* Add the locale data which is not yet in the archive. */
774 for (cnt
= 0, lastoffset
= 0; cnt
< __LC_LAST
; ++cnt
)
775 if ((small_mask
== 0 ? cnt
!= LC_ALL
: !(small_mask
& (1 << cnt
)))
776 && file_offsets
[cnt
] == 0)
778 /* The data for this section is not yet available in the
779 archive. Append it. */
783 lastpos
= lseek64 (ah
->fd
, 0, SEEK_END
);
784 if (lastpos
== (off64_t
) -1)
785 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
787 /* If block of small categories would cross page boundary,
788 align it unless it immediately follows a large category. */
789 if (cnt
== LC_ALL
&& lastoffset
!= lastpos
790 && ((((lastpos
& (pagesz
- 1)) + data
[cnt
].size
+ pagesz
- 1)
792 > ((data
[cnt
].size
+ pagesz
- 1) & -pagesz
)))
794 size_t sz
= pagesz
- (lastpos
& (pagesz
- 1));
795 char *zeros
= alloca (sz
);
797 memset (zeros
, 0, sz
);
798 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, sz
) != sz
))
799 error (EXIT_FAILURE
, errno
,
800 _("cannot add to locale archive"));
805 /* Align all data to a 16 byte boundary. */
806 if ((lastpos
& 15) != 0)
808 static const char zeros
[15] = { 0, };
810 if (TEMP_FAILURE_RETRY (write (ah
->fd
, zeros
, 16 - (lastpos
& 15)))
811 != 16 - (lastpos
& 15))
812 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
814 lastpos
+= 16 - (lastpos
& 15);
817 /* Remember the position. */
818 file_offsets
[cnt
] = lastpos
;
819 lastoffset
= lastpos
+ data
[cnt
].size
;
821 /* Write the data. */
822 if (TEMP_FAILURE_RETRY (write (ah
->fd
, data
[cnt
].addr
, data
[cnt
].size
))
824 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
826 /* Add the hash value to the hash table. */
827 md5hval
= archive_hashval (data
[cnt
].sum
, 16);
829 idx
= md5hval
% head
->sumhash_size
;
830 incr
= 1 + md5hval
% (head
->sumhash_size
- 2);
832 while (sumhashtab
[idx
].file_offset
!= 0)
835 if (idx
>= head
->sumhash_size
)
836 idx
-= head
->sumhash_size
;
839 memcpy (sumhashtab
[idx
].sum
, data
[cnt
].sum
, 16);
840 sumhashtab
[idx
].file_offset
= file_offsets
[cnt
];
842 ++head
->sumhash_used
;
845 lastoffset
= file_offsets
[LC_ALL
];
846 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
847 if (small_mask
& (1 << cnt
))
849 file_offsets
[cnt
] = lastoffset
;
850 lastoffset
+= (data
[cnt
].size
+ 15) & -16;
853 if (namehashent
->name_offset
== 0)
855 /* Add the name string. */
856 memcpy ((char *) ah
->addr
+ head
->string_offset
+ head
->string_used
,
858 namehashent
->name_offset
= head
->string_offset
+ head
->string_used
;
859 head
->string_used
+= name_len
+ 1;
860 ++head
->namehash_used
;
863 if (namehashent
->locrec_offset
== 0)
865 /* Allocate a name location record. */
866 namehashent
->locrec_offset
= (head
->locrectab_offset
867 + (head
->locrectab_used
++
868 * sizeof (struct locrecent
)));
869 locrecent
= (struct locrecent
*) ((char *) ah
->addr
870 + namehashent
->locrec_offset
);
875 /* If there are other aliases pointing to this locrecent,
876 we still need a new one. If not, reuse the old one. */
878 locrecent
= (struct locrecent
*) ((char *) ah
->addr
879 + namehashent
->locrec_offset
);
880 if (locrecent
->refs
> 1)
883 namehashent
->locrec_offset
= (head
->locrectab_offset
884 + (head
->locrectab_used
++
885 * sizeof (struct locrecent
)));
886 locrecent
= (struct locrecent
*) ((char *) ah
->addr
887 + namehashent
->locrec_offset
);
892 /* Fill in the table with the locations of the locale data. */
893 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
895 locrecent
->record
[cnt
].offset
= file_offsets
[cnt
];
896 locrecent
->record
[cnt
].len
= data
[cnt
].size
;
899 return namehashent
->locrec_offset
;
903 /* Check the content of the archive for duplicates. Add the content
904 of the files if necessary. Add all the names, possibly overwriting
907 add_locale_to_archive (ah
, name
, data
, replace
)
908 struct locarhandle
*ah
;
913 char *normalized_name
= NULL
;
914 uint32_t locrec_offset
;
916 /* First analyze the name to decide how to archive it. */
917 const char *language
;
918 const char *modifier
;
919 const char *territory
;
921 const char *normalized_codeset
;
922 int mask
= _nl_explode_name (strdupa (name
),
923 &language
, &modifier
, &territory
,
924 &codeset
, &normalized_codeset
);
926 if (mask
& XPG_NORM_CODESET
)
927 /* This name contains a codeset in unnormalized form.
928 We will store it in the archive with a normalized name. */
929 asprintf (&normalized_name
, "%s%s%s.%s%s%s",
930 language
, territory
== NULL
? "" : "_", territory
?: "",
931 (mask
& XPG_NORM_CODESET
) ? normalized_codeset
: codeset
,
932 modifier
== NULL
? "" : "@", modifier
?: "");
934 /* This call does the main work. */
935 locrec_offset
= add_locale (ah
, normalized_name
?: name
, data
, replace
);
936 if (locrec_offset
== 0)
938 free (normalized_name
);
939 if (mask
& XPG_NORM_CODESET
)
940 free ((char *) normalized_codeset
);
944 if ((mask
& XPG_CODESET
) == 0)
946 /* This name lacks a codeset, so determine the locale's codeset and
947 add an alias for its name with normalized codeset appended. */
952 unsigned int nstrings
;
953 unsigned int strindex
[0];
954 } *filedata
= data
[LC_CTYPE
].addr
;
955 codeset
= (char *) filedata
956 + filedata
->strindex
[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME
)];
957 char *normalized_codeset_name
= NULL
;
959 normalized_codeset
= _nl_normalize_codeset (codeset
, strlen (codeset
));
960 mask
|= XPG_NORM_CODESET
;
962 asprintf (&normalized_codeset_name
, "%s%s%s.%s%s%s",
963 language
, territory
== NULL
? "" : "_", territory
?: "",
965 modifier
== NULL
? "" : "@", modifier
?: "");
967 add_alias (ah
, normalized_codeset_name
, replace
,
968 normalized_name
?: name
, &locrec_offset
);
969 free (normalized_codeset_name
);
972 /* Now read the locale.alias files looking for lines whose
973 right hand side matches our name after normalization. */
974 if (alias_file
!= NULL
)
977 fp
= fopen (alias_file
, "rm");
979 error (1, errno
, _("locale alias file `%s' not found"),
982 /* No threads present. */
983 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
985 while (! feof_unlocked (fp
))
987 /* It is a reasonable approach to use a fix buffer here
989 a) we are only interested in the first two fields
990 b) these fields must be usable as file names and so must
997 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1002 /* Ignore leading white space. */
1003 while (isspace (cp
[0]) && cp
[0] != '\n')
1006 /* A leading '#' signals a comment line. */
1007 if (cp
[0] != '\0' && cp
[0] != '#' && cp
[0] != '\n')
1010 while (cp
[0] != '\0' && !isspace (cp
[0]))
1012 /* Terminate alias name. */
1016 /* Now look for the beginning of the value. */
1017 while (isspace (cp
[0]))
1023 while (cp
[0] != '\0' && !isspace (cp
[0]))
1025 /* Terminate value. */
1028 /* This has to be done to make the following
1029 test for the end of line possible. We are
1030 looking for the terminating '\n' which do not
1035 else if (cp
[0] != '\0')
1038 /* Does this alias refer to our locale? We will
1039 normalize the right hand side and compare the
1040 elements of the normalized form. */
1042 const char *rhs_language
;
1043 const char *rhs_modifier
;
1044 const char *rhs_territory
;
1045 const char *rhs_codeset
;
1046 const char *rhs_normalized_codeset
;
1047 int rhs_mask
= _nl_explode_name (value
,
1052 &rhs_normalized_codeset
);
1053 if (!strcmp (language
, rhs_language
)
1054 && ((rhs_mask
& XPG_CODESET
)
1055 /* He has a codeset, it must match normalized. */
1056 ? !strcmp ((mask
& XPG_NORM_CODESET
)
1057 ? normalized_codeset
: codeset
,
1058 (rhs_mask
& XPG_NORM_CODESET
)
1059 ? rhs_normalized_codeset
: rhs_codeset
)
1060 /* He has no codeset, we must also have none. */
1061 : (mask
& XPG_CODESET
) == 0)
1062 /* Codeset (or lack thereof) matches. */
1063 && !strcmp (territory
?: "", rhs_territory
?: "")
1064 && !strcmp (modifier
?: "", rhs_modifier
?: ""))
1065 /* We have a winner. */
1066 add_alias (ah
, alias
, replace
,
1067 normalized_name
?: name
, &locrec_offset
);
1068 if (rhs_mask
& XPG_NORM_CODESET
)
1069 free ((char *) rhs_normalized_codeset
);
1074 /* Possibly not the whole line fits into the buffer.
1075 Ignore the rest of the line. */
1076 while (strchr (cp
, '\n') == NULL
)
1079 if (fgets_unlocked (buf
, BUFSIZ
, fp
) == NULL
)
1080 /* Make sure the inner loop will be left. The outer
1081 loop will exit at the `feof' test. */
1089 free (normalized_name
);
1091 if (mask
& XPG_NORM_CODESET
)
1092 free ((char *) normalized_codeset
);
1099 add_locales_to_archive (nlist
, list
, replace
)
1104 struct locarhandle ah
;
1107 /* Open the archive. This call never returns if we cannot
1108 successfully open the archive. */
1109 open_archive (&ah
, false);
1113 const char *fname
= *list
++;
1114 size_t fnamelen
= strlen (fname
);
1123 printf (_("Adding %s\n"), fname
);
1125 /* First see whether this really is a directory and whether it
1126 contains all the require locale category files. */
1127 if (stat64 (fname
, &st
) < 0)
1129 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname
,
1133 if (!S_ISDIR (st
.st_mode
))
1135 error (0, 0, _("\"%s\" is no directory; ignored"), fname
);
1139 dirp
= opendir (fname
);
1142 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1143 fname
, strerror (errno
));
1148 while ((d
= readdir64 (dirp
)) != NULL
)
1150 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1152 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
1154 unsigned char d_type
;
1156 /* We have an object of the required name. If it's
1157 a directory we have to look at a file with the
1158 prefix "SYS_". Otherwise we have found what we
1160 #ifdef _DIRENT_HAVE_D_TYPE
1163 if (d_type
!= DT_REG
)
1166 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
1168 #ifdef _DIRENT_HAVE_D_TYPE
1169 if (d_type
== DT_UNKNOWN
)
1172 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
1175 if (stat64 (fullname
, &st
) == -1)
1176 /* We cannot stat the file, ignore it. */
1179 d_type
= IFTODT (st
.st_mode
);
1182 if (d_type
== DT_DIR
)
1184 /* We have to do more tests. The file is a
1185 directory and it therefore must contain a
1186 regular file with the same name except a
1188 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
1189 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
1192 if (stat64 (fullname
, &st
) == -1)
1193 /* There is no SYS_* file or we cannot
1197 d_type
= IFTODT (st
.st_mode
);
1201 /* If we found a regular file (eventually after
1202 following a symlink) we are successful. */
1203 if (d_type
== DT_REG
)
1211 if (seen
!= __LC_LAST
- 1)
1213 /* We don't have all locale category files. Ignore the name. */
1214 error (0, 0, _("incomplete set of locale files in \"%s\""),
1219 /* Add the files to the archive. To do this we first compute
1220 sizes and the MD5 sums of all the files. */
1221 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1224 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
1227 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
1228 fd
= open64 (fullname
, O_RDONLY
);
1229 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
1231 /* Cannot read the file. */
1237 if (S_ISDIR (st
.st_mode
))
1241 t
= stpcpy (stpcpy (fullname
, fname
), "/");
1242 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
1245 fd
= open64 (fullname
, O_RDONLY
);
1246 if (fd
== -1 || fstat64 (fd
, &st
) == -1
1247 || !S_ISREG (st
.st_mode
))
1256 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
1258 if (data
[cnt
].addr
== MAP_FAILED
)
1260 /* Cannot map it. */
1265 data
[cnt
].size
= st
.st_size
;
1266 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
1268 /* We don't need the file descriptor anymore. */
1272 if (cnt
!= __LC_LAST
)
1276 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1278 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname
);
1283 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, replace
);
1285 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
1287 munmap (data
[cnt
].addr
, data
[cnt
].size
);
1291 close_archive (&ah
);
1298 delete_locales_from_archive (nlist
, list
)
1302 struct locarhandle ah
;
1303 struct locarhead
*head
;
1304 struct namehashent
*namehashtab
;
1306 /* Open the archive. This call never returns if we cannot
1307 successfully open the archive. */
1308 open_archive (&ah
, false);
1311 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1312 + head
->namehash_offset
);
1316 const char *locname
= *list
++;
1321 /* Search for this locale in the archive. */
1322 hval
= archive_hashval (locname
, strlen (locname
));
1324 idx
= hval
% head
->namehash_size
;
1325 incr
= 1 + hval
% (head
->namehash_size
- 2);
1327 /* If the name_offset field is zero this means this is no
1328 deleted entry and therefore no entry can be found. */
1329 while (namehashtab
[idx
].name_offset
!= 0)
1331 if (namehashtab
[idx
].hashval
== hval
1332 && (strcmp (locname
,
1333 (char *) ah
.addr
+ namehashtab
[idx
].name_offset
)
1336 /* Found the entry. Now mark it as removed by zero-ing
1337 the reference to the locale record. */
1338 namehashtab
[idx
].locrec_offset
= 0;
1343 if (idx
>= head
->namehash_size
)
1344 idx
-= head
->namehash_size
;
1347 if (namehashtab
[idx
].name_offset
== 0 && ! be_quiet
)
1348 error (0, 0, _("locale \"%s\" not in archive"), locname
);
1351 close_archive (&ah
);
1360 uint32_t locrec_offset
;
1366 const unsigned char *sum
;
1367 uint32_t file_offset
;
1373 nameentcmp (const void *a
, const void *b
)
1375 return strcmp (((const struct nameent
*) a
)->name
,
1376 ((const struct nameent
*) b
)->name
);
1381 dataentcmp (const void *a
, const void *b
)
1383 if (((const struct dataent
*) a
)->file_offset
1384 < ((const struct dataent
*) b
)->file_offset
)
1387 if (((const struct dataent
*) a
)->file_offset
1388 > ((const struct dataent
*) b
)->file_offset
)
1396 show_archive_content (int verbose
)
1398 struct locarhandle ah
;
1399 struct locarhead
*head
;
1400 struct namehashent
*namehashtab
;
1401 struct nameent
*names
;
1404 /* Open the archive. This call never returns if we cannot
1405 successfully open the archive. */
1406 open_archive (&ah
, true);
1410 names
= (struct nameent
*) xmalloc (head
->namehash_used
1411 * sizeof (struct nameent
));
1413 namehashtab
= (struct namehashent
*) ((char *) ah
.addr
1414 + head
->namehash_offset
);
1415 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
1416 if (namehashtab
[cnt
].locrec_offset
!= 0)
1418 assert (used
< head
->namehash_used
);
1419 names
[used
].name
= ah
.addr
+ namehashtab
[cnt
].name_offset
;
1420 names
[used
++].locrec_offset
= namehashtab
[cnt
].locrec_offset
;
1423 /* Sort the names. */
1424 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
1428 struct dataent
*files
;
1429 struct sumhashent
*sumhashtab
;
1432 files
= (struct dataent
*) xmalloc (head
->sumhash_used
1433 * sizeof (struct sumhashent
));
1435 sumhashtab
= (struct sumhashent
*) ((char *) ah
.addr
1436 + head
->sumhash_offset
);
1437 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
1438 if (sumhashtab
[cnt
].file_offset
!= 0)
1440 assert (sumused
< head
->sumhash_used
);
1441 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
1442 files
[sumused
].file_offset
= sumhashtab
[cnt
].file_offset
;
1443 files
[sumused
++].nlink
= 0;
1446 /* Sort by file locations. */
1447 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
1449 /* Compute nlink fields. */
1450 for (cnt
= 0; cnt
< used
; ++cnt
)
1452 struct locrecent
*locrec
;
1455 locrec
= (struct locrecent
*) ((char *) ah
.addr
1456 + names
[cnt
].locrec_offset
);
1457 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1458 if (locrec
->record
[LC_ALL
].offset
!= 0
1460 || (locrec
->record
[idx
].offset
1461 < locrec
->record
[LC_ALL
].offset
)
1462 || (locrec
->record
[idx
].offset
+ locrec
->record
[idx
].len
1463 > (locrec
->record
[LC_ALL
].offset
1464 + locrec
->record
[LC_ALL
].len
)))
1467 struct dataent
*data
, dataent
;
1469 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1470 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1471 sizeof (struct dataent
),
1473 assert (data
!= NULL
);
1479 for (cnt
= 0; cnt
< used
; ++cnt
)
1481 struct locrecent
*locrec
;
1484 locrec
= (struct locrecent
*) ((char *) ah
.addr
1485 + names
[cnt
].locrec_offset
);
1486 for (idx
= 0; idx
< __LC_LAST
; ++idx
)
1489 struct dataent
*data
, dataent
;
1491 dataent
.file_offset
= locrec
->record
[idx
].offset
;
1492 if (locrec
->record
[LC_ALL
].offset
!= 0
1493 && dataent
.file_offset
>= locrec
->record
[LC_ALL
].offset
1494 && (dataent
.file_offset
+ locrec
->record
[idx
].len
1495 <= (locrec
->record
[LC_ALL
].offset
1496 + locrec
->record
[LC_ALL
].len
)))
1497 dataent
.file_offset
= locrec
->record
[LC_ALL
].offset
;
1499 data
= (struct dataent
*) bsearch (&dataent
, files
, sumused
,
1500 sizeof (struct dataent
),
1502 printf ("%6d %7x %3d%c ",
1503 locrec
->record
[idx
].len
, locrec
->record
[idx
].offset
,
1505 dataent
.file_offset
== locrec
->record
[LC_ALL
].offset
1507 for (i
= 0; i
< 16; i
+= 4)
1508 printf ("%02x%02x%02x%02x",
1509 data
->sum
[i
], data
->sum
[i
+ 1],
1510 data
->sum
[i
+ 2], data
->sum
[i
+ 3]);
1511 printf (" %s/%s\n", names
[cnt
].name
,
1512 idx
== LC_MESSAGES
? "LC_MESSAGES/SYS_LC_MESSAGES"
1518 for (cnt
= 0; cnt
< used
; ++cnt
)
1519 puts (names
[cnt
].name
);
1521 close_archive (&ah
);
1523 exit (EXIT_SUCCESS
);