1/* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
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.
8
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.
13
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/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20
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>
32
33#include "../../crypt/md5.h"
34#include "localedef.h"
35#include "localeinfo.h"
36#include "locfile.h"
37#include "simple-hash.h"
38
39#include "locfile-kw.h"
40
41#define obstack_chunk_alloc xmalloc
42#define obstack_chunk_free free
43
44/* Temporary storage of the locale data before writing it to the archive. */
45static locale_data_t to_archive;
46
47
48int
49locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
50{
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;
56
57 /* If no repertoire name was specified use the global one. */
58 if (repertoire_name == NULL)
59 repertoire_name = repertoire_global;
60
61 /* Open the locale definition file. */
62 ldfile = lr_open (fname: filename, hf: locfile_hash);
63 if (ldfile == NULL)
64 {
65 if (filename != NULL && filename[0] != '/')
66 {
67 char *i18npath = getenv (name: "I18NPATH");
68 if (i18npath != NULL && *i18npath != '\0')
69 {
70 const size_t pathlen = strlen (s: i18npath);
71 char i18npathbuf[pathlen + 1];
72 char path[strlen (s: filename) + 1 + pathlen
73 + sizeof ("/locales/") - 1];
74 char *next;
75 i18npath = memcpy (dest: i18npathbuf, src: i18npath, n: pathlen + 1);
76
77 while (ldfile == NULL
78 && (next = strsep (stringp: &i18npath, delim: ":")) != NULL)
79 {
80 stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
81
82 ldfile = lr_open (fname: path, hf: locfile_hash);
83
84 if (ldfile == NULL)
85 {
86 stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
87
88 ldfile = lr_open (fname: path, hf: locfile_hash);
89 }
90 }
91 }
92
93 /* Test in the default directory. */
94 if (ldfile == NULL)
95 {
96 char path[strlen (s: filename) + 1 + sizeof (LOCSRCDIR)];
97
98 stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
99 ldfile = lr_open (fname: path, hf: locfile_hash);
100 }
101 }
102
103 if (ldfile == NULL)
104 return 1;
105 }
106
107 /* Parse locale definition file and store result in RESULT. */
108 while (1)
109 {
110 struct token *now = lr_token (lr: ldfile, charmap, NULL, NULL, verbose);
111 enum token_t nowtok = now->tok;
112 struct token *arg;
113
114 if (nowtok == tok_eof)
115 break;
116
117 if (nowtok == tok_eol)
118 /* Ignore empty lines. */
119 continue;
120
121 switch (nowtok)
122 {
123 case tok_escape_char:
124 case tok_comment_char:
125 /* We need an argument. */
126 arg = lr_token (lr: ldfile, charmap, NULL, NULL, verbose);
127
128 if (arg->tok != tok_ident)
129 {
130 SYNTAX_ERROR (_("bad argument"));
131 continue;
132 }
133
134 if (arg->val.str.lenmb != 1)
135 {
136 lr_error (lr: ldfile, _("\
137argument to `%s' must be a single character"),
138 nowtok == tok_escape_char
139 ? "escape_char" : "comment_char");
140
141 lr_ignore_rest (lr: ldfile, verbose: 0);
142 continue;
143 }
144
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;
150
151 case tok_repertoiremap:
152 /* We need an argument. */
153 arg = lr_token (lr: ldfile, charmap, NULL, NULL, verbose);
154
155 if (arg->tok != tok_ident)
156 {
157 SYNTAX_ERROR (_("bad argument"));
158 continue;
159 }
160
161 if (repertoire_name == NULL)
162 {
163 char *newp = alloca (arg->val.str.lenmb + 1);
164
165 *((char *) mempcpy (newp, arg->val.str.startmb,
166 arg->val.str.lenmb)) = '\0';
167 repertoire_name = newp;
168 }
169 break;
170
171 case tok_lc_ctype:
172 ctype_read (ldfile, result, charmap, repertoire_name,
173 ignore_content: (locale_mask & CTYPE_LOCALE) == 0);
174 result->avail |= locale_mask & CTYPE_LOCALE;
175 not_here ^= CTYPE_LOCALE;
176 continue;
177
178 case tok_lc_collate:
179 collate_read (ldfile, result, charmap, repertoire_name,
180 ignore_content: (locale_mask & COLLATE_LOCALE) == 0);
181 result->avail |= locale_mask & COLLATE_LOCALE;
182 not_here ^= COLLATE_LOCALE;
183 continue;
184
185 case tok_lc_monetary:
186 monetary_read (ldfile, result, charmap, repertoire_name,
187 ignore_content: (locale_mask & MONETARY_LOCALE) == 0);
188 result->avail |= locale_mask & MONETARY_LOCALE;
189 not_here ^= MONETARY_LOCALE;
190 continue;
191
192 case tok_lc_numeric:
193 numeric_read (ldfile, result, charmap, repertoire_name,
194 ignore_content: (locale_mask & NUMERIC_LOCALE) == 0);
195 result->avail |= locale_mask & NUMERIC_LOCALE;
196 not_here ^= NUMERIC_LOCALE;
197 continue;
198
199 case tok_lc_time:
200 time_read (ldfile, result, charmap, repertoire_name,
201 ignore_content: (locale_mask & TIME_LOCALE) == 0);
202 result->avail |= locale_mask & TIME_LOCALE;
203 not_here ^= TIME_LOCALE;
204 continue;
205
206 case tok_lc_messages:
207 messages_read (ldfile, result, charmap, repertoire_name,
208 ignore_content: (locale_mask & MESSAGES_LOCALE) == 0);
209 result->avail |= locale_mask & MESSAGES_LOCALE;
210 not_here ^= MESSAGES_LOCALE;
211 continue;
212
213 case tok_lc_paper:
214 paper_read (ldfile, result, charmap, repertoire_name,
215 ignore_content: (locale_mask & PAPER_LOCALE) == 0);
216 result->avail |= locale_mask & PAPER_LOCALE;
217 not_here ^= PAPER_LOCALE;
218 continue;
219
220 case tok_lc_name:
221 name_read (ldfile, result, charmap, repertoire_name,
222 ignore_content: (locale_mask & NAME_LOCALE) == 0);
223 result->avail |= locale_mask & NAME_LOCALE;
224 not_here ^= NAME_LOCALE;
225 continue;
226
227 case tok_lc_address:
228 address_read (ldfile, result, charmap, repertoire_name,
229 ignore_content: (locale_mask & ADDRESS_LOCALE) == 0);
230 result->avail |= locale_mask & ADDRESS_LOCALE;
231 not_here ^= ADDRESS_LOCALE;
232 continue;
233
234 case tok_lc_telephone:
235 telephone_read (ldfile, result, charmap, repertoire_name,
236 ignore_content: (locale_mask & TELEPHONE_LOCALE) == 0);
237 result->avail |= locale_mask & TELEPHONE_LOCALE;
238 not_here ^= TELEPHONE_LOCALE;
239 continue;
240
241 case tok_lc_measurement:
242 measurement_read (ldfile, result, charmap, repertoire_name,
243 ignore_content: (locale_mask & MEASUREMENT_LOCALE) == 0);
244 result->avail |= locale_mask & MEASUREMENT_LOCALE;
245 not_here ^= MEASUREMENT_LOCALE;
246 continue;
247
248 case tok_lc_identification:
249 identification_read (ldfile, result, charmap, repertoire_name,
250 ignore_content: (locale_mask & IDENTIFICATION_LOCALE) == 0);
251 result->avail |= locale_mask & IDENTIFICATION_LOCALE;
252 not_here ^= IDENTIFICATION_LOCALE;
253 continue;
254
255 default:
256 SYNTAX_ERROR (_("\
257syntax error: not inside a locale definition section"));
258 continue;
259 }
260
261 /* The rest of the line must be empty. */
262 lr_ignore_rest (lr: ldfile, verbose: 1);
263 }
264
265 /* We read all of the file. */
266 lr_close (lr: ldfile);
267
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;
271
272 return 0;
273}
274
275
276/* Semantic checking of locale specifications. */
277
278static void (*const check_funcs[]) (struct localedef_t *,
279 const struct charmap_t *) =
280{
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
293};
294
295void
296check_all_categories (struct localedef_t *definitions,
297 const struct charmap_t *charmap)
298{
299 int cnt;
300
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);
304}
305
306
307/* Writing the locale data files. All files use the same output_path. */
308
309static void (*const write_funcs[]) (struct localedef_t *,
310 const struct charmap_t *, const char *) =
311{
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
324};
325
326
327void
328write_all_categories (struct localedef_t *definitions,
329 const struct charmap_t *charmap, const char *locname,
330 const char *output_path)
331{
332 int cnt;
333
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);
337
338 if (! no_archive)
339 {
340 /* The data has to be added to the archive. Do this now. */
341 struct locarhandle ah;
342
343 /* Open the archive. This call never returns if we cannot
344 successfully open the archive. */
345 ah.fname = NULL;
346 open_archive (ah: &ah, false);
347
348 if (add_locale_to_archive (ah: &ah, name: locname, data: to_archive, true) != 0)
349 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
350
351 /* We are done. */
352 close_archive (ah: &ah);
353 }
354}
355
356
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. */
359static const char **
360siblings_uncached (const char *output_path)
361{
362 size_t len;
363 char *base, *p;
364 struct stat64 output_stat;
365 DIR *dirp;
366 int nelems;
367 const char **elems;
368
369 /* Remove trailing slashes and trailing pathname component. */
370 len = strlen (s: output_path);
371 base = (char *) alloca (len);
372 memcpy (dest: base, src: output_path, n: len);
373 p = base + len;
374 while (p > base && p[-1] == '/')
375 p--;
376 if (p == base)
377 return NULL;
378 do
379 p--;
380 while (p > base && p[-1] != '/');
381 if (p == base)
382 return NULL;
383 *--p = '\0';
384 len = p - base;
385
386 /* Get the properties of output_path. */
387 if (lstat64 (file: output_path, buf: &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
388 return NULL;
389
390 /* Iterate through the directories in base directory. */
391 dirp = opendir (name: base);
392 if (dirp == NULL)
393 return NULL;
394 nelems = 0;
395 elems = NULL;
396 for (;;)
397 {
398 struct dirent64 *other_dentry;
399 const char *other_name;
400 char *other_path;
401 struct stat64 other_stat;
402
403 other_dentry = readdir64 (dirp: dirp);
404 if (other_dentry == NULL)
405 break;
406
407 other_name = other_dentry->d_name;
408 if (strcmp (s1: other_name, s2: ".") == 0 || strcmp (s1: other_name, s2: "..") == 0)
409 continue;
410
411 other_path = (char *) xmalloc (n: len + 1 + strlen (s: other_name) + 2);
412 memcpy (dest: other_path, src: base, n: len);
413 other_path[len] = '/';
414 strcpy (dest: other_path + len + 1, src: other_name);
415
416 if (lstat64 (file: other_path, buf: &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)
422 {
423 /* Found a subdirectory. Add a trailing slash and store it. */
424 p = other_path + len + 1 + strlen (s: other_name);
425 *p++ = '/';
426 *p = '\0';
427 elems = (const char **) xrealloc (o: (char *) elems,
428 n: (nelems + 2) * sizeof (char **));
429 elems[nelems++] = other_path;
430 }
431 else
432 free (ptr: other_path);
433 }
434 closedir (dirp: dirp);
435
436 if (elems != NULL)
437 elems[nelems] = NULL;
438 return elems;
439}
440
441
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. */
445static const char **
446siblings (const char *output_path)
447{
448 static const char *last_output_path;
449 static const char **last_result;
450
451 if (output_path != last_output_path)
452 {
453 if (last_result != NULL)
454 {
455 const char **p;
456
457 for (p = last_result; *p != NULL; p++)
458 free (ptr: (char *) *p);
459 free (ptr: last_result);
460 }
461
462 last_output_path = output_path;
463 last_result = siblings_uncached (output_path);
464 }
465 return last_result;
466}
467
468
469/* Read as many bytes from a file descriptor as possible. */
470static ssize_t
471full_read (int fd, void *bufarea, size_t nbyte)
472{
473 char *buf = (char *) bufarea;
474
475 while (nbyte > 0)
476 {
477 ssize_t retval = read (fd: fd, buf: buf, nbytes: nbyte);
478
479 if (retval == 0)
480 break;
481 else if (retval > 0)
482 {
483 buf += retval;
484 nbyte -= retval;
485 }
486 else if (errno != EINTR)
487 return retval;
488 }
489 return buf - (char *) bufarea;
490}
491
492
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. */
495static int
496compare_files (const char *filename1, const char *filename2, size_t size,
497 size_t blocksize)
498{
499 int fd1, fd2;
500 int ret = -1;
501
502 fd1 = open (file: filename1, O_RDONLY);
503 if (fd1 >= 0)
504 {
505 fd2 = open (file: filename2, O_RDONLY);
506 if (fd2 >= 0)
507 {
508 char *buf1 = (char *) xmalloc (n: 2 * blocksize);
509 char *buf2 = buf1 + blocksize;
510
511 ret = 0;
512 while (size > 0)
513 {
514 size_t bytes = (size < blocksize ? size : blocksize);
515
516 if (full_read (fd: fd1, bufarea: buf1, nbyte: bytes) < (ssize_t) bytes)
517 {
518 ret = -1;
519 break;
520 }
521 if (full_read (fd: fd2, bufarea: buf2, nbyte: bytes) < (ssize_t) bytes)
522 {
523 ret = -1;
524 break;
525 }
526 if (memcmp (s1: buf1, s2: buf2, n: bytes) != 0)
527 {
528 ret = 1;
529 break;
530 }
531 size -= bytes;
532 }
533
534 free (ptr: buf1);
535 close (fd: fd2);
536 }
537 close (fd: fd1);
538 }
539 return ret;
540}
541
542/* True if the locale files use the opposite endianness to the
543 machine running localedef. */
544bool swap_endianness_p;
545
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. */
551static void
552record_offset (struct locale_file *file)
553{
554 if (file->structure_stage < 2)
555 {
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;
562 }
563}
564
565/* Initialize FILE for a new output file. N_ELEMENTS is the number
566 of elements in the file. */
567void
568init_locale_data (struct locale_file *file, size_t n_elements)
569{
570 file->n_elements = n_elements;
571 file->next_element = 0;
572 file->offsets = xmalloc (n: sizeof (uint32_t) * n_elements);
573 obstack_init (&file->data);
574 file->structure_stage = 0;
575}
576
577/* Align the size of FILE's obstack object to BOUNDARY bytes. */
578void
579align_locale_data (struct locale_file *file, size_t boundary)
580{
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, c: 0, n: size);
584}
585
586/* Record that FILE's next element contains no data. */
587void
588add_locale_empty (struct locale_file *file)
589{
590 record_offset (file);
591}
592
593/* Record that FILE's next element consists of SIZE bytes starting at DATA. */
594void
595add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
596{
597 record_offset (file);
598 obstack_grow (&file->data, data, size);
599}
600
601/* Finish the current object on OBSTACK and use it as the data for FILE's
602 next element. */
603void
604add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
605{
606 size_t size = obstack_object_size (obstack);
607 record_offset (file);
608 obstack_grow (&file->data, obstack_finish (obstack), size);
609}
610
611/* Use STRING as FILE's next element. */
612void
613add_locale_string (struct locale_file *file, const char *string)
614{
615 record_offset (file);
616 obstack_grow (&file->data, string, strlen (string) + 1);
617}
618
619/* Likewise for wide strings. */
620void
621add_locale_wstring (struct locale_file *file, const uint32_t *string)
622{
623 add_locale_uint32_array (file, data: string, n_elems: wcslen (s: (const wchar_t *) string) + 1);
624}
625
626/* Record that FILE's next element is the 32-bit integer VALUE. */
627void
628add_locale_uint32 (struct locale_file *file, uint32_t value)
629{
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));
634}
635
636/* Record that FILE's next element is an array of N_ELEMS integers
637 starting at DATA. */
638void
639add_locale_uint32_array (struct locale_file *file,
640 const uint32_t *data, size_t n_elems)
641{
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 (obstack: &file->data, n: n_elems);
646}
647
648/* Record that FILE's next element is the single byte given by VALUE. */
649void
650add_locale_char (struct locale_file *file, char value)
651{
652 record_offset (file);
653 obstack_1grow (&file->data, value);
654}
655
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. */
660void
661start_locale_structure (struct locale_file *file)
662{
663 assert (file->structure_stage == 0);
664 file->structure_stage = 1;
665}
666
667/* Finish a structure element that was started by start_locale_structure.
668 Empty structures are OK and behave like add_locale_empty. */
669void
670end_locale_structure (struct locale_file *file)
671{
672 record_offset (file);
673 assert (file->structure_stage == 2);
674 file->structure_stage = 0;
675}
676
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. */
681void
682start_locale_prelude (struct locale_file *file)
683{
684 assert (file->structure_stage == 0);
685 file->structure_stage = 3;
686}
687
688/* End a block started by start_locale_prelude. */
689void
690end_locale_prelude (struct locale_file *file)
691{
692 assert (file->structure_stage == 3);
693 file->structure_stage = 0;
694}
695
696/* Write a locale file, with contents given by FILE. */
697void
698write_locale_data (const char *output_path, int catidx, const char *category,
699 struct locale_file *file)
700{
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];
708
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 (array: vec[0].iov_base, n: 2);
719 maybe_swap_uint32_array (array: vec[1].iov_base, n: file->n_elements);
720 n_elem = 3;
721 if (! no_archive)
722 {
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;
728
729 to_archive[catidx].size = 0;
730 for (cnt = 0; cnt < n_elem; ++cnt)
731 to_archive[catidx].size += vec[cnt].iov_len;
732
733 /* Allocate the memory for it. */
734 to_archive[catidx].addr = xmalloc (n: to_archive[catidx].size);
735
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);
739
740 /* Compute the MD5 sum for the data. */
741 __md5_buffer (buffer: to_archive[catidx].addr, len: to_archive[catidx].size,
742 resblock: to_archive[catidx].sum);
743
744 return;
745 }
746
747 fname = xmalloc (n: strlen (s: output_path) + 2 * strlen (s: category) + 7);
748
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 (s: fname, format: "%s%s", output_path, category);
754 fd = -2;
755 if (strcmp (s1: category, s2: "LC_MESSAGES") == 0)
756 {
757 struct stat64 st;
758
759 if (stat64 (file: fname, buf: &st) < 0)
760 {
761 if (mkdir (path: fname, mode: 0777) >= 0)
762 {
763 fd = -1;
764 errno = EISDIR;
765 }
766 }
767 else if (!S_ISREG (st.st_mode))
768 {
769 fd = -1;
770 errno = EISDIR;
771 }
772 }
773
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)
778 {
779 unlink (name: fname);
780 fd = creat (file: fname, mode: 0666);
781 }
782
783 if (fd == -1)
784 {
785 int save_err = errno;
786
787 if (errno == EISDIR)
788 {
789 sprintf (s: fname, format: "%1$s%2$s/SYS_%2$s", output_path, category);
790 unlink (name: fname);
791 fd = creat (file: fname, mode: 0666);
792 if (fd == -1)
793 save_err = errno;
794 }
795
796 if (fd == -1)
797 {
798 record_error (status: 0, errnum: save_err, _("\
799cannot open output file `%s' for category `%s'"), fname, category);
800 free (ptr: fname);
801 return;
802 }
803 }
804
805#ifdef UIO_MAXIOV
806 maxiov = UIO_MAXIOV;
807#else
808 maxiov = sysconf (_SC_UIO_MAXIOV);
809#endif
810
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)
814 {
815 step = n_elem - cnt;
816 if (maxiov > 0)
817 step = MIN (maxiov, step);
818
819 if (writev (fd: fd, iovec: &vec[cnt], count: step) < 0)
820 {
821 record_error (status: 0, errno, _("\
822failure while writing data for category `%s'"), category);
823 break;
824 }
825 }
826
827 close (fd: fd);
828
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);
841
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)
846 {
847 struct stat64 fname_stat;
848
849 if (lstat64 (file: fname, buf: &fname_stat) >= 0
850 && S_ISREG (fname_stat.st_mode))
851 {
852 const char *fname_tail = fname + strlen (s: output_path);
853 const char **other_p;
854 int seen_count;
855 ino_t *seen_inodes;
856
857 seen_count = 0;
858 for (other_p = other_paths; *other_p; other_p++)
859 seen_count++;
860 seen_inodes = (ino_t *) xmalloc (n: seen_count * sizeof (ino_t));
861 seen_count = 0;
862
863 for (other_p = other_paths; *other_p; other_p++)
864 {
865 const char *other_path = *other_p;
866 size_t other_path_len = strlen (s: other_path);
867 char *other_fname;
868 struct stat64 other_fname_stat;
869
870 other_fname =
871 (char *) xmalloc (n: other_path_len + strlen (s: fname_tail) + 1);
872 memcpy (dest: other_fname, src: other_path, n: other_path_len);
873 strcpy (dest: other_fname + other_path_len, src: fname_tail);
874
875 if (lstat64 (file: other_fname, buf: &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)
890 {
891 /* Skip this file if we have already read it (under a
892 different name). */
893 int i;
894
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)
899 {
900 /* Now compare fname and other_fname for real. */
901 blksize_t blocksize;
902
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
911
912 if (compare_files (filename1: fname, filename2: other_fname,
913 size: fname_stat.st_size, blocksize) == 0)
914 {
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 (n: strlen (s: fname) + 4 + 1);
921
922 strcpy (dest: stpcpy (tmp_fname, fname), src: ".tmp");
923
924 if (link (from: other_fname, to: tmp_fname) >= 0)
925 {
926 unlink (name: fname);
927 if (rename (old: tmp_fname, new: fname) < 0)
928 {
929 record_error (status: 0, errno, _("\
930cannot create output file `%s' for category `%s'"), fname, category);
931 }
932 free (ptr: tmp_fname);
933 free (ptr: other_fname);
934 break;
935 }
936 free (ptr: tmp_fname);
937 }
938
939 /* Don't compare with this file a second time. */
940 seen_inodes[seen_count++] = other_fname_stat.st_ino;
941 }
942 }
943 free (ptr: other_fname);
944 }
945 free (ptr: seen_inodes);
946 }
947 }
948
949 free (ptr: fname);
950}
951
952
953/* General handling of `copy'. */
954void
955handle_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)
959{
960 struct token *now;
961 int warned = 0;
962
963 now = lr_token (lr: ldfile, charmap, locale: result, NULL, verbose);
964 if (now->tok != tok_string)
965 lr_error (lr: ldfile, _("expecting string argument for `copy'"));
966 else if (!ignore_content)
967 {
968 if (now->val.str.startmb == NULL)
969 lr_error (lr: ldfile, _("\
970locale name should consist only of portable characters"));
971 else
972 {
973 (void) add_to_readlist (locale, name: now->val.str.startmb,
974 repertoire_name, generate: 1, NULL);
975 result->copy_name[locale] = now->val.str.startmb;
976 }
977 }
978
979 lr_ignore_rest (lr: ldfile, verbose: now->tok == tok_string);
980
981 /* The rest of the line must be empty and the next keyword must be
982 `END xxx'. */
983 while ((now = lr_token (lr: ldfile, charmap, locale: result, NULL, verbose))->tok
984 != tok_end && now->tok != tok_eof)
985 {
986 if (warned == 0)
987 {
988 lr_error (lr: ldfile, _("\
989no other keyword shall be specified when `copy' is used"));
990 warned = 1;
991 }
992
993 lr_ignore_rest (lr: ldfile, verbose: 0);
994 }
995
996 if (now->tok != tok_eof)
997 {
998 /* Handle `END xxx'. */
999 now = lr_token (lr: ldfile, charmap, locale: result, NULL, verbose);
1000
1001 if (now->tok != token)
1002 lr_error (lr: ldfile, _("\
1003`%1$s' definition does not end with `END %1$s'"), locale_name);
1004
1005 lr_ignore_rest (lr: ldfile, verbose: now->tok == token);
1006 }
1007 else
1008 /* When we come here we reached the end of the file. */
1009 lr_error (lr: ldfile, _("%s: premature end of file"), locale_name);
1010}
1011

source code of glibc/locale/programs/locfile.c