crypt: Remove libcrypt support
[glibc.git] / locale / programs / locfile.c
blob1fecca6520b0b01b7882554c9fd062861d25e93f
1 /* Copyright (C) 1996-2023 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation; version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <assert.h>
31 #include <wchar.h>
33 #include "localedef.h"
34 #include "localeinfo.h"
35 #include "locfile.h"
36 #include "simple-hash.h"
38 #include "locfile-kw.h"
39 #include "md5.h"
41 #define obstack_chunk_alloc xmalloc
42 #define obstack_chunk_free free
44 /* Temporary storage of the locale data before writing it to the archive. */
45 static locale_data_t to_archive;
48 int
49 locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
51 const char *filename = result->name;
52 const char *repertoire_name = result->repertoire_name;
53 int locale_mask = result->needed & ~result->avail;
54 struct linereader *ldfile;
55 int not_here = ALL_LOCALES;
57 /* If no repertoire name was specified use the global one. */
58 if (repertoire_name == NULL)
59 repertoire_name = repertoire_global;
61 /* Open the locale definition file. */
62 ldfile = lr_open (filename, locfile_hash);
63 if (ldfile == NULL)
65 if (filename != NULL && filename[0] != '/')
67 char *i18npath = getenv ("I18NPATH");
68 if (i18npath != NULL && *i18npath != '\0')
70 const size_t pathlen = strlen (i18npath);
71 char i18npathbuf[pathlen + 1];
72 char path[strlen (filename) + 1 + pathlen
73 + sizeof ("/locales/") - 1];
74 char *next;
75 i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
77 while (ldfile == NULL
78 && (next = strsep (&i18npath, ":")) != NULL)
80 stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
82 ldfile = lr_open (path, locfile_hash);
84 if (ldfile == NULL)
86 stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
88 ldfile = lr_open (path, locfile_hash);
93 /* Test in the default directory. */
94 if (ldfile == NULL)
96 char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
98 stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
99 ldfile = lr_open (path, locfile_hash);
103 if (ldfile == NULL)
104 return 1;
107 /* Parse locale definition file and store result in RESULT. */
108 while (1)
110 struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
111 enum token_t nowtok = now->tok;
112 struct token *arg;
114 if (nowtok == tok_eof)
115 break;
117 if (nowtok == tok_eol)
118 /* Ignore empty lines. */
119 continue;
121 switch (nowtok)
123 case tok_escape_char:
124 case tok_comment_char:
125 /* We need an argument. */
126 arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
128 if (arg->tok != tok_ident)
130 SYNTAX_ERROR (_("bad argument"));
131 continue;
134 if (arg->val.str.lenmb != 1)
136 lr_error (ldfile, _("\
137 argument to `%s' must be a single character"),
138 nowtok == tok_escape_char
139 ? "escape_char" : "comment_char");
141 lr_ignore_rest (ldfile, 0);
142 continue;
145 if (nowtok == tok_escape_char)
146 ldfile->escape_char = *arg->val.str.startmb;
147 else
148 ldfile->comment_char = *arg->val.str.startmb;
149 break;
151 case tok_repertoiremap:
152 /* We need an argument. */
153 arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
155 if (arg->tok != tok_ident)
157 SYNTAX_ERROR (_("bad argument"));
158 continue;
161 if (repertoire_name == NULL)
163 char *newp = alloca (arg->val.str.lenmb + 1);
165 *((char *) mempcpy (newp, arg->val.str.startmb,
166 arg->val.str.lenmb)) = '\0';
167 repertoire_name = newp;
169 break;
171 case tok_lc_ctype:
172 ctype_read (ldfile, result, charmap, repertoire_name,
173 (locale_mask & CTYPE_LOCALE) == 0);
174 result->avail |= locale_mask & CTYPE_LOCALE;
175 not_here ^= CTYPE_LOCALE;
176 continue;
178 case tok_lc_collate:
179 collate_read (ldfile, result, charmap, repertoire_name,
180 (locale_mask & COLLATE_LOCALE) == 0);
181 result->avail |= locale_mask & COLLATE_LOCALE;
182 not_here ^= COLLATE_LOCALE;
183 continue;
185 case tok_lc_monetary:
186 monetary_read (ldfile, result, charmap, repertoire_name,
187 (locale_mask & MONETARY_LOCALE) == 0);
188 result->avail |= locale_mask & MONETARY_LOCALE;
189 not_here ^= MONETARY_LOCALE;
190 continue;
192 case tok_lc_numeric:
193 numeric_read (ldfile, result, charmap, repertoire_name,
194 (locale_mask & NUMERIC_LOCALE) == 0);
195 result->avail |= locale_mask & NUMERIC_LOCALE;
196 not_here ^= NUMERIC_LOCALE;
197 continue;
199 case tok_lc_time:
200 time_read (ldfile, result, charmap, repertoire_name,
201 (locale_mask & TIME_LOCALE) == 0);
202 result->avail |= locale_mask & TIME_LOCALE;
203 not_here ^= TIME_LOCALE;
204 continue;
206 case tok_lc_messages:
207 messages_read (ldfile, result, charmap, repertoire_name,
208 (locale_mask & MESSAGES_LOCALE) == 0);
209 result->avail |= locale_mask & MESSAGES_LOCALE;
210 not_here ^= MESSAGES_LOCALE;
211 continue;
213 case tok_lc_paper:
214 paper_read (ldfile, result, charmap, repertoire_name,
215 (locale_mask & PAPER_LOCALE) == 0);
216 result->avail |= locale_mask & PAPER_LOCALE;
217 not_here ^= PAPER_LOCALE;
218 continue;
220 case tok_lc_name:
221 name_read (ldfile, result, charmap, repertoire_name,
222 (locale_mask & NAME_LOCALE) == 0);
223 result->avail |= locale_mask & NAME_LOCALE;
224 not_here ^= NAME_LOCALE;
225 continue;
227 case tok_lc_address:
228 address_read (ldfile, result, charmap, repertoire_name,
229 (locale_mask & ADDRESS_LOCALE) == 0);
230 result->avail |= locale_mask & ADDRESS_LOCALE;
231 not_here ^= ADDRESS_LOCALE;
232 continue;
234 case tok_lc_telephone:
235 telephone_read (ldfile, result, charmap, repertoire_name,
236 (locale_mask & TELEPHONE_LOCALE) == 0);
237 result->avail |= locale_mask & TELEPHONE_LOCALE;
238 not_here ^= TELEPHONE_LOCALE;
239 continue;
241 case tok_lc_measurement:
242 measurement_read (ldfile, result, charmap, repertoire_name,
243 (locale_mask & MEASUREMENT_LOCALE) == 0);
244 result->avail |= locale_mask & MEASUREMENT_LOCALE;
245 not_here ^= MEASUREMENT_LOCALE;
246 continue;
248 case tok_lc_identification:
249 identification_read (ldfile, result, charmap, repertoire_name,
250 (locale_mask & IDENTIFICATION_LOCALE) == 0);
251 result->avail |= locale_mask & IDENTIFICATION_LOCALE;
252 not_here ^= IDENTIFICATION_LOCALE;
253 continue;
255 default:
256 SYNTAX_ERROR (_("\
257 syntax error: not inside a locale definition section"));
258 continue;
261 /* The rest of the line must be empty. */
262 lr_ignore_rest (ldfile, 1);
265 /* We read all of the file. */
266 lr_close (ldfile);
268 /* Mark the categories which are not contained in the file. We assume
269 them to be available and the default data will be used. */
270 result->avail |= not_here;
272 return 0;
276 /* Semantic checking of locale specifications. */
278 static void (*const check_funcs[]) (struct localedef_t *,
279 const struct charmap_t *) =
281 [LC_CTYPE] = ctype_finish,
282 [LC_COLLATE] = collate_finish,
283 [LC_MESSAGES] = messages_finish,
284 [LC_MONETARY] = monetary_finish,
285 [LC_NUMERIC] = numeric_finish,
286 [LC_TIME] = time_finish,
287 [LC_PAPER] = paper_finish,
288 [LC_NAME] = name_finish,
289 [LC_ADDRESS] = address_finish,
290 [LC_TELEPHONE] = telephone_finish,
291 [LC_MEASUREMENT] = measurement_finish,
292 [LC_IDENTIFICATION] = identification_finish
295 void
296 check_all_categories (struct localedef_t *definitions,
297 const struct charmap_t *charmap)
299 int cnt;
301 for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
302 if (check_funcs[cnt] != NULL)
303 check_funcs[cnt] (definitions, charmap);
307 /* Writing the locale data files. All files use the same output_path. */
309 static void (*const write_funcs[]) (struct localedef_t *,
310 const struct charmap_t *, const char *) =
312 [LC_CTYPE] = ctype_output,
313 [LC_COLLATE] = collate_output,
314 [LC_MESSAGES] = messages_output,
315 [LC_MONETARY] = monetary_output,
316 [LC_NUMERIC] = numeric_output,
317 [LC_TIME] = time_output,
318 [LC_PAPER] = paper_output,
319 [LC_NAME] = name_output,
320 [LC_ADDRESS] = address_output,
321 [LC_TELEPHONE] = telephone_output,
322 [LC_MEASUREMENT] = measurement_output,
323 [LC_IDENTIFICATION] = identification_output
327 void
328 write_all_categories (struct localedef_t *definitions,
329 const struct charmap_t *charmap, const char *locname,
330 const char *output_path)
332 int cnt;
334 for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
335 if (write_funcs[cnt] != NULL)
336 write_funcs[cnt] (definitions, charmap, output_path);
338 if (! no_archive)
340 /* The data has to be added to the archive. Do this now. */
341 struct locarhandle ah;
343 /* Open the archive. This call never returns if we cannot
344 successfully open the archive. */
345 ah.fname = NULL;
346 open_archive (&ah, false);
348 if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
349 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
351 /* We are done. */
352 close_archive (&ah);
357 /* Return a NULL terminated list of the directories next to output_path
358 that have the same owner, group, permissions and device as output_path. */
359 static const char **
360 siblings_uncached (const char *output_path)
362 size_t len;
363 char *base, *p;
364 struct stat64 output_stat;
365 DIR *dirp;
366 int nelems;
367 const char **elems;
369 /* Remove trailing slashes and trailing pathname component. */
370 len = strlen (output_path);
371 base = (char *) alloca (len);
372 memcpy (base, output_path, len);
373 p = base + len;
374 while (p > base && p[-1] == '/')
375 p--;
376 if (p == base)
377 return NULL;
379 p--;
380 while (p > base && p[-1] != '/');
381 if (p == base)
382 return NULL;
383 *--p = '\0';
384 len = p - base;
386 /* Get the properties of output_path. */
387 if (lstat64 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
388 return NULL;
390 /* Iterate through the directories in base directory. */
391 dirp = opendir (base);
392 if (dirp == NULL)
393 return NULL;
394 nelems = 0;
395 elems = NULL;
396 for (;;)
398 struct dirent64 *other_dentry;
399 const char *other_name;
400 char *other_path;
401 struct stat64 other_stat;
403 other_dentry = readdir64 (dirp);
404 if (other_dentry == NULL)
405 break;
407 other_name = other_dentry->d_name;
408 if (strcmp (other_name, ".") == 0 || strcmp (other_name, "..") == 0)
409 continue;
411 other_path = (char *) xmalloc (len + 1 + strlen (other_name) + 2);
412 memcpy (other_path, base, len);
413 other_path[len] = '/';
414 strcpy (other_path + len + 1, other_name);
416 if (lstat64 (other_path, &other_stat) >= 0
417 && S_ISDIR (other_stat.st_mode)
418 && other_stat.st_uid == output_stat.st_uid
419 && other_stat.st_gid == output_stat.st_gid
420 && other_stat.st_mode == output_stat.st_mode
421 && other_stat.st_dev == output_stat.st_dev)
423 /* Found a subdirectory. Add a trailing slash and store it. */
424 p = other_path + len + 1 + strlen (other_name);
425 *p++ = '/';
426 *p = '\0';
427 elems = (const char **) xrealloc ((char *) elems,
428 (nelems + 2) * sizeof (char **));
429 elems[nelems++] = other_path;
431 else
432 free (other_path);
434 closedir (dirp);
436 if (elems != NULL)
437 elems[nelems] = NULL;
438 return elems;
442 /* Return a NULL terminated list of the directories next to output_path
443 that have the same owner, group, permissions and device as output_path.
444 Cache the result for future calls. */
445 static const char **
446 siblings (const char *output_path)
448 static const char *last_output_path;
449 static const char **last_result;
451 if (output_path != last_output_path)
453 if (last_result != NULL)
455 const char **p;
457 for (p = last_result; *p != NULL; p++)
458 free ((char *) *p);
459 free (last_result);
462 last_output_path = output_path;
463 last_result = siblings_uncached (output_path);
465 return last_result;
469 /* Read as many bytes from a file descriptor as possible. */
470 static ssize_t
471 full_read (int fd, void *bufarea, size_t nbyte)
473 char *buf = (char *) bufarea;
475 while (nbyte > 0)
477 ssize_t retval = read (fd, buf, nbyte);
479 if (retval == 0)
480 break;
481 else if (retval > 0)
483 buf += retval;
484 nbyte -= retval;
486 else if (errno != EINTR)
487 return retval;
489 return buf - (char *) bufarea;
493 /* Compare the contents of two regular files of the same size. Return 0
494 if they are equal, 1 if they are different, or -1 if an error occurs. */
495 static int
496 compare_files (const char *filename1, const char *filename2, size_t size,
497 size_t blocksize)
499 int fd1, fd2;
500 int ret = -1;
502 fd1 = open (filename1, O_RDONLY);
503 if (fd1 >= 0)
505 fd2 = open (filename2, O_RDONLY);
506 if (fd2 >= 0)
508 char *buf1 = (char *) xmalloc (2 * blocksize);
509 char *buf2 = buf1 + blocksize;
511 ret = 0;
512 while (size > 0)
514 size_t bytes = (size < blocksize ? size : blocksize);
516 if (full_read (fd1, buf1, bytes) < (ssize_t) bytes)
518 ret = -1;
519 break;
521 if (full_read (fd2, buf2, bytes) < (ssize_t) bytes)
523 ret = -1;
524 break;
526 if (memcmp (buf1, buf2, bytes) != 0)
528 ret = 1;
529 break;
531 size -= bytes;
534 free (buf1);
535 close (fd2);
537 close (fd1);
539 return ret;
542 /* True if the locale files use the opposite endianness to the
543 machine running localedef. */
544 bool swap_endianness_p;
546 /* When called outside a start_locale_structure/end_locale_structure
547 or start_locale_prelude/end_locale_prelude block, record that the
548 next byte in FILE's obstack will be the first byte of a new element.
549 Do likewise for the first call inside a start_locale_structure/
550 end_locale_structure block. */
551 static void
552 record_offset (struct locale_file *file)
554 if (file->structure_stage < 2)
556 assert (file->next_element < file->n_elements);
557 file->offsets[file->next_element++]
558 = (obstack_object_size (&file->data)
559 + (file->n_elements + 2) * sizeof (uint32_t));
560 if (file->structure_stage == 1)
561 file->structure_stage = 2;
565 /* Initialize FILE for a new output file. N_ELEMENTS is the number
566 of elements in the file. */
567 void
568 init_locale_data (struct locale_file *file, size_t n_elements)
570 file->n_elements = n_elements;
571 file->next_element = 0;
572 file->offsets = xmalloc (sizeof (uint32_t) * n_elements);
573 obstack_init (&file->data);
574 file->structure_stage = 0;
577 /* Align the size of FILE's obstack object to BOUNDARY bytes. */
578 void
579 align_locale_data (struct locale_file *file, size_t boundary)
581 size_t size = -obstack_object_size (&file->data) & (boundary - 1);
582 obstack_blank (&file->data, size);
583 memset (obstack_next_free (&file->data) - size, 0, size);
586 /* Record that FILE's next element contains no data. */
587 void
588 add_locale_empty (struct locale_file *file)
590 record_offset (file);
593 /* Record that FILE's next element consists of SIZE bytes starting at DATA. */
594 void
595 add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
597 record_offset (file);
598 obstack_grow (&file->data, data, size);
601 /* Finish the current object on OBSTACK and use it as the data for FILE's
602 next element. */
603 void
604 add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
606 size_t size = obstack_object_size (obstack);
607 record_offset (file);
608 obstack_grow (&file->data, obstack_finish (obstack), size);
611 /* Use STRING as FILE's next element. */
612 void
613 add_locale_string (struct locale_file *file, const char *string)
615 record_offset (file);
616 obstack_grow (&file->data, string, strlen (string) + 1);
619 /* Likewise for wide strings. */
620 void
621 add_locale_wstring (struct locale_file *file, const uint32_t *string)
623 add_locale_uint32_array (file, string, wcslen ((const wchar_t *) string) + 1);
626 /* Record that FILE's next element is the 32-bit integer VALUE. */
627 void
628 add_locale_uint32 (struct locale_file *file, uint32_t value)
630 align_locale_data (file, LOCFILE_ALIGN);
631 record_offset (file);
632 value = maybe_swap_uint32 (value);
633 obstack_grow (&file->data, &value, sizeof (value));
636 /* Record that FILE's next element is an array of N_ELEMS integers
637 starting at DATA. */
638 void
639 add_locale_uint32_array (struct locale_file *file,
640 const uint32_t *data, size_t n_elems)
642 align_locale_data (file, LOCFILE_ALIGN);
643 record_offset (file);
644 obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
645 maybe_swap_uint32_obstack (&file->data, n_elems);
648 /* Record that FILE's next element is the single byte given by VALUE. */
649 void
650 add_locale_char (struct locale_file *file, char value)
652 record_offset (file);
653 obstack_1grow (&file->data, value);
656 /* Start building an element that contains several different pieces of data.
657 Subsequent calls to add_locale_* will add data to the same element up
658 till the next call to end_locale_structure. The element's alignment
659 is dictated by the first piece of data added to it. */
660 void
661 start_locale_structure (struct locale_file *file)
663 assert (file->structure_stage == 0);
664 file->structure_stage = 1;
667 /* Finish a structure element that was started by start_locale_structure.
668 Empty structures are OK and behave like add_locale_empty. */
669 void
670 end_locale_structure (struct locale_file *file)
672 record_offset (file);
673 assert (file->structure_stage == 2);
674 file->structure_stage = 0;
677 /* Start building data that goes before the next element's recorded offset.
678 Subsequent calls to add_locale_* will add data to the file without
679 treating any of it as the start of a new element. Calling
680 end_locale_prelude switches back to the usual behavior. */
681 void
682 start_locale_prelude (struct locale_file *file)
684 assert (file->structure_stage == 0);
685 file->structure_stage = 3;
688 /* End a block started by start_locale_prelude. */
689 void
690 end_locale_prelude (struct locale_file *file)
692 assert (file->structure_stage == 3);
693 file->structure_stage = 0;
696 /* Write a locale file, with contents given by FILE. */
697 void
698 write_locale_data (const char *output_path, int catidx, const char *category,
699 struct locale_file *file)
701 size_t cnt, step, maxiov;
702 int fd;
703 char *fname;
704 const char **other_paths = NULL;
705 uint32_t header[2];
706 size_t n_elem;
707 struct iovec vec[3];
709 assert (file->n_elements == file->next_element);
710 header[0] = LIMAGIC (catidx);
711 header[1] = file->n_elements;
712 vec[0].iov_len = sizeof (header);
713 vec[0].iov_base = header;
714 vec[1].iov_len = sizeof (uint32_t) * file->n_elements;
715 vec[1].iov_base = file->offsets;
716 vec[2].iov_len = obstack_object_size (&file->data);
717 vec[2].iov_base = obstack_finish (&file->data);
718 maybe_swap_uint32_array (vec[0].iov_base, 2);
719 maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
720 n_elem = 3;
721 if (! no_archive)
723 /* The data will be added to the archive. For now we simply
724 generate the image which will be written. First determine
725 the size. */
726 int cnt;
727 void *endp;
729 to_archive[catidx].size = 0;
730 for (cnt = 0; cnt < n_elem; ++cnt)
731 to_archive[catidx].size += vec[cnt].iov_len;
733 /* Allocate the memory for it. */
734 to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
736 /* Fill it in. */
737 for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
738 endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
740 /* Compute the MD5 sum for the data. */
741 __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
742 to_archive[catidx].sum);
744 return;
747 fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
749 /* Normally we write to the directory pointed to by the OUTPUT_PATH.
750 But for LC_MESSAGES we have to take care for the translation
751 data. This means we need to have a directory LC_MESSAGES in
752 which we place the file under the name SYS_LC_MESSAGES. */
753 sprintf (fname, "%s%s", output_path, category);
754 fd = -2;
755 if (strcmp (category, "LC_MESSAGES") == 0)
757 struct stat64 st;
759 if (stat64 (fname, &st) < 0)
761 if (mkdir (fname, 0777) >= 0)
763 fd = -1;
764 errno = EISDIR;
767 else if (!S_ISREG (st.st_mode))
769 fd = -1;
770 errno = EISDIR;
774 /* Create the locale file with nlinks == 1; this avoids crashing processes
775 which currently use the locale and damaging files belonging to other
776 locales as well. */
777 if (fd == -2)
779 unlink (fname);
780 fd = creat (fname, 0666);
783 if (fd == -1)
785 int save_err = errno;
787 if (errno == EISDIR)
789 sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
790 unlink (fname);
791 fd = creat (fname, 0666);
792 if (fd == -1)
793 save_err = errno;
796 if (fd == -1)
798 record_error (0, save_err, _("\
799 cannot open output file `%s' for category `%s'"), fname, category);
800 free (fname);
801 return;
805 #ifdef UIO_MAXIOV
806 maxiov = UIO_MAXIOV;
807 #else
808 maxiov = sysconf (_SC_UIO_MAXIOV);
809 #endif
811 /* Write the data using writev. But we must take care for the
812 limitation of the implementation. */
813 for (cnt = 0; cnt < n_elem; cnt += step)
815 step = n_elem - cnt;
816 if (maxiov > 0)
817 step = MIN (maxiov, step);
819 if (writev (fd, &vec[cnt], step) < 0)
821 record_error (0, errno, _("\
822 failure while writing data for category `%s'"), category);
823 break;
827 close (fd);
829 /* Compare the file with the locale data files for the same category
830 in other locales, and see if we can reuse it, to save disk space.
831 If the user specified --no-hard-links to localedef then hard_links
832 is false, other_paths remains NULL and we skip the optimization
833 below. The use of --no-hard-links is distribution specific since
834 some distros have post-processing hard-link steps and so doing this
835 here is a waste of time. Worse than a waste of time in rpm-based
836 distributions it can result in build determinism issues from
837 build-to-build since some files may get a hard link in one pass but
838 not in another (if the files happened to be created in parallel). */
839 if (hard_links)
840 other_paths = siblings (output_path);
842 /* If there are other paths, then walk the sibling paths looking for
843 files with the same content so we can hard link and reduce disk
844 space usage. */
845 if (other_paths != NULL)
847 struct stat64 fname_stat;
849 if (lstat64 (fname, &fname_stat) >= 0
850 && S_ISREG (fname_stat.st_mode))
852 const char *fname_tail = fname + strlen (output_path);
853 const char **other_p;
854 int seen_count;
855 ino_t *seen_inodes;
857 seen_count = 0;
858 for (other_p = other_paths; *other_p; other_p++)
859 seen_count++;
860 seen_inodes = (ino_t *) xmalloc (seen_count * sizeof (ino_t));
861 seen_count = 0;
863 for (other_p = other_paths; *other_p; other_p++)
865 const char *other_path = *other_p;
866 size_t other_path_len = strlen (other_path);
867 char *other_fname;
868 struct stat64 other_fname_stat;
870 other_fname =
871 (char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
872 memcpy (other_fname, other_path, other_path_len);
873 strcpy (other_fname + other_path_len, fname_tail);
875 if (lstat64 (other_fname, &other_fname_stat) >= 0
876 && S_ISREG (other_fname_stat.st_mode)
877 /* Consider only files on the same device.
878 Otherwise hard linking won't work anyway. */
879 && other_fname_stat.st_dev == fname_stat.st_dev
880 /* Consider only files with the same permissions.
881 Otherwise there are security risks. */
882 && other_fname_stat.st_uid == fname_stat.st_uid
883 && other_fname_stat.st_gid == fname_stat.st_gid
884 && other_fname_stat.st_mode == fname_stat.st_mode
885 /* Don't compare fname with itself. */
886 && other_fname_stat.st_ino != fname_stat.st_ino
887 /* Files must have the same size, otherwise they
888 cannot be the same. */
889 && other_fname_stat.st_size == fname_stat.st_size)
891 /* Skip this file if we have already read it (under a
892 different name). */
893 int i;
895 for (i = seen_count - 1; i >= 0; i--)
896 if (seen_inodes[i] == other_fname_stat.st_ino)
897 break;
898 if (i < 0)
900 /* Now compare fname and other_fname for real. */
901 blksize_t blocksize;
903 #ifdef _STATBUF_ST_BLKSIZE
904 blocksize = MAX (fname_stat.st_blksize,
905 other_fname_stat.st_blksize);
906 if (blocksize > 8 * 1024)
907 blocksize = 8 * 1024;
908 #else
909 blocksize = 8 * 1024;
910 #endif
912 if (compare_files (fname, other_fname,
913 fname_stat.st_size, blocksize) == 0)
915 /* Found! other_fname is identical to fname. */
916 /* Link other_fname to fname. But use a temporary
917 file, in case hard links don't work on the
918 particular filesystem. */
919 char * tmp_fname =
920 (char *) xmalloc (strlen (fname) + 4 + 1);
922 strcpy (stpcpy (tmp_fname, fname), ".tmp");
924 if (link (other_fname, tmp_fname) >= 0)
926 unlink (fname);
927 if (rename (tmp_fname, fname) < 0)
929 record_error (0, errno, _("\
930 cannot create output file `%s' for category `%s'"), fname, category);
932 free (tmp_fname);
933 free (other_fname);
934 break;
936 free (tmp_fname);
939 /* Don't compare with this file a second time. */
940 seen_inodes[seen_count++] = other_fname_stat.st_ino;
943 free (other_fname);
945 free (seen_inodes);
949 free (fname);
953 /* General handling of `copy'. */
954 void
955 handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
956 const char *repertoire_name, struct localedef_t *result,
957 enum token_t token, int locale, const char *locale_name,
958 int ignore_content)
960 struct token *now;
961 int warned = 0;
963 now = lr_token (ldfile, charmap, result, NULL, verbose);
964 if (now->tok != tok_string)
965 lr_error (ldfile, _("expecting string argument for `copy'"));
966 else if (!ignore_content)
968 if (now->val.str.startmb == NULL)
969 lr_error (ldfile, _("\
970 locale name should consist only of portable characters"));
971 else
973 (void) add_to_readlist (locale, now->val.str.startmb,
974 repertoire_name, 1, NULL);
975 result->copy_name[locale] = now->val.str.startmb;
979 lr_ignore_rest (ldfile, now->tok == tok_string);
981 /* The rest of the line must be empty and the next keyword must be
982 `END xxx'. */
983 while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
984 != tok_end && now->tok != tok_eof)
986 if (warned == 0)
988 lr_error (ldfile, _("\
989 no other keyword shall be specified when `copy' is used"));
990 warned = 1;
993 lr_ignore_rest (ldfile, 0);
996 if (now->tok != tok_eof)
998 /* Handle `END xxx'. */
999 now = lr_token (ldfile, charmap, result, NULL, verbose);
1001 if (now->tok != token)
1002 lr_error (ldfile, _("\
1003 `%1$s' definition does not end with `END %1$s'"), locale_name);
1005 lr_ignore_rest (ldfile, now->tok == token);
1007 else
1008 /* When we come here we reached the end of the file. */
1009 lr_error (ldfile, _("%s: premature end of file"), locale_name);