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