]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locfile.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / locale / programs / locfile.c
CommitLineData
04277e02 1/* Copyright (C) 1996-2019 Free Software Foundation, Inc.
6d52618b 2 This file is part of the GNU C Library.
e5681dee 3 Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
19bc17a9 4
43bc8ac6 5 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
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.
19bc17a9 9
43bc8ac6 10 This program is distributed in the hope that it will be useful,
6d52618b 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
19bc17a9 14
43bc8ac6 15 You should have received a copy of the GNU General Public License
5a82c748 16 along with this program; if not, see <https://www.gnu.org/licenses/>. */
19bc17a9
RM
17
18#ifdef HAVE_CONFIG_H
19# include <config.h>
20#endif
21
aac0e8c4 22#include <dirent.h>
19bc17a9
RM
23#include <errno.h>
24#include <fcntl.h>
6055173a 25#include <stdbool.h>
f166d865 26#include <stdlib.h>
aac0e8c4 27#include <string.h>
19bc17a9 28#include <unistd.h>
4b10dd6c 29#include <sys/param.h>
19bc17a9 30#include <sys/stat.h>
1ecbb381
RS
31#include <assert.h>
32#include <wchar.h>
19bc17a9 33
a7b65cdc 34#include "../../crypt/md5.h"
4b10dd6c 35#include "localedef.h"
1ecbb381 36#include "localeinfo.h"
19bc17a9 37#include "locfile.h"
a7b65cdc 38#include "simple-hash.h"
19bc17a9 39
19bc17a9
RM
40#include "locfile-kw.h"
41
1ecbb381
RS
42#define obstack_chunk_alloc xmalloc
43#define obstack_chunk_free free
19bc17a9 44
a7b65cdc
UD
45/* Temporary storage of the locale data before writing it to the archive. */
46static locale_data_t to_archive;
47
48
4b10dd6c 49int
47e8b443 50locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
19bc17a9 51{
4b10dd6c
UD
52 const char *filename = result->name;
53 const char *repertoire_name = result->repertoire_name;
69f6a804 54 int locale_mask = result->needed & ~result->avail;
19bc17a9 55 struct linereader *ldfile;
b9eb05d6 56 int not_here = ALL_LOCALES;
19bc17a9 57
4b10dd6c
UD
58 /* If no repertoire name was specified use the global one. */
59 if (repertoire_name == NULL)
60 repertoire_name = repertoire_global;
19bc17a9 61
4b10dd6c 62 /* Open the locale definition file. */
19bc17a9
RM
63 ldfile = lr_open (filename, locfile_hash);
64 if (ldfile == NULL)
65 {
dcf56f42 66 if (filename != NULL && filename[0] != '/')
19bc17a9 67 {
4b10dd6c 68 char *i18npath = getenv ("I18NPATH");
f166d865
UD
69 if (i18npath != NULL && *i18npath != '\0')
70 {
db2f05ba
RM
71 const size_t pathlen = strlen (i18npath);
72 char i18npathbuf[pathlen + 1];
73 char path[strlen (filename) + 1 + pathlen
74 + sizeof ("/locales/") - 1];
f166d865 75 char *next;
db2f05ba 76 i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
f166d865
UD
77
78 while (ldfile == NULL
79 && (next = strsep (&i18npath, ":")) != NULL)
80 {
12a9fabe 81 stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
f166d865
UD
82
83 ldfile = lr_open (path, locfile_hash);
4b10dd6c
UD
84
85 if (ldfile == NULL)
86 {
b60ea6ff 87 stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
4b10dd6c
UD
88
89 ldfile = lr_open (path, locfile_hash);
90 }
f166d865
UD
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 }
19bc17a9
RM
102 }
103
104 if (ldfile == NULL)
4b10dd6c 105 return 1;
19bc17a9
RM
106 }
107
4b10dd6c 108 /* Parse locale definition file and store result in RESULT. */
19bc17a9
RM
109 while (1)
110 {
47e8b443 111 struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
19bc17a9
RM
112 enum token_t nowtok = now->tok;
113 struct token *arg;
114
115 if (nowtok == tok_eof)
116 break;
117
4b10dd6c
UD
118 if (nowtok == tok_eol)
119 /* Ignore empty lines. */
120 continue;
19bc17a9 121
4b10dd6c
UD
122 switch (nowtok)
123 {
124 case tok_escape_char:
125 case tok_comment_char:
126 /* We need an argument. */
47e8b443 127 arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
19bc17a9 128
4b10dd6c 129 if (arg->tok != tok_ident)
19bc17a9 130 {
4b10dd6c 131 SYNTAX_ERROR (_("bad argument"));
19bc17a9
RM
132 continue;
133 }
134
4b10dd6c 135 if (arg->val.str.lenmb != 1)
19bc17a9 136 {
4b10dd6c
UD
137 lr_error (ldfile, _("\
138argument to `%s' must be a single character"),
139 nowtok == tok_escape_char
140 ? "escape_char" : "comment_char");
19bc17a9 141
4b10dd6c 142 lr_ignore_rest (ldfile, 0);
19bc17a9
RM
143 continue;
144 }
145
4b10dd6c
UD
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;
19bc17a9 151
4b10dd6c
UD
152 case tok_repertoiremap:
153 /* We need an argument. */
47e8b443 154 arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
19bc17a9 155
4b10dd6c 156 if (arg->tok != tok_ident)
19bc17a9 157 {
4b10dd6c 158 SYNTAX_ERROR (_("bad argument"));
19bc17a9
RM
159 continue;
160 }
161
4b10dd6c 162 if (repertoire_name == NULL)
19bc17a9 163 {
51e59260
UD
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;
19bc17a9 169 }
4b10dd6c 170 break;
19bc17a9 171
4b10dd6c
UD
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;
b9eb05d6 176 not_here ^= CTYPE_LOCALE;
19bc17a9
RM
177 continue;
178
4b10dd6c
UD
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;
b9eb05d6 183 not_here ^= COLLATE_LOCALE;
19bc17a9
RM
184 continue;
185
4b10dd6c
UD
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;
b9eb05d6 190 not_here ^= MONETARY_LOCALE;
19bc17a9
RM
191 continue;
192
4b10dd6c
UD
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;
b9eb05d6 197 not_here ^= NUMERIC_LOCALE;
19bc17a9
RM
198 continue;
199
4b10dd6c
UD
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;
b9eb05d6 204 not_here ^= TIME_LOCALE;
19bc17a9
RM
205 continue;
206
4b10dd6c
UD
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;
b9eb05d6 211 not_here ^= MESSAGES_LOCALE;
19bc17a9
RM
212 continue;
213
4b10dd6c
UD
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;
b9eb05d6 218 not_here ^= PAPER_LOCALE;
19bc17a9
RM
219 continue;
220
4b10dd6c
UD
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;
b9eb05d6 225 not_here ^= NAME_LOCALE;
19bc17a9
RM
226 continue;
227
4b10dd6c
UD
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;
b9eb05d6 232 not_here ^= ADDRESS_LOCALE;
19bc17a9
RM
233 continue;
234
4b10dd6c
UD
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;
b9eb05d6 239 not_here ^= TELEPHONE_LOCALE;
19bc17a9
RM
240 continue;
241
4b10dd6c
UD
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;
b9eb05d6 246 not_here ^= MEASUREMENT_LOCALE;
19bc17a9
RM
247 continue;
248
4b10dd6c
UD
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;
b9eb05d6 253 not_here ^= IDENTIFICATION_LOCALE;
19bc17a9
RM
254 continue;
255
256 default:
4b10dd6c
UD
257 SYNTAX_ERROR (_("\
258syntax error: not inside a locale definition section"));
259 continue;
19bc17a9
RM
260 }
261
4b10dd6c
UD
262 /* The rest of the line must be empty. */
263 lr_ignore_rest (ldfile, 1);
19bc17a9
RM
264 }
265
266 /* We read all of the file. */
267 lr_close (ldfile);
268
b9eb05d6
UD
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
4b10dd6c 273 return 0;
19bc17a9
RM
274}
275
276
aac0e8c4
UD
277/* Semantic checking of locale specifications. */
278
4b10dd6c 279static void (*const check_funcs[]) (struct localedef_t *,
47e8b443 280 const struct charmap_t *) =
4b10dd6c
UD
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
19bc17a9 296void
4b10dd6c 297check_all_categories (struct localedef_t *definitions,
47e8b443 298 const struct charmap_t *charmap)
19bc17a9 299{
4b10dd6c
UD
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);
19bc17a9
RM
305}
306
307
aac0e8c4
UD
308/* Writing the locale data files. All files use the same output_path. */
309
47e8b443
UD
310static void (*const write_funcs[]) (struct localedef_t *,
311 const struct charmap_t *, const char *) =
4b10dd6c
UD
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
a7b65cdc 327
19bc17a9 328void
4b10dd6c 329write_all_categories (struct localedef_t *definitions,
a7b65cdc 330 const struct charmap_t *charmap, const char *locname,
75cd5204 331 const char *output_path)
19bc17a9 332{
4b10dd6c
UD
333 int cnt;
334
335 for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
b9eb05d6 336 if (write_funcs[cnt] != NULL)
4b10dd6c 337 write_funcs[cnt] (definitions, charmap, output_path);
a7b65cdc
UD
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. */
484c12fb 346 ah.fname = NULL;
b2bffca2 347 open_archive (&ah, false);
a7b65cdc
UD
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 }
19bc17a9
RM
355}
356
a7b65cdc 357
aac0e8c4
UD
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;
4c0fe6fe 365 struct stat64 output_stat;
aac0e8c4
UD
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. */
4c0fe6fe 388 if (lstat64 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
aac0e8c4
UD
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 {
9ef0a840 399 struct dirent64 *other_dentry;
aac0e8c4
UD
400 const char *other_name;
401 char *other_path;
4c0fe6fe 402 struct stat64 other_stat;
aac0e8c4 403
9ef0a840 404 other_dentry = readdir64 (dirp);
aac0e8c4
UD
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;
19bc17a9 411
aac0e8c4
UD
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
4c0fe6fe 417 if (lstat64 (other_path, &other_stat) >= 0
aac0e8c4
UD
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
a7b65cdc 442
aac0e8c4
UD
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
a7b65cdc 469
aac0e8c4
UD
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
a7b65cdc 493
aac0e8c4
UD
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
6055173a
JM
543/* True if the locale files use the opposite endianness to the
544 machine running localedef. */
545bool swap_endianness_p;
546
1ecbb381
RS
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}
a7b65cdc 593
1ecbb381
RS
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{
7602d070 631 align_locale_data (file, LOCFILE_ALIGN);
1ecbb381 632 record_offset (file);
6055173a 633 value = maybe_swap_uint32 (value);
1ecbb381
RS
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{
7602d070 643 align_locale_data (file, LOCFILE_ALIGN);
1ecbb381
RS
644 record_offset (file);
645 obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
6055173a 646 maybe_swap_uint32_obstack (&file->data, n_elems);
1ecbb381
RS
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. */
19bc17a9 698void
a7b65cdc 699write_locale_data (const char *output_path, int catidx, const char *category,
1ecbb381 700 struct locale_file *file)
19bc17a9 701{
c199a24f 702 size_t cnt, step, maxiov;
19bc17a9
RM
703 int fd;
704 char *fname;
8cebd4ff 705 const char **other_paths = NULL;
1ecbb381
RS
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);
6055173a
JM
719 maybe_swap_uint32_array (vec[0].iov_base, 2);
720 maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
1ecbb381 721 n_elem = 3;
a7b65cdc
UD
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
aac0e8c4 748 fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
036cc82f
RM
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. */
5e6160ed 754 sprintf (fname, "%s%s", output_path, category);
601d2942 755 fd = -2;
036cc82f 756 if (strcmp (category, "LC_MESSAGES") == 0)
036cc82f 757 {
4c0fe6fe 758 struct stat64 st;
ce7a5ef4 759
4c0fe6fe 760 if (stat64 (fname, &st) < 0)
ce7a5ef4 761 {
601d2942 762 if (mkdir (fname, 0777) >= 0)
ce7a5ef4
RM
763 {
764 fd = -1;
765 errno = EISDIR;
766 }
767 }
601d2942 768 else if (!S_ISREG (st.st_mode))
ce7a5ef4
RM
769 {
770 fd = -1;
771 errno = EISDIR;
772 }
036cc82f 773 }
601d2942
UD
774
775 /* Create the locale file with nlinks == 1; this avoids crashing processes
aac0e8c4
UD
776 which currently use the locale and damaging files belonging to other
777 locales as well. */
601d2942
UD
778 if (fd == -2)
779 {
780 unlink (fname);
781 fd = creat (fname, 0666);
782 }
036cc82f 783
19bc17a9
RM
784 if (fd == -1)
785 {
786 int save_err = errno;
787
788 if (errno == EISDIR)
789 {
601d2942
UD
790 sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
791 unlink (fname);
19bc17a9
RM
792 fd = creat (fname, 0666);
793 if (fd == -1)
794 save_err = errno;
795 }
796
880f421f 797 if (fd == -1)
19bc17a9 798 {
f16491eb
CD
799 record_error (0, save_err, _("\
800cannot open output file `%s' for category `%s'"), fname, category);
601d2942 801 free (fname);
19bc17a9
RM
802 return;
803 }
804 }
19bc17a9 805
c199a24f
MB
806#ifdef UIO_MAXIOV
807 maxiov = UIO_MAXIOV;
808#else
809 maxiov = sysconf (_SC_UIO_MAXIOV);
810#endif
811
19bc17a9
RM
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 {
c199a24f
MB
816 step = n_elem - cnt;
817 if (maxiov > 0)
818 step = MIN (maxiov, step);
19bc17a9 819
880f421f 820 if (writev (fd, &vec[cnt], step) < 0)
19bc17a9 821 {
f16491eb
CD
822 record_error (0, errno, _("\
823failure while writing data for category `%s'"), category);
19bc17a9
RM
824 break;
825 }
826 }
827
828 close (fd);
aac0e8c4 829
8cebd4ff
CD
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. */
aac0e8c4
UD
846 if (other_paths != NULL)
847 {
4c0fe6fe 848 struct stat64 fname_stat;
aac0e8c4 849
4c0fe6fe 850 if (lstat64 (fname, &fname_stat) >= 0
aac0e8c4
UD
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;
4c0fe6fe 869 struct stat64 other_fname_stat;
aac0e8c4
UD
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
4c0fe6fe 876 if (lstat64 (other_fname, &other_fname_stat) >= 0
aac0e8c4
UD
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
a7b65cdc 923 strcpy (stpcpy (tmp_fname, fname), ".tmp");
aac0e8c4
UD
924
925 if (link (other_fname, tmp_fname) >= 0)
926 {
927 unlink (fname);
928 if (rename (tmp_fname, fname) < 0)
929 {
f16491eb
CD
930 record_error (0, errno, _("\
931cannot create output file `%s' for category `%s'"), fname, category);
aac0e8c4
UD
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);
19bc17a9 951}
25337753
UD
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)
1d20f7f8 966 lr_error (ldfile, _("expecting string argument for `copy'"));
25337753
UD
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}