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.  */
45 static locale_data_t to_archive;
46 
47 
48 int
locfile_read(struct localedef_t * result,const struct charmap_t * charmap)49 locfile_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 (filename, locfile_hash);
63   if (ldfile == NULL)
64     {
65       if (filename != NULL && filename[0] != '/')
66 	{
67 	  char *i18npath = getenv ("I18NPATH");
68 	  if (i18npath != NULL && *i18npath != '\0')
69 	    {
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);
76 
77 	      while (ldfile == NULL
78 		     && (next = strsep (&i18npath, ":")) != NULL)
79 		{
80 		  stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
81 
82 		  ldfile = lr_open (path, locfile_hash);
83 
84 		  if (ldfile == NULL)
85 		    {
86 		      stpcpy (stpcpy (stpcpy (path, next), "/"), filename);
87 
88 		      ldfile = lr_open (path, locfile_hash);
89 		    }
90 		}
91 	    }
92 
93 	  /* Test in the default directory.  */
94 	  if (ldfile == NULL)
95 	    {
96 	      char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
97 
98 	      stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
99 	      ldfile = lr_open (path, 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 (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 (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 (ldfile, _("\
137 argument to `%s' must be a single character"),
138 			nowtok == tok_escape_char
139 			? "escape_char" : "comment_char");
140 
141 	      lr_ignore_rest (ldfile, 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 (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 		      (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 			(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 			 (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 			(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 		     (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 			 (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 		      (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 		     (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 			(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 			  (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 			    (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 			       (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 (_("\
257 syntax error: not inside a locale definition section"));
258 	  continue;
259 	}
260 
261       /* The rest of the line must be empty.  */
262       lr_ignore_rest (ldfile, 1);
263     }
264 
265   /* We read all of the file.  */
266   lr_close (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 
278 static 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 
295 void
check_all_categories(struct localedef_t * definitions,const struct charmap_t * charmap)296 check_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 
309 static 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 
327 void
write_all_categories(struct localedef_t * definitions,const struct charmap_t * charmap,const char * locname,const char * output_path)328 write_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, false);
347 
348       if (add_locale_to_archive (&ah, locname, to_archive, true) != 0)
349 	error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
350 
351       /* We are done.  */
352       close_archive (&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.  */
359 static const char **
siblings_uncached(const char * output_path)360 siblings_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 (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;
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 (output_path, &output_stat) < 0 || !S_ISDIR (output_stat.st_mode))
388     return NULL;
389 
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 (;;)
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);
404       if (other_dentry == NULL)
405 	break;
406 
407       other_name = other_dentry->d_name;
408       if (strcmp (other_name, ".") == 0 || strcmp (other_name, "..") == 0)
409 	continue;
410 
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);
415 
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)
422 	{
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;
430 	}
431       else
432 	free (other_path);
433     }
434   closedir (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.  */
445 static const char **
siblings(const char * output_path)446 siblings (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 ((char *) *p);
459 	  free (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.  */
470 static ssize_t
full_read(int fd,void * bufarea,size_t nbyte)471 full_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, buf, 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.  */
495 static int
compare_files(const char * filename1,const char * filename2,size_t size,size_t blocksize)496 compare_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 (filename1, O_RDONLY);
503   if (fd1 >= 0)
504     {
505       fd2 = open (filename2, O_RDONLY);
506       if (fd2 >= 0)
507 	{
508 	  char *buf1 = (char *) xmalloc (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 (fd1, buf1, bytes) < (ssize_t) bytes)
517 		{
518 		  ret = -1;
519 		  break;
520 		}
521 	      if (full_read (fd2, buf2, bytes) < (ssize_t) bytes)
522 		{
523 		  ret = -1;
524 		  break;
525 		}
526 	      if (memcmp (buf1, buf2, bytes) != 0)
527 		{
528 		  ret = 1;
529 		  break;
530 		}
531 	      size -= bytes;
532 	    }
533 
534 	  free (buf1);
535 	  close (fd2);
536 	}
537       close (fd1);
538     }
539   return ret;
540 }
541 
542 /* True if the locale files use the opposite endianness to the
543    machine running localedef.  */
544 bool 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.  */
551 static void
record_offset(struct locale_file * file)552 record_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.  */
567 void
init_locale_data(struct locale_file * file,size_t n_elements)568 init_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 (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.  */
578 void
align_locale_data(struct locale_file * file,size_t boundary)579 align_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, 0, size);
584 }
585 
586 /* Record that FILE's next element contains no data.  */
587 void
add_locale_empty(struct locale_file * file)588 add_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.  */
594 void
add_locale_raw_data(struct locale_file * file,const void * data,size_t size)595 add_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.  */
603 void
add_locale_raw_obstack(struct locale_file * file,struct obstack * obstack)604 add_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.  */
612 void
add_locale_string(struct locale_file * file,const char * string)613 add_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.  */
620 void
add_locale_wstring(struct locale_file * file,const uint32_t * string)621 add_locale_wstring (struct locale_file *file, const uint32_t *string)
622 {
623   add_locale_uint32_array (file, string, wcslen ((const wchar_t *) string) + 1);
624 }
625 
626 /* Record that FILE's next element is the 32-bit integer VALUE.  */
627 void
add_locale_uint32(struct locale_file * file,uint32_t value)628 add_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.  */
638 void
add_locale_uint32_array(struct locale_file * file,const uint32_t * data,size_t n_elems)639 add_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 (&file->data, n_elems);
646 }
647 
648 /* Record that FILE's next element is the single byte given by VALUE.  */
649 void
add_locale_char(struct locale_file * file,char value)650 add_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.  */
660 void
start_locale_structure(struct locale_file * file)661 start_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.  */
669 void
end_locale_structure(struct locale_file * file)670 end_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.  */
681 void
start_locale_prelude(struct locale_file * file)682 start_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.  */
689 void
end_locale_prelude(struct locale_file * file)690 end_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.  */
697 void
write_locale_data(const char * output_path,int catidx,const char * category,struct locale_file * file)698 write_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 (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)
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 (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 (to_archive[catidx].addr, to_archive[catidx].size,
742 		    to_archive[catidx].sum);
743 
744       return;
745     }
746 
747   fname = xmalloc (strlen (output_path) + 2 * strlen (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 (fname, "%s%s", output_path, category);
754   fd = -2;
755   if (strcmp (category, "LC_MESSAGES") == 0)
756     {
757       struct stat64 st;
758 
759       if (stat64 (fname, &st) < 0)
760 	{
761 	  if (mkdir (fname, 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 (fname);
780       fd = creat (fname, 0666);
781     }
782 
783   if (fd == -1)
784     {
785       int save_err = errno;
786 
787       if (errno == EISDIR)
788 	{
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;
794 	}
795 
796       if (fd == -1)
797 	{
798 	  record_error (0, save_err, _("\
799 cannot open output file `%s' for category `%s'"), fname, category);
800 	  free (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, &vec[cnt], step) < 0)
820 	{
821 	  record_error (0, errno, _("\
822 failure while writing data for category `%s'"), category);
823 	  break;
824 	}
825     }
826 
827   close (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 (fname, &fname_stat) >= 0
850 	  && S_ISREG (fname_stat.st_mode))
851 	{
852 	  const char *fname_tail = fname + strlen (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 (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 (other_path);
867 	      char *other_fname;
868 	      struct stat64 other_fname_stat;
869 
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);
874 
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)
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 (fname, other_fname,
913 					 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 (strlen (fname) + 4 + 1);
921 
922 			  strcpy (stpcpy (tmp_fname, fname), ".tmp");
923 
924 			  if (link (other_fname, tmp_fname) >= 0)
925 			    {
926 			      unlink (fname);
927 			      if (rename (tmp_fname, fname) < 0)
928 				{
929 				  record_error (0, errno, _("\
930 cannot create output file `%s' for category `%s'"), fname, category);
931 				}
932 			      free (tmp_fname);
933 			      free (other_fname);
934 			      break;
935 			    }
936 			  free (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 (other_fname);
944 	    }
945 	  free (seen_inodes);
946 	}
947     }
948 
949   free (fname);
950 }
951 
952 
953 /* General handling of `copy'.  */
954 void
handle_copy(struct linereader * ldfile,const struct charmap_t * charmap,const char * repertoire_name,struct localedef_t * result,enum token_t token,int locale,const char * locale_name,int ignore_content)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)
959 {
960   struct token *now;
961   int warned = 0;
962 
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)
967     {
968       if (now->val.str.startmb == NULL)
969 	lr_error (ldfile, _("\
970 locale name should consist only of portable characters"));
971       else
972 	{
973 	  (void) add_to_readlist (locale, now->val.str.startmb,
974 				  repertoire_name, 1, NULL);
975 	  result->copy_name[locale] = now->val.str.startmb;
976 	}
977     }
978 
979   lr_ignore_rest (ldfile, 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 (ldfile, charmap, result, NULL, verbose))->tok
984 	 != tok_end && now->tok != tok_eof)
985     {
986       if (warned == 0)
987 	{
988 	  lr_error (ldfile, _("\
989 no other keyword shall be specified when `copy' is used"));
990 	  warned = 1;
991 	}
992 
993       lr_ignore_rest (ldfile, 0);
994     }
995 
996   if (now->tok != tok_eof)
997     {
998       /* Handle `END xxx'.  */
999       now = lr_token (ldfile, charmap, result, NULL, verbose);
1000 
1001       if (now->tok != token)
1002 	lr_error (ldfile, _("\
1003 `%1$s' definition does not end with `END %1$s'"), locale_name);
1004 
1005       lr_ignore_rest (ldfile, now->tok == token);
1006     }
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);
1010 }
1011