]> sourceware.org Git - glibc.git/blob - locale/programs/locarchive.c
* locale/programs/localedef.c (add_to_readlist): Error about
[glibc.git] / locale / programs / locarchive.c
1 /* Copyright (C) 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
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.
9
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.
14
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
18 02111-1307 USA. */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <assert.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <fcntl.h>
29 #include <inttypes.h>
30 #include <libintl.h>
31 #include <locale.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdio_ext.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42
43 #include "../../crypt/md5.h"
44 #include "../localeinfo.h"
45 #include "../locarchive.h"
46 #include "simple-hash.h"
47 #include "localedef.h"
48
49 extern const char *output_prefix;
50
51 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
52
53 static const char *locnames[] =
54 {
55 #define DEFINE_CATEGORY(category, category_name, items, a) \
56 [category] = category_name,
57 #include "categories.def"
58 #undef DEFINE_CATEGORY
59 };
60
61
62 /* Size of the initial archive header. */
63 #define INITIAL_NUM_NAMES 450
64 #define INITIAL_SIZE_STRINGS 3500
65 #define INITIAL_NUM_LOCREC 350
66 #define INITIAL_NUM_SUMS 2000
67
68
69 static void
70 create_archive (const char *archivefname, struct locarhandle *ah)
71 {
72 int fd;
73 char fname[strlen (archivefname) + sizeof (".XXXXXX")];
74 struct locarhead head;
75 void *p;
76 size_t total;
77
78 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
79
80 /* Create a temporary file in the correct directory. */
81 fd = mkstemp (fname);
82 if (fd == -1)
83 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
84
85 /* Create the initial content of the archive. */
86 head.magic = AR_MAGIC;
87 head.namehash_offset = sizeof (struct locarhead);
88 head.namehash_used = 0;
89 head.namehash_size = next_prime (INITIAL_NUM_NAMES);
90
91 head.string_offset = (head.namehash_offset
92 + head.namehash_size * sizeof (struct namehashent));
93 head.string_used = 0;
94 head.string_size = INITIAL_SIZE_STRINGS;
95
96 head.locrectab_offset = head.string_offset + head.string_size;
97 head.locrectab_used = 0;
98 head.locrectab_size = INITIAL_NUM_LOCREC;
99
100 head.sumhash_offset = (head.locrectab_offset
101 + head.locrectab_size * sizeof (struct locrecent));
102 head.sumhash_used = 0;
103 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
104
105 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
106
107 /* Write out the header and create room for the other data structures. */
108 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
109 {
110 int errval = errno;
111 unlink (fname);
112 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
113 }
114
115 if (ftruncate64 (fd, total) != 0)
116 {
117 int errval = errno;
118 unlink (fname);
119 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
120 }
121
122 /* Map the header and all the administration data structures. */
123 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
124 if (p == MAP_FAILED)
125 {
126 int errval = errno;
127 unlink (fname);
128 error (EXIT_FAILURE, errval, _("cannot map archive header"));
129 }
130
131 /* Now try to rename it. We don't use the rename function since
132 this would overwrite a file which has been created in
133 parallel. */
134 if (link (fname, archivefname) == -1)
135 {
136 int errval = errno;
137
138 /* We cannot use the just created file. */
139 close (fd);
140 unlink (fname);
141
142 if (errval == EEXIST)
143 {
144 /* There is already an archive. Must have been a localedef run
145 which happened in parallel. Simply open this file then. */
146 open_archive (ah, false);
147 return;
148 }
149
150 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
151 }
152
153 /* Remove the temporary name. */
154 unlink (fname);
155
156 /* Make the file globally readable. */
157 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
158 {
159 int errval = errno;
160 unlink (archivefname);
161 error (EXIT_FAILURE, errval,
162 _("cannot change mode of new locale archive"));
163 }
164
165 ah->fd = fd;
166 ah->addr = p;
167 ah->len = total;
168 }
169
170
171 /* This structure and qsort comparator function are used below to sort an
172 old archive's locrec table in order of data position in the file. */
173 struct oldlocrecent
174 {
175 unsigned int cnt;
176 struct locrecent *locrec;
177 };
178
179 static int
180 oldlocrecentcmp (const void *a, const void *b)
181 {
182 struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
183 struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
184 uint32_t start_a = -1, end_a = 0;
185 uint32_t start_b = -1, end_b = 0;
186 int cnt;
187
188 for (cnt = 0; cnt < __LC_LAST; ++cnt)
189 if (cnt != LC_ALL)
190 {
191 if (la->record[cnt].offset < start_a)
192 start_a = la->record[cnt].offset;
193 if (la->record[cnt].offset + la->record[cnt].len > end_a)
194 end_a = la->record[cnt].offset + la->record[cnt].len;
195 }
196 assert (start_a != (uint32_t)-1);
197 assert (end_a != 0);
198
199 for (cnt = 0; cnt < __LC_LAST; ++cnt)
200 if (cnt != LC_ALL)
201 {
202 if (lb->record[cnt].offset < start_b)
203 start_b = lb->record[cnt].offset;
204 if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
205 end_b = lb->record[cnt].offset + lb->record[cnt].len;
206 }
207 assert (start_b != (uint32_t)-1);
208 assert (end_b != 0);
209
210 if (start_a != start_b)
211 return (int)start_a - (int)start_b;
212 return (int)end_a - (int)end_b;
213 }
214
215
216 /* forward decl for below */
217 static uint32_t add_locale (struct locarhandle *ah, const char *name,
218 locale_data_t data, bool replace);
219
220 static void
221 enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
222 {
223 struct stat64 st;
224 int fd;
225 struct locarhead newhead;
226 size_t total;
227 void *p;
228 unsigned int cnt, loccnt;
229 struct namehashent *oldnamehashtab;
230 struct locrecent *oldlocrectab;
231 struct locarhandle new_ah;
232 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
233 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
234 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
235 struct oldlocrecent *oldlocrecarray;
236
237 if (output_prefix)
238 memcpy (archivefname, output_prefix, prefix_len);
239 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
240 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
241
242 /* Not all of the old file has to be mapped. Change this now this
243 we will have to access the whole content. */
244 if (fstat64 (ah->fd, &st) != 0
245 || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
246 MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
247 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
248 ah->len = st.st_size;
249
250 /* Create a temporary file in the correct directory. */
251 fd = mkstemp (fname);
252 if (fd == -1)
253 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
254
255 /* Copy the existing head information. */
256 newhead = *head;
257
258 /* Create the new archive header. The sizes of the various tables
259 should be double from what is currently used. */
260 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
261 newhead.namehash_size);
262 if (! be_quiet)
263 printf ("name: size: %u, used: %d, new: size: %u\n",
264 head->namehash_size, head->namehash_used, newhead.namehash_size);
265
266 newhead.string_offset = (newhead.namehash_offset
267 + (newhead.namehash_size
268 * sizeof (struct namehashent)));
269 newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
270
271 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
272 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
273 newhead.locrectab_size);
274
275 newhead.sumhash_offset = (newhead.locrectab_offset
276 + (newhead.locrectab_size
277 * sizeof (struct locrecent)));
278 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
279 newhead.sumhash_size);
280
281 total = (newhead.sumhash_offset
282 + newhead.sumhash_size * sizeof (struct sumhashent));
283
284 /* The new file is empty now. */
285 newhead.namehash_used = 0;
286 newhead.string_used = 0;
287 newhead.locrectab_used = 0;
288 newhead.sumhash_used = 0;
289
290 /* Write out the header and create room for the other data structures. */
291 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
292 != sizeof (newhead))
293 {
294 int errval = errno;
295 unlink (fname);
296 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
297 }
298
299 if (ftruncate64 (fd, total) != 0)
300 {
301 int errval = errno;
302 unlink (fname);
303 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
304 }
305
306 /* Map the header and all the administration data structures. */
307 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
308 if (p == MAP_FAILED)
309 {
310 int errval = errno;
311 unlink (fname);
312 error (EXIT_FAILURE, errval, _("cannot map archive header"));
313 }
314
315 /* Lock the new file. */
316 if (lockf64 (fd, F_LOCK, total) != 0)
317 {
318 int errval = errno;
319 unlink (fname);
320 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
321 }
322
323 new_ah.len = total;
324 new_ah.addr = p;
325 new_ah.fd = fd;
326
327 /* Walk through the hash name hash table to find out what data is
328 still referenced and transfer it into the new file. */
329 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
330 + head->namehash_offset);
331 oldlocrectab = (struct locrecent *) ((char *) ah->addr
332 + head->locrectab_offset);
333
334 /* Sort the old locrec table in order of data position. */
335 oldlocrecarray = (struct oldlocrecent *)
336 alloca (head->namehash_size
337 * sizeof (struct oldlocrecent));
338 for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
339 if (oldnamehashtab[cnt].locrec_offset != 0)
340 {
341 oldlocrecarray[loccnt].cnt = cnt;
342 oldlocrecarray[loccnt++].locrec
343 = (struct locrecent *) ((char *) ah->addr
344 + oldnamehashtab[cnt].locrec_offset);
345 }
346 qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
347 oldlocrecentcmp);
348
349 for (cnt = 0; cnt < loccnt; ++cnt)
350 {
351 /* Insert this entry in the new hash table. */
352 locale_data_t old_data;
353 unsigned int idx;
354 struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
355
356 for (idx = 0; idx < __LC_LAST; ++idx)
357 if (idx != LC_ALL)
358 {
359 old_data[idx].size = oldlocrec->record[idx].len;
360 old_data[idx].addr
361 = ((char *) ah->addr + oldlocrec->record[idx].offset);
362
363 __md5_buffer (old_data[idx].addr, old_data[idx].size,
364 old_data[idx].sum);
365 }
366
367 if (add_locale (&new_ah,
368 ((char *) ah->addr
369 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
370 old_data, 0) == 0)
371 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
372 }
373
374 /* Make the file globally readable. */
375 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
376 {
377 int errval = errno;
378 unlink (fname);
379 error (EXIT_FAILURE, errval,
380 _("cannot change mode of resized locale archive"));
381 }
382
383 /* Rename the new file. */
384 if (rename (fname, archivefname) != 0)
385 {
386 int errval = errno;
387 unlink (fname);
388 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
389 }
390
391 /* Close the old file. */
392 close_archive (ah);
393
394 /* Add the information for the new one. */
395 *ah = new_ah;
396 }
397
398
399 void
400 open_archive (struct locarhandle *ah, bool readonly)
401 {
402 struct stat64 st;
403 struct stat64 st2;
404 int fd;
405 struct locarhead head;
406 int retry = 0;
407 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
408 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
409
410 if (output_prefix)
411 memcpy (archivefname, output_prefix, prefix_len);
412 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
413
414 again:
415 /* Open the archive. We must have exclusive write access. */
416 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
417 if (fd == -1)
418 {
419 /* Maybe the file does not yet exist. */
420 if (errno == ENOENT)
421 {
422 if (readonly)
423 {
424 static const struct locarhead nullhead =
425 {
426 .namehash_used = 0,
427 .namehash_offset = 0,
428 .namehash_size = 0
429 };
430
431 ah->addr = (void *) &nullhead;
432 ah->fd = -1;
433 }
434 else
435 create_archive (archivefname, ah);
436
437 return;
438 }
439 else
440 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
441 archivefname);
442 }
443
444 if (fstat64 (fd, &st) < 0)
445 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
446 archivefname);
447
448 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
449 {
450 close (fd);
451
452 if (retry++ < max_locarchive_open_retry)
453 {
454 struct timespec req;
455
456 /* Wait for a bit. */
457 req.tv_sec = 0;
458 req.tv_nsec = 1000000 * (random () % 500 + 1);
459 (void) nanosleep (&req, NULL);
460
461 goto again;
462 }
463
464 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
465 archivefname);
466 }
467
468 /* One more check. Maybe another process replaced the archive file
469 with a new, larger one since we opened the file. */
470 if (stat64 (archivefname, &st2) == -1
471 || st.st_dev != st2.st_dev
472 || st.st_ino != st2.st_ino)
473 {
474 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
475 close (fd);
476 goto again;
477 }
478
479 /* Read the header. */
480 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
481 {
482 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
483 error (EXIT_FAILURE, errno, _("cannot read archive header"));
484 }
485
486 ah->fd = fd;
487 ah->len = (head.sumhash_offset
488 + head.sumhash_size * sizeof (struct sumhashent));
489
490 /* Now we know how large the administrative information part is.
491 Map all of it. */
492 ah->addr = mmap64 (NULL, ah->len, PROT_READ | (readonly ? 0 : PROT_WRITE),
493 MAP_SHARED, fd, 0);
494 if (ah->addr == MAP_FAILED)
495 {
496 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
497 error (EXIT_FAILURE, errno, _("cannot map archive header"));
498 }
499 }
500
501
502 void
503 close_archive (struct locarhandle *ah)
504 {
505 if (ah->fd != -1)
506 {
507 munmap (ah->addr, ah->len);
508 close (ah->fd);
509 }
510 }
511
512 #include "../../intl/explodename.c"
513 #include "../../intl/l10nflist.c"
514
515 static struct namehashent *
516 insert_name (struct locarhandle *ah,
517 const char *name, size_t name_len, bool replace)
518 {
519 const struct locarhead *const head = ah->addr;
520 struct namehashent *namehashtab
521 = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
522 unsigned int insert_idx, idx, incr;
523
524 /* Hash value of the locale name. */
525 uint32_t hval = compute_hashval (name, name_len);
526
527 insert_idx = -1;
528 idx = hval % head->namehash_size;
529 incr = 1 + hval % (head->namehash_size - 2);
530
531 /* If the name_offset field is zero this means this is a
532 deleted entry and therefore no entry can be found. */
533 while (namehashtab[idx].name_offset != 0)
534 {
535 if (namehashtab[idx].hashval == hval
536 && strcmp (name,
537 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
538 {
539 /* Found the entry. */
540 if (namehashtab[idx].locrec_offset != 0 && ! replace)
541 {
542 if (! be_quiet)
543 error (0, 0, _("locale '%s' already exists"), name);
544 return NULL;
545 }
546
547 break;
548 }
549
550 if (namehashtab[idx].hashval == hval)
551 {
552 error (0, 0, "hash collision (%u) %s, %s",
553 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
554 }
555
556
557 /* Remember the first place we can insert the new entry. */
558 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
559 insert_idx = idx;
560
561 idx += incr;
562 if (idx >= head->namehash_size)
563 idx -= head->namehash_size;
564 }
565
566 /* Add as early as possible. */
567 if (insert_idx != -1)
568 idx = insert_idx;
569
570 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
571 return &namehashtab[idx];
572 }
573
574 static void
575 add_alias (struct locarhandle *ah, const char *alias, bool replace,
576 const char *oldname, uint32_t locrec_offset)
577 {
578 struct locarhead *head = ah->addr;
579 const size_t name_len = strlen (alias);
580 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
581 replace);
582 if (namehashent == NULL && ! replace)
583 return;
584
585 if (namehashent->name_offset == 0)
586 {
587 /* We are adding a new hash entry for this alias.
588 Determine whether we have to resize the file. */
589 if (head->string_used + name_len + 1 > head->string_size
590 || 100 * head->namehash_used > 75 * head->namehash_size)
591 {
592 /* The current archive is not large enough. */
593 enlarge_archive (ah, head);
594
595 /* The locrecent might have moved, so we have to look up
596 the old name afresh. */
597 namehashent = insert_name (ah, oldname, strlen (oldname), true);
598 assert (namehashent->name_offset != 0);
599 assert (namehashent->locrec_offset != 0);
600 locrec_offset = namehashent->locrec_offset;
601
602 /* Tail call to try the whole thing again. */
603 add_alias (ah, alias, replace, oldname, locrec_offset);
604 return;
605 }
606
607 /* Add the name string. */
608 memcpy (ah->addr + head->string_offset + head->string_used,
609 alias, name_len + 1);
610 namehashent->name_offset = head->string_offset + head->string_used;
611 head->string_used += name_len + 1;
612
613 ++head->namehash_used;
614 }
615
616 if (namehashent->locrec_offset != 0)
617 {
618 /* Replacing an existing entry.
619 Mark that we are no longer using the old locrecent. */
620 struct locrecent *locrecent
621 = (struct locrecent *) ((char *) ah->addr
622 + namehashent->locrec_offset);
623 --locrecent->refs;
624 }
625
626 /* Point this entry at the locrecent installed for the main name. */
627 namehashent->locrec_offset = locrec_offset;
628 }
629
630 static int /* qsort comparator used below */
631 cmpcategorysize (const void *a, const void *b)
632 {
633 if (*(const void **) a == NULL)
634 return 1;
635 if (*(const void **) b == NULL)
636 return -1;
637 return ((*(const struct locale_category_data **) a)->size
638 - (*(const struct locale_category_data **) b)->size);
639 }
640
641 /* Check the content of the archive for duplicates. Add the content
642 of the files if necessary. Returns the locrec_offset. */
643 static uint32_t
644 add_locale (struct locarhandle *ah,
645 const char *name, locale_data_t data, bool replace)
646 {
647 /* First look for the name. If it already exists and we are not
648 supposed to replace it don't do anything. If it does not exist
649 we have to allocate a new locale record. */
650 size_t name_len = strlen (name);
651 uint32_t file_offsets[__LC_LAST];
652 unsigned int num_new_offsets = 0;
653 struct sumhashent *sumhashtab;
654 uint32_t hval;
655 unsigned int cnt, idx;
656 struct locarhead *head;
657 struct namehashent *namehashent;
658 unsigned int incr;
659 struct locrecent *locrecent;
660 off64_t lastoffset;
661 char *ptr;
662 struct locale_category_data *size_order[__LC_LAST];
663 const size_t pagesz = getpagesize ();
664 int small_mask;
665
666 head = ah->addr;
667 sumhashtab = (struct sumhashent *) ((char *) ah->addr
668 + head->sumhash_offset);
669
670 memset (file_offsets, 0, sizeof (file_offsets));
671
672 size_order[LC_ALL] = NULL;
673 for (cnt = 0; cnt < __LC_LAST; ++cnt)
674 if (cnt != LC_ALL)
675 size_order[cnt] = &data[cnt];
676
677 /* Sort the array in ascending order of data size. */
678 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
679
680 small_mask = 0;
681 data[LC_ALL].size = 0;
682 for (cnt = 0; cnt < __LC_LAST; ++cnt)
683 if (size_order[cnt] != NULL)
684 {
685 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
686 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
687 {
688 /* This category makes the small-categories block
689 stop being small, so this is the end of the road. */
690 do
691 size_order[cnt++] = NULL;
692 while (cnt < __LC_LAST);
693 break;
694 }
695 data[LC_ALL].size += rounded_size;
696 small_mask |= 1 << (size_order[cnt] - data);
697 }
698
699 /* Copy the data for all the small categories into the LC_ALL
700 pseudo-category. */
701
702 data[LC_ALL].addr = alloca (data[LC_ALL].size);
703 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
704
705 ptr = data[LC_ALL].addr;
706 for (cnt = 0; cnt < __LC_LAST; ++cnt)
707 if (small_mask & (1 << cnt))
708 {
709 memcpy (ptr, data[cnt].addr, data[cnt].size);
710 ptr += (data[cnt].size + 15) & -16;
711 }
712 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
713
714 /* For each locale category data set determine whether the same data
715 is already somewhere in the archive. */
716 for (cnt = 0; cnt < __LC_LAST; ++cnt)
717 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
718 {
719 ++num_new_offsets;
720
721 /* Compute the hash value of the checksum to determine a
722 starting point for the search in the MD5 hash value
723 table. */
724 hval = compute_hashval (data[cnt].sum, 16);
725
726 idx = hval % head->sumhash_size;
727 incr = 1 + hval % (head->sumhash_size - 2);
728
729 while (sumhashtab[idx].file_offset != 0)
730 {
731 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
732 {
733 /* Found it. */
734 file_offsets[cnt] = sumhashtab[idx].file_offset;
735 --num_new_offsets;
736 break;
737 }
738
739 idx += incr;
740 if (idx >= head->sumhash_size)
741 idx -= head->sumhash_size;
742 }
743 }
744
745 /* Find a slot for the locale name in the hash table. */
746 namehashent = insert_name (ah, name, name_len, replace);
747 if (namehashent == NULL) /* Already exists and !REPLACE. */
748 return 0;
749
750 /* Determine whether we have to resize the file. */
751 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
752 || (namehashent->locrec_offset == 0
753 && (head->locrectab_used == head->locrectab_size
754 || head->string_used + name_len + 1 > head->string_size
755 || 100 * head->namehash_used > 75 * head->namehash_size)))
756 {
757 /* The current archive is not large enough. */
758 enlarge_archive (ah, head);
759 return add_locale (ah, name, data, replace);
760 }
761
762 /* Add the locale data which is not yet in the archive. */
763 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
764 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
765 && file_offsets[cnt] == 0)
766 {
767 /* The data for this section is not yet available in the
768 archive. Append it. */
769 off64_t lastpos;
770 uint32_t md5hval;
771
772 lastpos = lseek64 (ah->fd, 0, SEEK_END);
773 if (lastpos == (off64_t) -1)
774 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
775
776 /* If block of small categories would cross page boundary,
777 align it unless it immediately follows a large category. */
778 if (cnt == LC_ALL && lastoffset != lastpos
779 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
780 & -pagesz)
781 > ((data[cnt].size + pagesz - 1) & -pagesz)))
782 {
783 size_t sz = pagesz - (lastpos & (pagesz - 1));
784 char *zeros = alloca (sz);
785
786 memset (zeros, 0, sz);
787 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
788 error (EXIT_FAILURE, errno,
789 _("cannot add to locale archive"));
790
791 lastpos += sz;
792 }
793
794 /* Align all data to a 16 byte boundary. */
795 if ((lastpos & 15) != 0)
796 {
797 static const char zeros[15] = { 0, };
798
799 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
800 != 16 - (lastpos & 15))
801 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
802
803 lastpos += 16 - (lastpos & 15);
804 }
805
806 /* Remember the position. */
807 file_offsets[cnt] = lastpos;
808 lastoffset = lastpos + data[cnt].size;
809
810 /* Write the data. */
811 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
812 != data[cnt].size)
813 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
814
815 /* Add the hash value to the hash table. */
816 md5hval = compute_hashval (data[cnt].sum, 16);
817
818 idx = md5hval % head->sumhash_size;
819 incr = 1 + md5hval % (head->sumhash_size - 2);
820
821 while (sumhashtab[idx].file_offset != 0)
822 {
823 idx += incr;
824 if (idx >= head->sumhash_size)
825 idx -= head->sumhash_size;
826 }
827
828 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
829 sumhashtab[idx].file_offset = file_offsets[cnt];
830
831 ++head->sumhash_used;
832 }
833
834 lastoffset = file_offsets[LC_ALL];
835 for (cnt = 0; cnt < __LC_LAST; ++cnt)
836 if (small_mask & (1 << cnt))
837 {
838 file_offsets[cnt] = lastoffset;
839 lastoffset += (data[cnt].size + 15) & -16;
840 }
841
842 if (namehashent->name_offset == 0)
843 {
844 /* Add the name string. */
845 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
846 name, name_len + 1);
847 namehashent->name_offset = head->string_offset + head->string_used;
848 head->string_used += name_len + 1;
849 ++head->namehash_used;
850 }
851
852 if (namehashent->locrec_offset == 0)
853 {
854 /* Allocate a name location record. */
855 namehashent->locrec_offset = (head->locrectab_offset
856 + (head->locrectab_used++
857 * sizeof (struct locrecent)));
858 locrecent = (struct locrecent *) ((char *) ah->addr
859 + namehashent->locrec_offset);
860 locrecent->refs = 1;
861 }
862 else
863 {
864 /* If there are other aliases pointing to this locrecent,
865 we still need a new one. If not, reuse the old one. */
866
867 locrecent = (struct locrecent *) ((char *) ah->addr
868 + namehashent->locrec_offset);
869 if (locrecent->refs > 1)
870 {
871 --locrecent->refs;
872 namehashent->locrec_offset = (head->locrectab_offset
873 + (head->locrectab_used++
874 * sizeof (struct locrecent)));
875 locrecent = (struct locrecent *) ((char *) ah->addr
876 + namehashent->locrec_offset);
877 locrecent->refs = 1;
878 }
879 }
880
881 /* Fill in the table with the locations of the locale data. */
882 for (cnt = 0; cnt < __LC_LAST; ++cnt)
883 {
884 locrecent->record[cnt].offset = file_offsets[cnt];
885 locrecent->record[cnt].len = data[cnt].size;
886 }
887
888 return namehashent->locrec_offset;
889 }
890
891
892 /* Check the content of the archive for duplicates. Add the content
893 of the files if necessary. Add all the names, possibly overwriting
894 old files. */
895 int
896 add_locale_to_archive (ah, name, data, replace)
897 struct locarhandle *ah;
898 const char *name;
899 locale_data_t data;
900 bool replace;
901 {
902 char *normalized_name = NULL;
903 uint32_t locrec_offset;
904
905 /* First analyze the name to decide how to archive it. */
906 const char *language;
907 const char *modifier;
908 const char *territory;
909 const char *codeset;
910 const char *normalized_codeset;
911 int mask = _nl_explode_name (strdupa (name),
912 &language, &modifier, &territory,
913 &codeset, &normalized_codeset);
914
915 if (mask & XPG_NORM_CODESET)
916 /* This name contains a codeset in unnormalized form.
917 We will store it in the archive with a normalized name. */
918 asprintf (&normalized_name, "%s%s%s.%s%s%s",
919 language, territory == NULL ? "" : "_", territory ?: "",
920 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
921 modifier == NULL ? "" : "@", modifier ?: "");
922
923 /* This call does the main work. */
924 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
925 free (normalized_name);
926 if (locrec_offset == 0)
927 {
928 if (mask & XPG_NORM_CODESET)
929 free ((char *) normalized_codeset);
930 return -1;
931 }
932
933 if ((mask & XPG_CODESET) == 0)
934 {
935 /* This name lacks a codeset, so determine the locale's codeset and
936 add an alias for its name with normalized codeset appended. */
937
938 const struct
939 {
940 unsigned int magic;
941 unsigned int nstrings;
942 unsigned int strindex[0];
943 } *filedata = data[LC_CTYPE].addr;
944 codeset = (char *) filedata
945 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
946
947 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
948 mask |= XPG_NORM_CODESET;
949
950 asprintf (&normalized_name, "%s%s%s.%s%s%s",
951 language, territory == NULL ? "" : "_", territory ?: "",
952 normalized_codeset,
953 modifier == NULL ? "" : "@", modifier ?: "");
954
955 add_alias (ah, normalized_name, replace, name, locrec_offset);
956 free (normalized_name);
957 }
958
959 /* Now read the locale.alias files looking for lines whose
960 right hand side matches our name after normalization. */
961 if (alias_file != NULL)
962 {
963 FILE *fp;
964 fp = fopen (alias_file, "r");
965 if (fp == NULL)
966 error (1, errno, _("locale alias file `%s' not found"),
967 alias_file);
968
969 /* No threads present. */
970 __fsetlocking (fp, FSETLOCKING_BYCALLER);
971
972 while (! feof_unlocked (fp))
973 {
974 /* It is a reasonable approach to use a fix buffer here
975 because
976 a) we are only interested in the first two fields
977 b) these fields must be usable as file names and so must
978 not be that long */
979 char buf[BUFSIZ];
980 char *alias;
981 char *value;
982 char *cp;
983
984 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
985 /* EOF reached. */
986 break;
987
988 cp = buf;
989 /* Ignore leading white space. */
990 while (isspace (cp[0]) && cp[0] != '\n')
991 ++cp;
992
993 /* A leading '#' signals a comment line. */
994 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
995 {
996 alias = cp++;
997 while (cp[0] != '\0' && !isspace (cp[0]))
998 ++cp;
999 /* Terminate alias name. */
1000 if (cp[0] != '\0')
1001 *cp++ = '\0';
1002
1003 /* Now look for the beginning of the value. */
1004 while (isspace (cp[0]))
1005 ++cp;
1006
1007 if (cp[0] != '\0')
1008 {
1009 value = cp++;
1010 while (cp[0] != '\0' && !isspace (cp[0]))
1011 ++cp;
1012 /* Terminate value. */
1013 if (cp[0] == '\n')
1014 {
1015 /* This has to be done to make the following
1016 test for the end of line possible. We are
1017 looking for the terminating '\n' which do not
1018 overwrite here. */
1019 *cp++ = '\0';
1020 *cp = '\n';
1021 }
1022 else if (cp[0] != '\0')
1023 *cp++ = '\0';
1024
1025 /* Does this alias refer to our locale? We will
1026 normalize the right hand side and compare the
1027 elements of the normalized form. */
1028 {
1029 const char *rhs_language;
1030 const char *rhs_modifier;
1031 const char *rhs_territory;
1032 const char *rhs_codeset;
1033 const char *rhs_normalized_codeset;
1034 int rhs_mask = _nl_explode_name (value,
1035 &rhs_language,
1036 &rhs_modifier,
1037 &rhs_territory,
1038 &rhs_codeset,
1039 &rhs_normalized_codeset);
1040 if (!strcmp (language, rhs_language)
1041 && ((rhs_mask & XPG_CODESET)
1042 /* He has a codeset, it must match normalized. */
1043 ? !strcmp ((mask & XPG_NORM_CODESET)
1044 ? normalized_codeset : codeset,
1045 (rhs_mask & XPG_NORM_CODESET)
1046 ? rhs_normalized_codeset : rhs_codeset)
1047 /* He has no codeset, we must also have none. */
1048 : (mask & XPG_CODESET) == 0)
1049 /* Codeset (or lack thereof) matches. */
1050 && !strcmp (territory ?: "", rhs_territory ?: "")
1051 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1052 /* We have a winner. */
1053 add_alias (ah, alias, replace,
1054 normalized_name ?: name, locrec_offset);
1055 if (rhs_mask & XPG_NORM_CODESET)
1056 free ((char *) rhs_normalized_codeset);
1057 }
1058 }
1059 }
1060
1061 /* Possibly not the whole line fits into the buffer.
1062 Ignore the rest of the line. */
1063 while (strchr (cp, '\n') == NULL)
1064 {
1065 cp = buf;
1066 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1067 /* Make sure the inner loop will be left. The outer
1068 loop will exit at the `feof' test. */
1069 *cp = '\n';
1070 }
1071 }
1072
1073 fclose (fp);
1074 }
1075
1076 if (mask & XPG_NORM_CODESET)
1077 free ((char *) normalized_codeset);
1078
1079 return 0;
1080 }
1081
1082
1083 int
1084 add_locales_to_archive (nlist, list, replace)
1085 size_t nlist;
1086 char *list[];
1087 bool replace;
1088 {
1089 struct locarhandle ah;
1090 int result = 0;
1091
1092 /* Open the archive. This call never returns if we cannot
1093 successfully open the archive. */
1094 open_archive (&ah, false);
1095
1096 while (nlist-- > 0)
1097 {
1098 const char *fname = *list++;
1099 size_t fnamelen = strlen (fname);
1100 struct stat64 st;
1101 DIR *dirp;
1102 struct dirent64 *d;
1103 int seen;
1104 locale_data_t data;
1105 int cnt;
1106
1107 if (! be_quiet)
1108 printf (_("Adding %s\n"), fname);
1109
1110 /* First see whether this really is a directory and whether it
1111 contains all the require locale category files. */
1112 if (stat64 (fname, &st) < 0)
1113 {
1114 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1115 strerror (errno));
1116 continue;
1117 }
1118 if (!S_ISDIR (st.st_mode))
1119 {
1120 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1121 continue;
1122 }
1123
1124 dirp = opendir (fname);
1125 if (dirp == NULL)
1126 {
1127 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1128 fname, strerror (errno));
1129 continue;
1130 }
1131
1132 seen = 0;
1133 while ((d = readdir64 (dirp)) != NULL)
1134 {
1135 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1136 if (cnt != LC_ALL)
1137 if (strcmp (d->d_name, locnames[cnt]) == 0)
1138 {
1139 unsigned char d_type;
1140
1141 /* We have an object of the required name. If it's
1142 a directory we have to look at a file with the
1143 prefix "SYS_". Otherwise we have found what we
1144 are looking for. */
1145 #ifdef _DIRENT_HAVE_D_TYPE
1146 d_type = d->d_type;
1147
1148 if (d_type != DT_REG)
1149 #endif
1150 {
1151 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1152
1153 #ifdef _DIRENT_HAVE_D_TYPE
1154 if (d_type == DT_UNKNOWN)
1155 #endif
1156 {
1157 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1158 d->d_name);
1159
1160 if (stat64 (fullname, &st) == -1)
1161 /* We cannot stat the file, ignore it. */
1162 break;
1163
1164 d_type = IFTODT (st.st_mode);
1165 }
1166
1167 if (d_type == DT_DIR)
1168 {
1169 /* We have to do more tests. The file is a
1170 directory and it therefore must contain a
1171 regular file with the same name except a
1172 "SYS_" prefix. */
1173 char *t = stpcpy (stpcpy (fullname, fname), "/");
1174 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
1175 d->d_name);
1176
1177 if (stat64 (fullname, &st) == -1)
1178 /* There is no SYS_* file or we cannot
1179 access it. */
1180 break;
1181
1182 d_type = IFTODT (st.st_mode);
1183 }
1184 }
1185
1186 /* If we found a regular file (eventually after
1187 following a symlink) we are successful. */
1188 if (d_type == DT_REG)
1189 ++seen;
1190 break;
1191 }
1192 }
1193
1194 closedir (dirp);
1195
1196 if (seen != __LC_LAST - 1)
1197 {
1198 /* We don't have all locale category files. Ignore the name. */
1199 error (0, 0, _("incomplete set of locale files in \"%s\""),
1200 fname);
1201 continue;
1202 }
1203
1204 /* Add the files to the archive. To do this we first compute
1205 sizes and the MD5 sums of all the files. */
1206 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1207 if (cnt != LC_ALL)
1208 {
1209 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1210 int fd;
1211
1212 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1213 fd = open64 (fullname, O_RDONLY);
1214 if (fd == -1 || fstat64 (fd, &st) == -1)
1215 {
1216 /* Cannot read the file. */
1217 if (fd != -1)
1218 close (fd);
1219 break;
1220 }
1221
1222 if (S_ISDIR (st.st_mode))
1223 {
1224 char *t;
1225 close (fd);
1226 t = stpcpy (stpcpy (fullname, fname), "/");
1227 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
1228 locnames[cnt]);
1229
1230 fd = open64 (fullname, O_RDONLY);
1231 if (fd == -1 || fstat64 (fd, &st) == -1
1232 || !S_ISREG (st.st_mode))
1233 {
1234 if (fd != -1)
1235 close (fd);
1236 break;
1237 }
1238 }
1239
1240 /* Map the file. */
1241 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1242 fd, 0);
1243 if (data[cnt].addr == MAP_FAILED)
1244 {
1245 /* Cannot map it. */
1246 close (fd);
1247 break;
1248 }
1249
1250 data[cnt].size = st.st_size;
1251 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1252
1253 /* We don't need the file descriptor anymore. */
1254 close (fd);
1255 }
1256
1257 if (cnt != __LC_LAST)
1258 {
1259 while (cnt-- > 0)
1260 if (cnt != LC_ALL)
1261 munmap (data[cnt].addr, data[cnt].size);
1262
1263 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1264
1265 continue;
1266 }
1267
1268 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1269
1270 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1271 if (cnt != LC_ALL)
1272 munmap (data[cnt].addr, data[cnt].size);
1273 }
1274
1275 /* We are done. */
1276 close_archive (&ah);
1277
1278 return result;
1279 }
1280
1281
1282 int
1283 delete_locales_from_archive (nlist, list)
1284 size_t nlist;
1285 char *list[];
1286 {
1287 struct locarhandle ah;
1288 struct locarhead *head;
1289 struct namehashent *namehashtab;
1290
1291 /* Open the archive. This call never returns if we cannot
1292 successfully open the archive. */
1293 open_archive (&ah, false);
1294
1295 head = ah.addr;
1296 namehashtab = (struct namehashent *) ((char *) ah.addr
1297 + head->namehash_offset);
1298
1299 while (nlist-- > 0)
1300 {
1301 const char *locname = *list++;
1302 uint32_t hval;
1303 unsigned int idx;
1304 unsigned int incr;
1305
1306 /* Search for this locale in the archive. */
1307 hval = compute_hashval (locname, strlen (locname));
1308
1309 idx = hval % head->namehash_size;
1310 incr = 1 + hval % (head->namehash_size - 2);
1311
1312 /* If the name_offset field is zero this means this is no
1313 deleted entry and therefore no entry can be found. */
1314 while (namehashtab[idx].name_offset != 0)
1315 {
1316 if (namehashtab[idx].hashval == hval
1317 && (strcmp (locname,
1318 (char *) ah.addr + namehashtab[idx].name_offset)
1319 == 0))
1320 {
1321 /* Found the entry. Now mark it as removed by zero-ing
1322 the reference to the locale record. */
1323 namehashtab[idx].locrec_offset = 0;
1324 break;
1325 }
1326
1327 idx += incr;
1328 if (idx >= head->namehash_size)
1329 idx -= head->namehash_size;
1330 }
1331
1332 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1333 error (0, 0, _("locale \"%s\" not in archive"), locname);
1334 }
1335
1336 close_archive (&ah);
1337
1338 return 0;
1339 }
1340
1341
1342 struct nameent
1343 {
1344 char *name;
1345 uint32_t locrec_offset;
1346 };
1347
1348
1349 struct dataent
1350 {
1351 const unsigned char *sum;
1352 uint32_t file_offset;
1353 uint32_t nlink;
1354 };
1355
1356
1357 static int
1358 nameentcmp (const void *a, const void *b)
1359 {
1360 return strcmp (((const struct nameent *) a)->name,
1361 ((const struct nameent *) b)->name);
1362 }
1363
1364
1365 static int
1366 dataentcmp (const void *a, const void *b)
1367 {
1368 if (((const struct dataent *) a)->file_offset
1369 < ((const struct dataent *) b)->file_offset)
1370 return -1;
1371
1372 if (((const struct dataent *) a)->file_offset
1373 > ((const struct dataent *) b)->file_offset)
1374 return 1;
1375
1376 return 0;
1377 }
1378
1379
1380 void
1381 show_archive_content (int verbose)
1382 {
1383 struct locarhandle ah;
1384 struct locarhead *head;
1385 struct namehashent *namehashtab;
1386 struct nameent *names;
1387 int cnt;
1388 int used;
1389
1390 /* Open the archive. This call never returns if we cannot
1391 successfully open the archive. */
1392 open_archive (&ah, true);
1393
1394 head = ah.addr;
1395
1396 names = (struct nameent *) xmalloc (head->namehash_used
1397 * sizeof (struct nameent));
1398
1399 namehashtab = (struct namehashent *) ((char *) ah.addr
1400 + head->namehash_offset);
1401 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1402 if (namehashtab[cnt].locrec_offset != 0)
1403 {
1404 assert (used < head->namehash_used);
1405 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1406 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
1407 }
1408
1409 /* Sort the names. */
1410 qsort (names, used, sizeof (struct nameent), nameentcmp);
1411
1412 if (verbose)
1413 {
1414 struct dataent *files;
1415 struct sumhashent *sumhashtab;
1416 int sumused;
1417
1418 files = (struct dataent *) xmalloc (head->sumhash_used
1419 * sizeof (struct sumhashent));
1420
1421 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1422 + head->sumhash_offset);
1423 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1424 if (sumhashtab[cnt].file_offset != 0)
1425 {
1426 assert (sumused < head->sumhash_used);
1427 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1428 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1429 files[sumused++].nlink = 0;
1430 }
1431
1432 /* Sort by file locations. */
1433 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1434
1435 /* Compute nlink fields. */
1436 for (cnt = 0; cnt < used; ++cnt)
1437 {
1438 struct locrecent *locrec;
1439 int idx;
1440
1441 locrec = (struct locrecent *) ((char *) ah.addr
1442 + names[cnt].locrec_offset);
1443 for (idx = 0; idx < __LC_LAST; ++idx)
1444 if (locrec->record[LC_ALL].offset != 0
1445 ? (idx == LC_ALL
1446 || (locrec->record[idx].offset
1447 < locrec->record[LC_ALL].offset)
1448 || (locrec->record[idx].offset + locrec->record[idx].len
1449 > (locrec->record[LC_ALL].offset
1450 + locrec->record[LC_ALL].len)))
1451 : idx != LC_ALL)
1452 {
1453 struct dataent *data, dataent;
1454
1455 dataent.file_offset = locrec->record[idx].offset;
1456 data = (struct dataent *) bsearch (&dataent, files, sumused,
1457 sizeof (struct dataent),
1458 dataentcmp);
1459 assert (data != NULL);
1460 ++data->nlink;
1461 }
1462 }
1463
1464 /* Print it. */
1465 for (cnt = 0; cnt < used; ++cnt)
1466 {
1467 struct locrecent *locrec;
1468 int idx, i;
1469
1470 locrec = (struct locrecent *) ((char *) ah.addr
1471 + names[cnt].locrec_offset);
1472 for (idx = 0; idx < __LC_LAST; ++idx)
1473 if (idx != LC_ALL)
1474 {
1475 struct dataent *data, dataent;
1476
1477 dataent.file_offset = locrec->record[idx].offset;
1478 if (locrec->record[LC_ALL].offset != 0
1479 && dataent.file_offset >= locrec->record[LC_ALL].offset
1480 && (dataent.file_offset + locrec->record[idx].len
1481 <= (locrec->record[LC_ALL].offset
1482 + locrec->record[LC_ALL].len)))
1483 dataent.file_offset = locrec->record[LC_ALL].offset;
1484
1485 data = (struct dataent *) bsearch (&dataent, files, sumused,
1486 sizeof (struct dataent),
1487 dataentcmp);
1488 printf ("%6d %7x %3d%c ",
1489 locrec->record[idx].len, locrec->record[idx].offset,
1490 data->nlink,
1491 dataent.file_offset == locrec->record[LC_ALL].offset
1492 ? '+' : ' ');
1493 for (i = 0; i < 16; i += 4)
1494 printf ("%02x%02x%02x%02x",
1495 data->sum[i], data->sum[i + 1],
1496 data->sum[i + 2], data->sum[i + 3]);
1497 printf (" %s/%s\n", names[cnt].name,
1498 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1499 : locnames[idx]);
1500 }
1501 }
1502 }
1503 else
1504 for (cnt = 0; cnt < used; ++cnt)
1505 puts (names[cnt].name);
1506
1507 close_archive (&ah);
1508
1509 exit (EXIT_SUCCESS);
1510 }
This page took 1.403027 seconds and 6 git commands to generate.