15 #include "../locale/hashval.h"
17 #include "../locale/locarchive.h"
18 #include "../crypt/md5.h"
20 const char *alias_file
= DATADIR
"/locale/locale.alias";
21 const char *locar_file
= PREFIX
"/lib/locale/locale-archive";
22 const char *tmpl_file
= PREFIX
"/lib/locale/locale-archive.tmpl";
23 const char *loc_path
= PREFIX
"/lib/locale/";
26 int max_locarchive_open_retry
= 10;
27 const char *output_prefix
;
29 static const char *locnames
[] =
31 #define DEFINE_CATEGORY(category, category_name, items, a) \
32 [category] = category_name,
33 #include "../locale/categories.def"
34 #undef DEFINE_CATEGORY
38 is_prime (unsigned long candidate
)
40 /* No even number and none less than 10 will be passed here. */
41 unsigned long int divn
= 3;
42 unsigned long int sq
= divn
* divn
;
44 while (sq
< candidate
&& candidate
% divn
!= 0)
51 return candidate
% divn
!= 0;
55 next_prime (unsigned long seed
)
57 /* Make it definitely odd. */
60 while (!is_prime (seed
))
66 /* xmalloc is only used by show_archive_content. */
75 error (int status
, int errnum
, const char *message
, ...)
79 va_start (args
, message
);
81 fprintf (stderr
, "%s: ", program_invocation_name
);
82 vfprintf (stderr
, message
, args
);
85 fprintf (stderr
, ": %s", strerror (errnum
));
89 exit (errnum
== EROFS
? 0 : status
);
93 open_tmpl_archive (struct locarhandle
*ah
)
97 struct locarhead head
;
98 const char *archivefname
= tmpl_file
;
100 /* Open the archive. We must have exclusive write access. */
101 fd
= open64 (archivefname
, O_RDONLY
);
103 error (EXIT_FAILURE
, errno
, "cannot open locale archive template file \"%s\"",
106 if (fstat64 (fd
, &st
) < 0)
107 error (EXIT_FAILURE
, errno
, "cannot stat locale archive template file \"%s\"",
110 /* Read the header. */
111 if (TEMP_FAILURE_RETRY (read (fd
, &head
, sizeof (head
))) != sizeof (head
))
112 error (EXIT_FAILURE
, errno
, "cannot read archive header");
115 ah
->len
= (head
.sumhash_offset
116 + head
.sumhash_size
* sizeof (struct sumhashent
));
117 if (ah
->len
> st
.st_size
)
118 error (EXIT_FAILURE
, 0, "locale archite template file truncated");
119 ah
->len
= st
.st_size
;
121 /* Now we know how large the administrative information part is.
123 ah
->addr
= mmap64 (NULL
, ah
->len
, PROT_READ
, MAP_SHARED
, fd
, 0);
124 if (ah
->addr
== MAP_FAILED
)
125 error (EXIT_FAILURE
, errno
, "cannot map archive header");
128 /* Open the locale archive. */
129 extern void open_archive (struct locarhandle
*ah
, bool readonly
);
131 /* Close the locale archive. */
132 extern void close_archive (struct locarhandle
*ah
);
134 /* Add given locale data to the archive. */
135 extern int add_locale_to_archive (struct locarhandle
*ah
, const char *name
,
136 locale_data_t data
, bool replace
);
138 extern void add_alias (struct locarhandle
*ah
, const char *alias
,
139 bool replace
, const char *oldname
,
140 uint32_t *locrec_offset_p
);
142 extern struct namehashent
*
143 insert_name (struct locarhandle
*ah
,
144 const char *name
, size_t name_len
, bool replace
);
149 struct locrecent
*locrec
;
154 const unsigned char *sum
;
155 uint32_t file_offset
;
159 nameentcmp (const void *a
, const void *b
)
161 struct locrecent
*la
= ((const struct nameent
*) a
)->locrec
;
162 struct locrecent
*lb
= ((const struct nameent
*) b
)->locrec
;
163 uint32_t start_a
= -1, end_a
= 0;
164 uint32_t start_b
= -1, end_b
= 0;
167 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
170 if (la
->record
[cnt
].offset
< start_a
)
171 start_a
= la
->record
[cnt
].offset
;
172 if (la
->record
[cnt
].offset
+ la
->record
[cnt
].len
> end_a
)
173 end_a
= la
->record
[cnt
].offset
+ la
->record
[cnt
].len
;
175 assert (start_a
!= (uint32_t)-1);
178 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
181 if (lb
->record
[cnt
].offset
< start_b
)
182 start_b
= lb
->record
[cnt
].offset
;
183 if (lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
> end_b
)
184 end_b
= lb
->record
[cnt
].offset
+ lb
->record
[cnt
].len
;
186 assert (start_b
!= (uint32_t)-1);
189 if (start_a
!= start_b
)
190 return (int)start_a
- (int)start_b
;
191 return (int)end_a
- (int)end_b
;
195 dataentcmp (const void *a
, const void *b
)
197 if (((const struct dataent
*) a
)->file_offset
198 < ((const struct dataent
*) b
)->file_offset
)
201 if (((const struct dataent
*) a
)->file_offset
202 > ((const struct dataent
*) b
)->file_offset
)
209 sumsearchfn (const void *key
, const void *ent
)
211 uint32_t keyn
= *(uint32_t *)key
;
212 uint32_t entn
= ((struct dataent
*)ent
)->file_offset
;
222 compute_data (struct locarhandle
*ah
, struct nameent
*name
, size_t sumused
,
223 struct dataent
*files
, locale_data_t data
)
226 struct locrecent
*locrec
= name
->locrec
;
227 struct dataent
*file
;
228 data
[LC_ALL
].addr
= ((char *) ah
->addr
) + locrec
->record
[LC_ALL
].offset
;
229 data
[LC_ALL
].size
= locrec
->record
[LC_ALL
].len
;
230 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
233 data
[cnt
].addr
= ((char *) ah
->addr
) + locrec
->record
[cnt
].offset
;
234 data
[cnt
].size
= locrec
->record
[cnt
].len
;
235 if (data
[cnt
].addr
>= data
[LC_ALL
].addr
236 && data
[cnt
].addr
+ data
[cnt
].size
237 <= data
[LC_ALL
].addr
+ data
[LC_ALL
].size
)
238 __md5_buffer (data
[cnt
].addr
, data
[cnt
].size
, data
[cnt
].sum
);
241 file
= bsearch (&locrec
->record
[cnt
].offset
, files
, sumused
,
242 sizeof (*files
), sumsearchfn
);
244 error (EXIT_FAILURE
, 0, "inconsistent template file");
245 memcpy (data
[cnt
].sum
, file
->sum
, sizeof (data
[cnt
].sum
));
251 fill_archive (struct locarhandle
*tmpl_ah
, size_t nlist
, char *list
[],
254 struct locarhandle ah
;
255 struct locarhead
*head
;
257 struct nameent
*names
;
258 struct namehashent
*namehashtab
;
260 struct dataent
*files
;
261 struct sumhashent
*sumhashtab
;
263 struct locrecent
*primary_locrec
= NULL
;
264 struct nameent
*primary_nameent
= NULL
;
266 head
= tmpl_ah
->addr
;
267 names
= (struct nameent
*) malloc (head
->namehash_used
268 * sizeof (struct nameent
));
269 files
= (struct dataent
*) malloc (head
->sumhash_used
270 * sizeof (struct dataent
));
271 if (names
== NULL
|| files
== NULL
)
272 error (EXIT_FAILURE
, errno
, "could not allocate tables");
274 namehashtab
= (struct namehashent
*) ((char *) tmpl_ah
->addr
275 + head
->namehash_offset
);
276 sumhashtab
= (struct sumhashent
*) ((char *) tmpl_ah
->addr
277 + head
->sumhash_offset
);
279 for (cnt
= used
= 0; cnt
< head
->namehash_size
; ++cnt
)
280 if (namehashtab
[cnt
].locrec_offset
!= 0)
282 assert (used
< head
->namehash_used
);
283 names
[used
].name
= tmpl_ah
->addr
+ namehashtab
[cnt
].name_offset
;
285 = (struct locrecent
*) ((char *) tmpl_ah
->addr
+
286 namehashtab
[cnt
].locrec_offset
);
289 /* Sort the names. */
290 qsort (names
, used
, sizeof (struct nameent
), nameentcmp
);
292 for (cnt
= sumused
= 0; cnt
< head
->sumhash_size
; ++cnt
)
293 if (sumhashtab
[cnt
].file_offset
!= 0)
295 assert (sumused
< head
->sumhash_used
);
296 files
[sumused
].sum
= (const unsigned char *) sumhashtab
[cnt
].sum
;
297 files
[sumused
++].file_offset
= sumhashtab
[cnt
].file_offset
;
300 /* Sort by file locations. */
301 qsort (files
, sumused
, sizeof (struct dataent
), dataentcmp
);
303 /* Open the archive. This call never returns if we cannot
304 successfully open the archive. */
305 open_archive (&ah
, false);
309 for (cnt
= 0; cnt
< used
; ++cnt
)
310 if (strcmp (names
[cnt
].name
, primary
) == 0)
316 compute_data (tmpl_ah
, &names
[cnt
], sumused
, files
, data
);
317 result
|= add_locale_to_archive (&ah
, primary
, data
, 0);
318 primary_locrec
= names
[cnt
].locrec
;
319 primary_nameent
= &names
[cnt
];
323 for (cnt
= 0; cnt
< used
; ++cnt
)
324 if (&names
[cnt
] == primary_nameent
)
326 else if ((cnt
> 0 && names
[cnt
- 1].locrec
== names
[cnt
].locrec
)
327 || names
[cnt
].locrec
== primary_locrec
)
330 struct namehashent
*namehashent
;
331 uint32_t locrec_offset
;
333 if (names
[cnt
].locrec
== primary_locrec
)
336 oldname
= names
[cnt
- 1].name
;
337 namehashent
= insert_name (&ah
, oldname
, strlen (oldname
), true);
338 assert (namehashent
->name_offset
!= 0);
339 assert (namehashent
->locrec_offset
!= 0);
340 locrec_offset
= namehashent
->locrec_offset
;
341 add_alias (&ah
, names
[cnt
].name
, 0, oldname
, &locrec_offset
);
347 compute_data (tmpl_ah
, &names
[cnt
], sumused
, files
, data
);
348 result
|= add_locale_to_archive (&ah
, names
[cnt
].name
, data
, 0);
353 const char *fname
= *list
++;
354 size_t fnamelen
= strlen (fname
);
362 /* First see whether this really is a directory and whether it
363 contains all the require locale category files. */
364 if (stat64 (fname
, &st
) < 0)
366 error (0, 0, "stat of \"%s\" failed: %s: ignored", fname
,
370 if (!S_ISDIR (st
.st_mode
))
372 error (0, 0, "\"%s\" is no directory; ignored", fname
);
376 dirp
= opendir (fname
);
379 error (0, 0, "cannot open directory \"%s\": %s: ignored",
380 fname
, strerror (errno
));
385 while ((d
= readdir64 (dirp
)) != NULL
)
387 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
389 if (strcmp (d
->d_name
, locnames
[cnt
]) == 0)
391 unsigned char d_type
;
393 /* We have an object of the required name. If it's
394 a directory we have to look at a file with the
395 prefix "SYS_". Otherwise we have found what we
397 #ifdef _DIRENT_HAVE_D_TYPE
400 if (d_type
!= DT_REG
)
403 char fullname
[fnamelen
+ 2 * strlen (d
->d_name
) + 7];
405 #ifdef _DIRENT_HAVE_D_TYPE
406 if (d_type
== DT_UNKNOWN
)
409 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"),
412 if (stat64 (fullname
, &st
) == -1)
413 /* We cannot stat the file, ignore it. */
416 d_type
= IFTODT (st
.st_mode
);
419 if (d_type
== DT_DIR
)
421 /* We have to do more tests. The file is a
422 directory and it therefore must contain a
423 regular file with the same name except a
425 char *t
= stpcpy (stpcpy (fullname
, fname
), "/");
426 strcpy (stpcpy (stpcpy (t
, d
->d_name
), "/SYS_"),
429 if (stat64 (fullname
, &st
) == -1)
430 /* There is no SYS_* file or we cannot
434 d_type
= IFTODT (st
.st_mode
);
438 /* If we found a regular file (eventually after
439 following a symlink) we are successful. */
440 if (d_type
== DT_REG
)
448 if (seen
!= __LC_LAST
- 1)
450 /* We don't have all locale category files. Ignore the name. */
451 error (0, 0, "incomplete set of locale files in \"%s\"",
456 /* Add the files to the archive. To do this we first compute
457 sizes and the MD5 sums of all the files. */
458 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
461 char fullname
[fnamelen
+ 2 * strlen (locnames
[cnt
]) + 7];
464 strcpy (stpcpy (stpcpy (fullname
, fname
), "/"), locnames
[cnt
]);
465 fd
= open64 (fullname
, O_RDONLY
);
466 if (fd
== -1 || fstat64 (fd
, &st
) == -1)
468 /* Cannot read the file. */
474 if (S_ISDIR (st
.st_mode
))
478 t
= stpcpy (stpcpy (fullname
, fname
), "/");
479 strcpy (stpcpy (stpcpy (t
, locnames
[cnt
]), "/SYS_"),
482 fd
= open64 (fullname
, O_RDONLY
);
483 if (fd
== -1 || fstat64 (fd
, &st
) == -1
484 || !S_ISREG (st
.st_mode
))
493 data
[cnt
].addr
= mmap64 (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
,
495 if (data
[cnt
].addr
== MAP_FAILED
)
502 data
[cnt
].size
= st
.st_size
;
503 __md5_buffer (data
[cnt
].addr
, st
.st_size
, data
[cnt
].sum
);
505 /* We don't need the file descriptor anymore. */
509 if (cnt
!= __LC_LAST
)
513 munmap (data
[cnt
].addr
, data
[cnt
].size
);
515 error (0, 0, "cannot read all files in \"%s\": ignored", fname
);
520 result
|= add_locale_to_archive (&ah
, basename (fname
), data
, 0);
522 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
524 munmap (data
[cnt
].addr
, data
[cnt
].size
);
539 char *list
[16384], *primary
;
540 unsigned int cnt
= 0;
541 struct locarhandle tmpl_ah
;
542 size_t loc_path_len
= strlen (loc_path
);
544 dirp
= opendir (loc_path
);
546 error (EXIT_FAILURE
, errno
, "cannot open directory \"%s\"", loc_path
);
548 open_tmpl_archive (&tmpl_ah
);
551 primary
= getenv ("LC_ALL");
553 primary
= getenv ("LANG");
556 if (strncmp (primary
, "ja", 2) != 0
557 && strncmp (primary
, "ko", 2) != 0
558 && strncmp (primary
, "zh", 2) != 0)
560 char *ptr
= malloc (strlen (primary
) + strlen (".utf8") + 1), *p
, *q
;
566 while (*q
&& *q
!= '.' && *q
!= '@')
569 while (*q
&& *q
!= '@')
571 p
= stpcpy (p
, ".utf8");
580 memcpy (path
, loc_path
, loc_path_len
);
582 while ((d
= readdir64 (dirp
)) != NULL
)
584 if (strcmp (d
->d_name
, ".") == 0 || strcmp (d
->d_name
, "..") == 0)
587 size_t d_name_len
= strlen (d
->d_name
);
588 if (loc_path_len
+ d_name_len
+ 1 > sizeof (path
))
590 error (0, 0, "too long filename \"%s\"", d
->d_name
);
594 memcpy (path
+ loc_path_len
, d
->d_name
, d_name_len
+ 1);
595 if (stat64 (path
, &st
) < 0)
597 error (0, errno
, "cannot stat \"%s\"", path
);
600 if (! S_ISDIR (st
.st_mode
))
603 error (EXIT_FAILURE
, 0, "too many directories in \"%s\"", loc_path
);
604 list
[cnt
] = strdup (path
);
605 if (list
[cnt
] == NULL
)
607 error (0, errno
, "cannot add file to list \"%s\"", path
);
610 if (primary
!= NULL
&& cnt
> 0 && strcmp (primary
, d
->d_name
) == 0)
619 fill_archive (&tmpl_ah
, cnt
, list
, primary
);
620 close_archive (&tmpl_ah
);
621 truncate (tmpl_file
, 0);
622 char *argv
[] = { "/usr/sbin/tzdata-update", NULL
};
623 execve (argv
[0], (char *const *)argv
, (char *const *)&argv
[1]);