]> sourceware.org Git - glibc.git/blob - locale/programs/locarchive.c
2002-08-28 Jakub Jelinek <jakub@redhat.com>
[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 && ! be_quiet)
551 {
552 error (0, 0, "hash collision (%u) %s, %s",
553 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
554 }
555
556 /* Remember the first place we can insert the new entry. */
557 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
558 insert_idx = idx;
559
560 idx += incr;
561 if (idx >= head->namehash_size)
562 idx -= head->namehash_size;
563 }
564
565 /* Add as early as possible. */
566 if (insert_idx != -1)
567 idx = insert_idx;
568
569 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
570 return &namehashtab[idx];
571 }
572
573 static void
574 add_alias (struct locarhandle *ah, const char *alias, bool replace,
575 const char *oldname, uint32_t locrec_offset)
576 {
577 struct locarhead *head = ah->addr;
578 const size_t name_len = strlen (alias);
579 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
580 replace);
581 if (namehashent == NULL && ! replace)
582 return;
583
584 if (namehashent->name_offset == 0)
585 {
586 /* We are adding a new hash entry for this alias.
587 Determine whether we have to resize the file. */
588 if (head->string_used + name_len + 1 > head->string_size
589 || 100 * head->namehash_used > 75 * head->namehash_size)
590 {
591 /* The current archive is not large enough. */
592 enlarge_archive (ah, head);
593
594 /* The locrecent might have moved, so we have to look up
595 the old name afresh. */
596 namehashent = insert_name (ah, oldname, strlen (oldname), true);
597 assert (namehashent->name_offset != 0);
598 assert (namehashent->locrec_offset != 0);
599 locrec_offset = namehashent->locrec_offset;
600
601 /* Tail call to try the whole thing again. */
602 add_alias (ah, alias, replace, oldname, locrec_offset);
603 return;
604 }
605
606 /* Add the name string. */
607 memcpy (ah->addr + head->string_offset + head->string_used,
608 alias, name_len + 1);
609 namehashent->name_offset = head->string_offset + head->string_used;
610 head->string_used += name_len + 1;
611
612 ++head->namehash_used;
613 }
614
615 if (namehashent->locrec_offset != 0)
616 {
617 /* Replacing an existing entry.
618 Mark that we are no longer using the old locrecent. */
619 struct locrecent *locrecent
620 = (struct locrecent *) ((char *) ah->addr
621 + namehashent->locrec_offset);
622 --locrecent->refs;
623 }
624
625 /* Point this entry at the locrecent installed for the main name. */
626 namehashent->locrec_offset = locrec_offset;
627 }
628
629 static int /* qsort comparator used below */
630 cmpcategorysize (const void *a, const void *b)
631 {
632 if (*(const void **) a == NULL)
633 return 1;
634 if (*(const void **) b == NULL)
635 return -1;
636 return ((*(const struct locale_category_data **) a)->size
637 - (*(const struct locale_category_data **) b)->size);
638 }
639
640 /* Check the content of the archive for duplicates. Add the content
641 of the files if necessary. Returns the locrec_offset. */
642 static uint32_t
643 add_locale (struct locarhandle *ah,
644 const char *name, locale_data_t data, bool replace)
645 {
646 /* First look for the name. If it already exists and we are not
647 supposed to replace it don't do anything. If it does not exist
648 we have to allocate a new locale record. */
649 size_t name_len = strlen (name);
650 uint32_t file_offsets[__LC_LAST];
651 unsigned int num_new_offsets = 0;
652 struct sumhashent *sumhashtab;
653 uint32_t hval;
654 unsigned int cnt, idx;
655 struct locarhead *head;
656 struct namehashent *namehashent;
657 unsigned int incr;
658 struct locrecent *locrecent;
659 off64_t lastoffset;
660 char *ptr;
661 struct locale_category_data *size_order[__LC_LAST];
662 const size_t pagesz = getpagesize ();
663 int small_mask;
664
665 head = ah->addr;
666 sumhashtab = (struct sumhashent *) ((char *) ah->addr
667 + head->sumhash_offset);
668
669 memset (file_offsets, 0, sizeof (file_offsets));
670
671 size_order[LC_ALL] = NULL;
672 for (cnt = 0; cnt < __LC_LAST; ++cnt)
673 if (cnt != LC_ALL)
674 size_order[cnt] = &data[cnt];
675
676 /* Sort the array in ascending order of data size. */
677 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
678
679 small_mask = 0;
680 data[LC_ALL].size = 0;
681 for (cnt = 0; cnt < __LC_LAST; ++cnt)
682 if (size_order[cnt] != NULL)
683 {
684 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
685 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
686 {
687 /* This category makes the small-categories block
688 stop being small, so this is the end of the road. */
689 do
690 size_order[cnt++] = NULL;
691 while (cnt < __LC_LAST);
692 break;
693 }
694 data[LC_ALL].size += rounded_size;
695 small_mask |= 1 << (size_order[cnt] - data);
696 }
697
698 /* Copy the data for all the small categories into the LC_ALL
699 pseudo-category. */
700
701 data[LC_ALL].addr = alloca (data[LC_ALL].size);
702 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
703
704 ptr = data[LC_ALL].addr;
705 for (cnt = 0; cnt < __LC_LAST; ++cnt)
706 if (small_mask & (1 << cnt))
707 {
708 memcpy (ptr, data[cnt].addr, data[cnt].size);
709 ptr += (data[cnt].size + 15) & -16;
710 }
711 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
712
713 /* For each locale category data set determine whether the same data
714 is already somewhere in the archive. */
715 for (cnt = 0; cnt < __LC_LAST; ++cnt)
716 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
717 {
718 ++num_new_offsets;
719
720 /* Compute the hash value of the checksum to determine a
721 starting point for the search in the MD5 hash value
722 table. */
723 hval = compute_hashval (data[cnt].sum, 16);
724
725 idx = hval % head->sumhash_size;
726 incr = 1 + hval % (head->sumhash_size - 2);
727
728 while (sumhashtab[idx].file_offset != 0)
729 {
730 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
731 {
732 /* Found it. */
733 file_offsets[cnt] = sumhashtab[idx].file_offset;
734 --num_new_offsets;
735 break;
736 }
737
738 idx += incr;
739 if (idx >= head->sumhash_size)
740 idx -= head->sumhash_size;
741 }
742 }
743
744 /* Find a slot for the locale name in the hash table. */
745 namehashent = insert_name (ah, name, name_len, replace);
746 if (namehashent == NULL) /* Already exists and !REPLACE. */
747 return 0;
748
749 /* Determine whether we have to resize the file. */
750 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
751 || (namehashent->locrec_offset == 0
752 && (head->locrectab_used == head->locrectab_size
753 || head->string_used + name_len + 1 > head->string_size
754 || 100 * head->namehash_used > 75 * head->namehash_size)))
755 {
756 /* The current archive is not large enough. */
757 enlarge_archive (ah, head);
758 return add_locale (ah, name, data, replace);
759 }
760
761 /* Add the locale data which is not yet in the archive. */
762 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
763 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
764 && file_offsets[cnt] == 0)
765 {
766 /* The data for this section is not yet available in the
767 archive. Append it. */
768 off64_t lastpos;
769 uint32_t md5hval;
770
771 lastpos = lseek64 (ah->fd, 0, SEEK_END);
772 if (lastpos == (off64_t) -1)
773 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
774
775 /* If block of small categories would cross page boundary,
776 align it unless it immediately follows a large category. */
777 if (cnt == LC_ALL && lastoffset != lastpos
778 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
779 & -pagesz)
780 > ((data[cnt].size + pagesz - 1) & -pagesz)))
781 {
782 size_t sz = pagesz - (lastpos & (pagesz - 1));
783 char *zeros = alloca (sz);
784
785 memset (zeros, 0, sz);
786 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
787 error (EXIT_FAILURE, errno,
788 _("cannot add to locale archive"));
789
790 lastpos += sz;
791 }
792
793 /* Align all data to a 16 byte boundary. */
794 if ((lastpos & 15) != 0)
795 {
796 static const char zeros[15] = { 0, };
797
798 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
799 != 16 - (lastpos & 15))
800 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
801
802 lastpos += 16 - (lastpos & 15);
803 }
804
805 /* Remember the position. */
806 file_offsets[cnt] = lastpos;
807 lastoffset = lastpos + data[cnt].size;
808
809 /* Write the data. */
810 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
811 != data[cnt].size)
812 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
813
814 /* Add the hash value to the hash table. */
815 md5hval = compute_hashval (data[cnt].sum, 16);
816
817 idx = md5hval % head->sumhash_size;
818 incr = 1 + md5hval % (head->sumhash_size - 2);
819
820 while (sumhashtab[idx].file_offset != 0)
821 {
822 idx += incr;
823 if (idx >= head->sumhash_size)
824 idx -= head->sumhash_size;
825 }
826
827 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
828 sumhashtab[idx].file_offset = file_offsets[cnt];
829
830 ++head->sumhash_used;
831 }
832
833 lastoffset = file_offsets[LC_ALL];
834 for (cnt = 0; cnt < __LC_LAST; ++cnt)
835 if (small_mask & (1 << cnt))
836 {
837 file_offsets[cnt] = lastoffset;
838 lastoffset += (data[cnt].size + 15) & -16;
839 }
840
841 if (namehashent->name_offset == 0)
842 {
843 /* Add the name string. */
844 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
845 name, name_len + 1);
846 namehashent->name_offset = head->string_offset + head->string_used;
847 head->string_used += name_len + 1;
848 ++head->namehash_used;
849 }
850
851 if (namehashent->locrec_offset == 0)
852 {
853 /* Allocate a name location record. */
854 namehashent->locrec_offset = (head->locrectab_offset
855 + (head->locrectab_used++
856 * sizeof (struct locrecent)));
857 locrecent = (struct locrecent *) ((char *) ah->addr
858 + namehashent->locrec_offset);
859 locrecent->refs = 1;
860 }
861 else
862 {
863 /* If there are other aliases pointing to this locrecent,
864 we still need a new one. If not, reuse the old one. */
865
866 locrecent = (struct locrecent *) ((char *) ah->addr
867 + namehashent->locrec_offset);
868 if (locrecent->refs > 1)
869 {
870 --locrecent->refs;
871 namehashent->locrec_offset = (head->locrectab_offset
872 + (head->locrectab_used++
873 * sizeof (struct locrecent)));
874 locrecent = (struct locrecent *) ((char *) ah->addr
875 + namehashent->locrec_offset);
876 locrecent->refs = 1;
877 }
878 }
879
880 /* Fill in the table with the locations of the locale data. */
881 for (cnt = 0; cnt < __LC_LAST; ++cnt)
882 {
883 locrecent->record[cnt].offset = file_offsets[cnt];
884 locrecent->record[cnt].len = data[cnt].size;
885 }
886
887 return namehashent->locrec_offset;
888 }
889
890
891 /* Check the content of the archive for duplicates. Add the content
892 of the files if necessary. Add all the names, possibly overwriting
893 old files. */
894 int
895 add_locale_to_archive (ah, name, data, replace)
896 struct locarhandle *ah;
897 const char *name;
898 locale_data_t data;
899 bool replace;
900 {
901 char *normalized_name = NULL;
902 uint32_t locrec_offset;
903
904 /* First analyze the name to decide how to archive it. */
905 const char *language;
906 const char *modifier;
907 const char *territory;
908 const char *codeset;
909 const char *normalized_codeset;
910 int mask = _nl_explode_name (strdupa (name),
911 &language, &modifier, &territory,
912 &codeset, &normalized_codeset);
913
914 if (mask & XPG_NORM_CODESET)
915 /* This name contains a codeset in unnormalized form.
916 We will store it in the archive with a normalized name. */
917 asprintf (&normalized_name, "%s%s%s.%s%s%s",
918 language, territory == NULL ? "" : "_", territory ?: "",
919 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
920 modifier == NULL ? "" : "@", modifier ?: "");
921
922 /* This call does the main work. */
923 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
924 free (normalized_name);
925 if (locrec_offset == 0)
926 {
927 if (mask & XPG_NORM_CODESET)
928 free ((char *) normalized_codeset);
929 return -1;
930 }
931
932 if ((mask & XPG_CODESET) == 0)
933 {
934 /* This name lacks a codeset, so determine the locale's codeset and
935 add an alias for its name with normalized codeset appended. */
936
937 const struct
938 {
939 unsigned int magic;
940 unsigned int nstrings;
941 unsigned int strindex[0];
942 } *filedata = data[LC_CTYPE].addr;
943 codeset = (char *) filedata
944 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
945
946 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
947 mask |= XPG_NORM_CODESET;
948
949 asprintf (&normalized_name, "%s%s%s.%s%s%s",
950 language, territory == NULL ? "" : "_", territory ?: "",
951 normalized_codeset,
952 modifier == NULL ? "" : "@", modifier ?: "");
953
954 add_alias (ah, normalized_name, replace, name, locrec_offset);
955 free (normalized_name);
956 }
957
958 /* Now read the locale.alias files looking for lines whose
959 right hand side matches our name after normalization. */
960 if (alias_file != NULL)
961 {
962 FILE *fp;
963 fp = fopen (alias_file, "r");
964 if (fp == NULL)
965 error (1, errno, _("locale alias file `%s' not found"),
966 alias_file);
967
968 /* No threads present. */
969 __fsetlocking (fp, FSETLOCKING_BYCALLER);
970
971 while (! feof_unlocked (fp))
972 {
973 /* It is a reasonable approach to use a fix buffer here
974 because
975 a) we are only interested in the first two fields
976 b) these fields must be usable as file names and so must
977 not be that long */
978 char buf[BUFSIZ];
979 char *alias;
980 char *value;
981 char *cp;
982
983 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
984 /* EOF reached. */
985 break;
986
987 cp = buf;
988 /* Ignore leading white space. */
989 while (isspace (cp[0]) && cp[0] != '\n')
990 ++cp;
991
992 /* A leading '#' signals a comment line. */
993 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
994 {
995 alias = cp++;
996 while (cp[0] != '\0' && !isspace (cp[0]))
997 ++cp;
998 /* Terminate alias name. */
999 if (cp[0] != '\0')
1000 *cp++ = '\0';
1001
1002 /* Now look for the beginning of the value. */
1003 while (isspace (cp[0]))
1004 ++cp;
1005
1006 if (cp[0] != '\0')
1007 {
1008 value = cp++;
1009 while (cp[0] != '\0' && !isspace (cp[0]))
1010 ++cp;
1011 /* Terminate value. */
1012 if (cp[0] == '\n')
1013 {
1014 /* This has to be done to make the following
1015 test for the end of line possible. We are
1016 looking for the terminating '\n' which do not
1017 overwrite here. */
1018 *cp++ = '\0';
1019 *cp = '\n';
1020 }
1021 else if (cp[0] != '\0')
1022 *cp++ = '\0';
1023
1024 /* Does this alias refer to our locale? We will
1025 normalize the right hand side and compare the
1026 elements of the normalized form. */
1027 {
1028 const char *rhs_language;
1029 const char *rhs_modifier;
1030 const char *rhs_territory;
1031 const char *rhs_codeset;
1032 const char *rhs_normalized_codeset;
1033 int rhs_mask = _nl_explode_name (value,
1034 &rhs_language,
1035 &rhs_modifier,
1036 &rhs_territory,
1037 &rhs_codeset,
1038 &rhs_normalized_codeset);
1039 if (!strcmp (language, rhs_language)
1040 && ((rhs_mask & XPG_CODESET)
1041 /* He has a codeset, it must match normalized. */
1042 ? !strcmp ((mask & XPG_NORM_CODESET)
1043 ? normalized_codeset : codeset,
1044 (rhs_mask & XPG_NORM_CODESET)
1045 ? rhs_normalized_codeset : rhs_codeset)
1046 /* He has no codeset, we must also have none. */
1047 : (mask & XPG_CODESET) == 0)
1048 /* Codeset (or lack thereof) matches. */
1049 && !strcmp (territory ?: "", rhs_territory ?: "")
1050 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1051 /* We have a winner. */
1052 add_alias (ah, alias, replace,
1053 normalized_name ?: name, locrec_offset);
1054 if (rhs_mask & XPG_NORM_CODESET)
1055 free ((char *) rhs_normalized_codeset);
1056 }
1057 }
1058 }
1059
1060 /* Possibly not the whole line fits into the buffer.
1061 Ignore the rest of the line. */
1062 while (strchr (cp, '\n') == NULL)
1063 {
1064 cp = buf;
1065 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1066 /* Make sure the inner loop will be left. The outer
1067 loop will exit at the `feof' test. */
1068 *cp = '\n';
1069 }
1070 }
1071
1072 fclose (fp);
1073 }
1074
1075 if (mask & XPG_NORM_CODESET)
1076 free ((char *) normalized_codeset);
1077
1078 return 0;
1079 }
1080
1081
1082 int
1083 add_locales_to_archive (nlist, list, replace)
1084 size_t nlist;
1085 char *list[];
1086 bool replace;
1087 {
1088 struct locarhandle ah;
1089 int result = 0;
1090
1091 /* Open the archive. This call never returns if we cannot
1092 successfully open the archive. */
1093 open_archive (&ah, false);
1094
1095 while (nlist-- > 0)
1096 {
1097 const char *fname = *list++;
1098 size_t fnamelen = strlen (fname);
1099 struct stat64 st;
1100 DIR *dirp;
1101 struct dirent64 *d;
1102 int seen;
1103 locale_data_t data;
1104 int cnt;
1105
1106 if (! be_quiet)
1107 printf (_("Adding %s\n"), fname);
1108
1109 /* First see whether this really is a directory and whether it
1110 contains all the require locale category files. */
1111 if (stat64 (fname, &st) < 0)
1112 {
1113 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1114 strerror (errno));
1115 continue;
1116 }
1117 if (!S_ISDIR (st.st_mode))
1118 {
1119 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1120 continue;
1121 }
1122
1123 dirp = opendir (fname);
1124 if (dirp == NULL)
1125 {
1126 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1127 fname, strerror (errno));
1128 continue;
1129 }
1130
1131 seen = 0;
1132 while ((d = readdir64 (dirp)) != NULL)
1133 {
1134 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1135 if (cnt != LC_ALL)
1136 if (strcmp (d->d_name, locnames[cnt]) == 0)
1137 {
1138 unsigned char d_type;
1139
1140 /* We have an object of the required name. If it's
1141 a directory we have to look at a file with the
1142 prefix "SYS_". Otherwise we have found what we
1143 are looking for. */
1144 #ifdef _DIRENT_HAVE_D_TYPE
1145 d_type = d->d_type;
1146
1147 if (d_type != DT_REG)
1148 #endif
1149 {
1150 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1151
1152 #ifdef _DIRENT_HAVE_D_TYPE
1153 if (d_type == DT_UNKNOWN)
1154 #endif
1155 {
1156 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1157 d->d_name);
1158
1159 if (stat64 (fullname, &st) == -1)
1160 /* We cannot stat the file, ignore it. */
1161 break;
1162
1163 d_type = IFTODT (st.st_mode);
1164 }
1165
1166 if (d_type == DT_DIR)
1167 {
1168 /* We have to do more tests. The file is a
1169 directory and it therefore must contain a
1170 regular file with the same name except a
1171 "SYS_" prefix. */
1172 char *t = stpcpy (stpcpy (fullname, fname), "/");
1173 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
1174 d->d_name);
1175
1176 if (stat64 (fullname, &st) == -1)
1177 /* There is no SYS_* file or we cannot
1178 access it. */
1179 break;
1180
1181 d_type = IFTODT (st.st_mode);
1182 }
1183 }
1184
1185 /* If we found a regular file (eventually after
1186 following a symlink) we are successful. */
1187 if (d_type == DT_REG)
1188 ++seen;
1189 break;
1190 }
1191 }
1192
1193 closedir (dirp);
1194
1195 if (seen != __LC_LAST - 1)
1196 {
1197 /* We don't have all locale category files. Ignore the name. */
1198 error (0, 0, _("incomplete set of locale files in \"%s\""),
1199 fname);
1200 continue;
1201 }
1202
1203 /* Add the files to the archive. To do this we first compute
1204 sizes and the MD5 sums of all the files. */
1205 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1206 if (cnt != LC_ALL)
1207 {
1208 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1209 int fd;
1210
1211 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1212 fd = open64 (fullname, O_RDONLY);
1213 if (fd == -1 || fstat64 (fd, &st) == -1)
1214 {
1215 /* Cannot read the file. */
1216 if (fd != -1)
1217 close (fd);
1218 break;
1219 }
1220
1221 if (S_ISDIR (st.st_mode))
1222 {
1223 char *t;
1224 close (fd);
1225 t = stpcpy (stpcpy (fullname, fname), "/");
1226 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
1227 locnames[cnt]);
1228
1229 fd = open64 (fullname, O_RDONLY);
1230 if (fd == -1 || fstat64 (fd, &st) == -1
1231 || !S_ISREG (st.st_mode))
1232 {
1233 if (fd != -1)
1234 close (fd);
1235 break;
1236 }
1237 }
1238
1239 /* Map the file. */
1240 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1241 fd, 0);
1242 if (data[cnt].addr == MAP_FAILED)
1243 {
1244 /* Cannot map it. */
1245 close (fd);
1246 break;
1247 }
1248
1249 data[cnt].size = st.st_size;
1250 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1251
1252 /* We don't need the file descriptor anymore. */
1253 close (fd);
1254 }
1255
1256 if (cnt != __LC_LAST)
1257 {
1258 while (cnt-- > 0)
1259 if (cnt != LC_ALL)
1260 munmap (data[cnt].addr, data[cnt].size);
1261
1262 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1263
1264 continue;
1265 }
1266
1267 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1268
1269 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1270 if (cnt != LC_ALL)
1271 munmap (data[cnt].addr, data[cnt].size);
1272 }
1273
1274 /* We are done. */
1275 close_archive (&ah);
1276
1277 return result;
1278 }
1279
1280
1281 int
1282 delete_locales_from_archive (nlist, list)
1283 size_t nlist;
1284 char *list[];
1285 {
1286 struct locarhandle ah;
1287 struct locarhead *head;
1288 struct namehashent *namehashtab;
1289
1290 /* Open the archive. This call never returns if we cannot
1291 successfully open the archive. */
1292 open_archive (&ah, false);
1293
1294 head = ah.addr;
1295 namehashtab = (struct namehashent *) ((char *) ah.addr
1296 + head->namehash_offset);
1297
1298 while (nlist-- > 0)
1299 {
1300 const char *locname = *list++;
1301 uint32_t hval;
1302 unsigned int idx;
1303 unsigned int incr;
1304
1305 /* Search for this locale in the archive. */
1306 hval = compute_hashval (locname, strlen (locname));
1307
1308 idx = hval % head->namehash_size;
1309 incr = 1 + hval % (head->namehash_size - 2);
1310
1311 /* If the name_offset field is zero this means this is no
1312 deleted entry and therefore no entry can be found. */
1313 while (namehashtab[idx].name_offset != 0)
1314 {
1315 if (namehashtab[idx].hashval == hval
1316 && (strcmp (locname,
1317 (char *) ah.addr + namehashtab[idx].name_offset)
1318 == 0))
1319 {
1320 /* Found the entry. Now mark it as removed by zero-ing
1321 the reference to the locale record. */
1322 namehashtab[idx].locrec_offset = 0;
1323 break;
1324 }
1325
1326 idx += incr;
1327 if (idx >= head->namehash_size)
1328 idx -= head->namehash_size;
1329 }
1330
1331 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1332 error (0, 0, _("locale \"%s\" not in archive"), locname);
1333 }
1334
1335 close_archive (&ah);
1336
1337 return 0;
1338 }
1339
1340
1341 struct nameent
1342 {
1343 char *name;
1344 uint32_t locrec_offset;
1345 };
1346
1347
1348 struct dataent
1349 {
1350 const unsigned char *sum;
1351 uint32_t file_offset;
1352 uint32_t nlink;
1353 };
1354
1355
1356 static int
1357 nameentcmp (const void *a, const void *b)
1358 {
1359 return strcmp (((const struct nameent *) a)->name,
1360 ((const struct nameent *) b)->name);
1361 }
1362
1363
1364 static int
1365 dataentcmp (const void *a, const void *b)
1366 {
1367 if (((const struct dataent *) a)->file_offset
1368 < ((const struct dataent *) b)->file_offset)
1369 return -1;
1370
1371 if (((const struct dataent *) a)->file_offset
1372 > ((const struct dataent *) b)->file_offset)
1373 return 1;
1374
1375 return 0;
1376 }
1377
1378
1379 void
1380 show_archive_content (int verbose)
1381 {
1382 struct locarhandle ah;
1383 struct locarhead *head;
1384 struct namehashent *namehashtab;
1385 struct nameent *names;
1386 int cnt;
1387 int used;
1388
1389 /* Open the archive. This call never returns if we cannot
1390 successfully open the archive. */
1391 open_archive (&ah, true);
1392
1393 head = ah.addr;
1394
1395 names = (struct nameent *) xmalloc (head->namehash_used
1396 * sizeof (struct nameent));
1397
1398 namehashtab = (struct namehashent *) ((char *) ah.addr
1399 + head->namehash_offset);
1400 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1401 if (namehashtab[cnt].locrec_offset != 0)
1402 {
1403 assert (used < head->namehash_used);
1404 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1405 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
1406 }
1407
1408 /* Sort the names. */
1409 qsort (names, used, sizeof (struct nameent), nameentcmp);
1410
1411 if (verbose)
1412 {
1413 struct dataent *files;
1414 struct sumhashent *sumhashtab;
1415 int sumused;
1416
1417 files = (struct dataent *) xmalloc (head->sumhash_used
1418 * sizeof (struct sumhashent));
1419
1420 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1421 + head->sumhash_offset);
1422 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1423 if (sumhashtab[cnt].file_offset != 0)
1424 {
1425 assert (sumused < head->sumhash_used);
1426 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1427 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1428 files[sumused++].nlink = 0;
1429 }
1430
1431 /* Sort by file locations. */
1432 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1433
1434 /* Compute nlink fields. */
1435 for (cnt = 0; cnt < used; ++cnt)
1436 {
1437 struct locrecent *locrec;
1438 int idx;
1439
1440 locrec = (struct locrecent *) ((char *) ah.addr
1441 + names[cnt].locrec_offset);
1442 for (idx = 0; idx < __LC_LAST; ++idx)
1443 if (locrec->record[LC_ALL].offset != 0
1444 ? (idx == LC_ALL
1445 || (locrec->record[idx].offset
1446 < locrec->record[LC_ALL].offset)
1447 || (locrec->record[idx].offset + locrec->record[idx].len
1448 > (locrec->record[LC_ALL].offset
1449 + locrec->record[LC_ALL].len)))
1450 : idx != LC_ALL)
1451 {
1452 struct dataent *data, dataent;
1453
1454 dataent.file_offset = locrec->record[idx].offset;
1455 data = (struct dataent *) bsearch (&dataent, files, sumused,
1456 sizeof (struct dataent),
1457 dataentcmp);
1458 assert (data != NULL);
1459 ++data->nlink;
1460 }
1461 }
1462
1463 /* Print it. */
1464 for (cnt = 0; cnt < used; ++cnt)
1465 {
1466 struct locrecent *locrec;
1467 int idx, i;
1468
1469 locrec = (struct locrecent *) ((char *) ah.addr
1470 + names[cnt].locrec_offset);
1471 for (idx = 0; idx < __LC_LAST; ++idx)
1472 if (idx != LC_ALL)
1473 {
1474 struct dataent *data, dataent;
1475
1476 dataent.file_offset = locrec->record[idx].offset;
1477 if (locrec->record[LC_ALL].offset != 0
1478 && dataent.file_offset >= locrec->record[LC_ALL].offset
1479 && (dataent.file_offset + locrec->record[idx].len
1480 <= (locrec->record[LC_ALL].offset
1481 + locrec->record[LC_ALL].len)))
1482 dataent.file_offset = locrec->record[LC_ALL].offset;
1483
1484 data = (struct dataent *) bsearch (&dataent, files, sumused,
1485 sizeof (struct dataent),
1486 dataentcmp);
1487 printf ("%6d %7x %3d%c ",
1488 locrec->record[idx].len, locrec->record[idx].offset,
1489 data->nlink,
1490 dataent.file_offset == locrec->record[LC_ALL].offset
1491 ? '+' : ' ');
1492 for (i = 0; i < 16; i += 4)
1493 printf ("%02x%02x%02x%02x",
1494 data->sum[i], data->sum[i + 1],
1495 data->sum[i + 2], data->sum[i + 3]);
1496 printf (" %s/%s\n", names[cnt].name,
1497 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1498 : locnames[idx]);
1499 }
1500 }
1501 }
1502 else
1503 for (cnt = 0; cnt < used; ++cnt)
1504 puts (names[cnt].name);
1505
1506 close_archive (&ah);
1507
1508 exit (EXIT_SUCCESS);
1509 }
This page took 0.113699 seconds and 6 git commands to generate.