chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / locale / programs / locfile.c
1 /* Copyright (C) 1996-2004, 2005, 2006, 2008 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.org>, 1996.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <assert.h>
32 #include <wchar.h>
33
34 #include "../../crypt/md5.h"
35 #include "localedef.h"
36 #include "localeinfo.h"
37 #include "locfile.h"
38 #include "simple-hash.h"
39
40 #include "locfile-kw.h"
41
42 #define obstack_chunk_alloc xmalloc
43 #define obstack_chunk_free free
44
45 /* Temporary storage of the locale data before writing it to the archive.  */
46 static locale_data_t to_archive;
47
48
49 int
50 locfile_read (struct localedef_t *result, const struct charmap_t *charmap)
51 {
52   const char *filename = result->name;
53   const char *repertoire_name = result->repertoire_name;
54   int locale_mask = result->needed & ~result->avail;
55   struct linereader *ldfile;
56   int not_here = ALL_LOCALES;
57
58   /* If no repertoire name was specified use the global one.  */
59   if (repertoire_name == NULL)
60     repertoire_name = repertoire_global;
61
62   /* Open the locale definition file.  */
63   ldfile = lr_open (filename, locfile_hash);
64   if (ldfile == NULL)
65     {
66       if (filename != NULL && filename[0] != '/')
67         {
68           char *i18npath = getenv ("I18NPATH");
69           if (i18npath != NULL && *i18npath != '\0')
70             {
71               const size_t pathlen = strlen (i18npath);
72               char i18npathbuf[pathlen + 1];
73               char path[strlen (filename) + 1 + pathlen
74                         + sizeof ("/locales/") - 1];
75               char *next;
76               i18npath = memcpy (i18npathbuf, i18npath, pathlen + 1);
77
78               while (ldfile == NULL
79                      && (next = strsep (&i18npath, ":")) != NULL)
80                 {
81                   stpcpy (stpcpy (stpcpy (path, next), "/locales/"), filename);
82
83                   ldfile = lr_open (path, locfile_hash);
84
85                   if (ldfile == NULL)
86                     {
87                       stpcpy (stpcpy (path, next), filename);
88
89                       ldfile = lr_open (path, locfile_hash);
90                     }
91                 }
92             }
93
94           /* Test in the default directory.  */
95           if (ldfile == NULL)
96             {
97               char path[strlen (filename) + 1 + sizeof (LOCSRCDIR)];
98
99               stpcpy (stpcpy (stpcpy (path, LOCSRCDIR), "/"), filename);
100               ldfile = lr_open (path, locfile_hash);
101             }
102         }
103
104       if (ldfile == NULL)
105         return 1;
106     }
107
108     /* Parse locale definition file and store result in RESULT.  */
109   while (1)
110     {
111       struct token *now = lr_token (ldfile, charmap, NULL, NULL, verbose);
112       enum token_t nowtok = now->tok;
113       struct token *arg;
114
115       if (nowtok == tok_eof)
116         break;
117
118       if (nowtok == tok_eol)
119         /* Ignore empty lines.  */
120         continue;
121
122       switch (nowtok)
123         {
124         case tok_escape_char:
125         case tok_comment_char:
126           /* We need an argument.  */
127           arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
128
129           if (arg->tok != tok_ident)
130             {
131               SYNTAX_ERROR (_("bad argument"));
132               continue;
133             }
134
135           if (arg->val.str.lenmb != 1)
136             {
137               lr_error (ldfile, _("\
138 argument to `%s' must be a single character"),
139                         nowtok == tok_escape_char
140                         ? "escape_char" : "comment_char");
141
142               lr_ignore_rest (ldfile, 0);
143               continue;
144             }
145
146           if (nowtok == tok_escape_char)
147             ldfile->escape_char = *arg->val.str.startmb;
148           else
149             ldfile->comment_char = *arg->val.str.startmb;
150           break;
151
152         case tok_repertoiremap:
153           /* We need an argument.  */
154           arg = lr_token (ldfile, charmap, NULL, NULL, verbose);
155
156           if (arg->tok != tok_ident)
157             {
158               SYNTAX_ERROR (_("bad argument"));
159               continue;
160             }
161
162           if (repertoire_name == NULL)
163             {
164               char *newp = alloca (arg->val.str.lenmb + 1);
165
166               *((char *) mempcpy (newp, arg->val.str.startmb,
167                                   arg->val.str.lenmb)) = '\0';
168               repertoire_name = newp;
169             }
170           break;
171
172         case tok_lc_ctype:
173           ctype_read (ldfile, result, charmap, repertoire_name,
174                       (locale_mask & CTYPE_LOCALE) == 0);
175           result->avail |= locale_mask & CTYPE_LOCALE;
176           not_here ^= CTYPE_LOCALE;
177           continue;
178
179         case tok_lc_collate:
180           collate_read (ldfile, result, charmap, repertoire_name,
181                         (locale_mask & COLLATE_LOCALE) == 0);
182           result->avail |= locale_mask & COLLATE_LOCALE;
183           not_here ^= COLLATE_LOCALE;
184           continue;
185
186         case tok_lc_monetary:
187           monetary_read (ldfile, result, charmap, repertoire_name,
188                          (locale_mask & MONETARY_LOCALE) == 0);
189           result->avail |= locale_mask & MONETARY_LOCALE;
190           not_here ^= MONETARY_LOCALE;
191           continue;
192
193         case tok_lc_numeric:
194           numeric_read (ldfile, result, charmap, repertoire_name,
195                         (locale_mask & NUMERIC_LOCALE) == 0);
196           result->avail |= locale_mask & NUMERIC_LOCALE;
197           not_here ^= NUMERIC_LOCALE;
198           continue;
199
200         case tok_lc_time:
201           time_read (ldfile, result, charmap, repertoire_name,
202                      (locale_mask & TIME_LOCALE) == 0);
203           result->avail |= locale_mask & TIME_LOCALE;
204           not_here ^= TIME_LOCALE;
205           continue;
206
207         case tok_lc_messages:
208           messages_read (ldfile, result, charmap, repertoire_name,
209                          (locale_mask & MESSAGES_LOCALE) == 0);
210           result->avail |= locale_mask & MESSAGES_LOCALE;
211           not_here ^= MESSAGES_LOCALE;
212           continue;
213
214         case tok_lc_paper:
215           paper_read (ldfile, result, charmap, repertoire_name,
216                       (locale_mask & PAPER_LOCALE) == 0);
217           result->avail |= locale_mask & PAPER_LOCALE;
218           not_here ^= PAPER_LOCALE;
219           continue;
220
221         case tok_lc_name:
222           name_read (ldfile, result, charmap, repertoire_name,
223                      (locale_mask & NAME_LOCALE) == 0);
224           result->avail |= locale_mask & NAME_LOCALE;
225           not_here ^= NAME_LOCALE;
226           continue;
227
228         case tok_lc_address:
229           address_read (ldfile, result, charmap, repertoire_name,
230                         (locale_mask & ADDRESS_LOCALE) == 0);
231           result->avail |= locale_mask & ADDRESS_LOCALE;
232           not_here ^= ADDRESS_LOCALE;
233           continue;
234
235         case tok_lc_telephone:
236           telephone_read (ldfile, result, charmap, repertoire_name,
237                           (locale_mask & TELEPHONE_LOCALE) == 0);
238           result->avail |= locale_mask & TELEPHONE_LOCALE;
239           not_here ^= TELEPHONE_LOCALE;
240           continue;
241
242         case tok_lc_measurement:
243           measurement_read (ldfile, result, charmap, repertoire_name,
244                             (locale_mask & MEASUREMENT_LOCALE) == 0);
245           result->avail |= locale_mask & MEASUREMENT_LOCALE;
246           not_here ^= MEASUREMENT_LOCALE;
247           continue;
248
249         case tok_lc_identification:
250           identification_read (ldfile, result, charmap, repertoire_name,
251                                (locale_mask & IDENTIFICATION_LOCALE) == 0);
252           result->avail |= locale_mask & IDENTIFICATION_LOCALE;
253           not_here ^= IDENTIFICATION_LOCALE;
254           continue;
255
256         default:
257           SYNTAX_ERROR (_("\
258 syntax error: not inside a locale definition section"));
259           continue;
260         }
261
262       /* The rest of the line must be empty.  */
263       lr_ignore_rest (ldfile, 1);
264     }
265
266   /* We read all of the file.  */
267   lr_close (ldfile);
268
269   /* Mark the categories which are not contained in the file.  We assume
270      them to be available and the default data will be used.  */
271   result->avail |= not_here;
272
273   return 0;
274 }
275
276
277 /* Semantic checking of locale specifications.  */
278
279 static void (*const check_funcs[]) (struct localedef_t *,
280                                     const struct charmap_t *) =
281 {
282   [LC_CTYPE] = ctype_finish,
283   [LC_COLLATE] = collate_finish,
284   [LC_MESSAGES] = messages_finish,
285   [LC_MONETARY] = monetary_finish,
286   [LC_NUMERIC] = numeric_finish,
287   [LC_TIME] = time_finish,
288   [LC_PAPER] = paper_finish,
289   [LC_NAME] = name_finish,
290   [LC_ADDRESS] = address_finish,
291   [LC_TELEPHONE] = telephone_finish,
292   [LC_MEASUREMENT] = measurement_finish,
293   [LC_IDENTIFICATION] = identification_finish
294 };
295
296 void
297 check_all_categories (struct localedef_t *definitions,
298                       const struct charmap_t *charmap)
299 {
300   int cnt;
301
302   for (cnt = 0; cnt < sizeof (check_funcs) / sizeof (check_funcs[0]); ++cnt)
303     if (check_funcs[cnt] != NULL)
304       check_funcs[cnt] (definitions, charmap);
305 }
306
307
308 /* Writing the locale data files.  All files use the same output_path.  */
309
310 static void (*const write_funcs[]) (struct localedef_t *,
311                                     const struct charmap_t *, const char *) =
312 {
313   [LC_CTYPE] = ctype_output,
314   [LC_COLLATE] = collate_output,
315   [LC_MESSAGES] = messages_output,
316   [LC_MONETARY] = monetary_output,
317   [LC_NUMERIC] = numeric_output,
318   [LC_TIME] = time_output,
319   [LC_PAPER] = paper_output,
320   [LC_NAME] = name_output,
321   [LC_ADDRESS] = address_output,
322   [LC_TELEPHONE] = telephone_output,
323   [LC_MEASUREMENT] = measurement_output,
324   [LC_IDENTIFICATION] = identification_output
325 };
326
327
328 void
329 write_all_categories (struct localedef_t *definitions,
330                       const struct charmap_t *charmap, const char *locname,
331                       const char *output_path)
332 {
333   int cnt;
334
335   for (cnt = 0; cnt < sizeof (write_funcs) / sizeof (write_funcs[0]); ++cnt)
336     if (write_funcs[cnt] != NULL)
337       write_funcs[cnt] (definitions, charmap, output_path);
338
339   if (! no_archive)
340     {
341       /* The data has to be added to the archive.  Do this now.  */
342       struct locarhandle ah;
343
344       /* Open the archive.  This call never returns if we cannot
345          successfully open the archive.  */
346       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 **
360 siblings_uncached (const char *output_path)
361 {
362   size_t len;
363   char *base, *p;
364   struct stat 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 (lstat (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 stat 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 (lstat (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 **
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
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
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 int swap_endianness_p;
545
546 /* The target's value of __align__(uint32_t) - 1.  */
547 unsigned int uint32_align_mask = 3;
548
549 /* When called outside a start_locale_structure()/end_locale_structure()
550    or start_locale_prelude()/end_locale_prelude() block, record that the
551    next byte in FILE's obstack will be the first byte of a new element.
552    Do likewise for the first call inside a start_locale_structure()/
553    end_locale_structure() block.  */
554 static inline void
555 record_offset (struct locale_file *file)
556 {
557   if (file->structure_stage < 2)
558     {
559       assert (file->next_element < file->n_elements);
560       file->offsets[file->next_element++]
561         = (obstack_object_size (&file->data)
562            + (file->n_elements + 2) * sizeof (uint32_t));
563       if (file->structure_stage == 1)
564         file->structure_stage = 2;
565     }
566 }
567
568 /* Initialize FILE for a new output file.  N_ELEMENTS is the number
569    of elements in the file.  */
570 void
571 init_locale_data (struct locale_file *file, size_t n_elements)
572 {
573   file->n_elements = n_elements;
574   file->next_element = 0;
575   file->offsets = xmalloc (sizeof (uint32_t) * n_elements);
576   obstack_init (&file->data);
577   file->structure_stage = 0;
578 }
579
580 /* Align the size of FILE's obstack object to BOUNDARY bytes.  */
581 void
582 align_locale_data (struct locale_file *file, size_t boundary)
583 {
584   size_t size = -obstack_object_size (&file->data) & (boundary - 1);
585   obstack_blank (&file->data, size);
586   memset (obstack_next_free (&file->data) - size, 0, size);
587 }
588
589 /* Record that FILE's next element contains no data.  */
590 void
591 add_locale_empty (struct locale_file *file)
592 {
593   record_offset (file);
594 }
595
596 /* Record that FILE's next element consists of SIZE bytes starting at DATA.  */
597 void
598 add_locale_raw_data (struct locale_file *file, const void *data, size_t size)
599 {
600   record_offset (file);
601   obstack_grow (&file->data, data, size);
602 }
603
604 /* Finish the current object on OBSTACK and use it as the data for FILE's
605    next element.  */
606 void
607 add_locale_raw_obstack (struct locale_file *file, struct obstack *obstack)
608 {
609   size_t size = obstack_object_size (obstack);
610   record_offset (file);
611   obstack_grow (&file->data, obstack_finish (obstack), size);
612 }
613
614 /* Use STRING as FILE's next element.  */
615 void
616 add_locale_string (struct locale_file *file, const char *string)
617 {
618   record_offset (file);
619   obstack_grow (&file->data, string, strlen (string) + 1);
620 }
621
622 /* Likewise for wide strings.  */
623 void
624 add_locale_wstring (struct locale_file *file, const uint32_t *string)
625 {
626   add_locale_uint32_array (file, string, wcslen_uint32 (string) + 1);
627 }
628
629 /* Record that FILE's next element is the 32-bit integer VALUE.  */
630 void
631 add_locale_uint32 (struct locale_file *file, uint32_t value)
632 {
633   align_locale_data (file, sizeof (uint32_t));
634   record_offset (file);
635   value = maybe_swap_uint32 (value);
636   obstack_grow (&file->data, &value, sizeof (value));
637 }
638
639 /* Record that FILE's next element is an array of N_ELEMS integers
640    starting at DATA.  */
641 void
642 add_locale_uint32_array (struct locale_file *file,
643                          const uint32_t *data, size_t n_elems)
644 {
645   align_locale_data (file, sizeof (uint32_t));
646   record_offset (file);
647   obstack_grow (&file->data, data, n_elems * sizeof (uint32_t));
648   maybe_swap_uint32_obstack (&file->data, n_elems);
649 }
650
651 /* Record that FILE's next element is the single byte given by VALUE.  */
652 void
653 add_locale_char (struct locale_file *file, char value)
654 {
655   record_offset (file);
656   obstack_1grow (&file->data, value);
657 }
658
659 /* Start building an element that contains several different pieces of data.
660    Subsequent calls to add_locale_*() will add data to the same element up
661    till the next call to end_locale_structure().  The element's alignment
662    is dictated by the first piece of data added to it.  */
663 void
664 start_locale_structure (struct locale_file *file)
665 {
666   assert (file->structure_stage == 0);
667   file->structure_stage = 1;
668 }
669
670 /* Finish a structure element that was started by start_locale_structure().
671    Empty structures are OK and behave like add_locale_empty().  */
672 void
673 end_locale_structure (struct locale_file *file)
674 {
675   record_offset (file);
676   assert (file->structure_stage == 2);
677   file->structure_stage = 0;
678 }
679
680 /* Start building data that goes before the next element's recorded offset.
681    Subsequent calls to add_locale_*() will add data to the file without
682    treating any of it as the start of a new element.  Calling
683    end_locale_prelude() switches back to the usual behavior.  */
684 void
685 start_locale_prelude (struct locale_file *file)
686 {
687   assert (file->structure_stage == 0);
688   file->structure_stage = 3;
689 }
690
691 /* End a block started by start_locale_prelude().  */
692 void
693 end_locale_prelude (struct locale_file *file)
694 {
695   assert (file->structure_stage == 3);
696   file->structure_stage = 0;
697 }
698
699 /* Write a locale file, with contents given by FILE.  */
700 void
701 write_locale_data (const char *output_path, int catidx, const char *category,
702                    struct locale_file *file)
703 {
704   size_t cnt, step, maxiov;
705   int fd;
706   char *fname;
707   const char **other_paths;
708   uint32_t header[2];
709   size_t n_elem;
710   struct iovec vec[3];
711
712   assert (file->n_elements == file->next_element);
713   header[0] = LIMAGIC (catidx);
714   header[1] = file->n_elements;
715   vec[0].iov_len = sizeof (header);
716   vec[0].iov_base = header;
717   vec[1].iov_len = sizeof (uint32_t) * file->n_elements;
718   vec[1].iov_base = file->offsets;
719   vec[2].iov_len = obstack_object_size (&file->data);
720   vec[2].iov_base = obstack_finish (&file->data);
721   maybe_swap_uint32_array (vec[0].iov_base, 2);
722   maybe_swap_uint32_array (vec[1].iov_base, file->n_elements);
723   n_elem = 3;
724   if (! no_archive)
725     {
726       /* The data will be added to the archive.  For now we simply
727          generate the image which will be written.  First determine
728          the size.  */
729       int cnt;
730       void *endp;
731
732       to_archive[catidx].size = 0;
733       for (cnt = 0; cnt < n_elem; ++cnt)
734         to_archive[catidx].size += vec[cnt].iov_len;
735
736       /* Allocate the memory for it.  */
737       to_archive[catidx].addr = xmalloc (to_archive[catidx].size);
738
739       /* Fill it in.  */
740       for (cnt = 0, endp = to_archive[catidx].addr; cnt < n_elem; ++cnt)
741         endp = mempcpy (endp, vec[cnt].iov_base, vec[cnt].iov_len);
742
743       /* Compute the MD5 sum for the data.  */
744       __md5_buffer (to_archive[catidx].addr, to_archive[catidx].size,
745                     to_archive[catidx].sum);
746
747       return;
748     }
749
750   fname = xmalloc (strlen (output_path) + 2 * strlen (category) + 7);
751
752   /* Normally we write to the directory pointed to by the OUTPUT_PATH.
753      But for LC_MESSAGES we have to take care for the translation
754      data.  This means we need to have a directory LC_MESSAGES in
755      which we place the file under the name SYS_LC_MESSAGES.  */
756   sprintf (fname, "%s%s", output_path, category);
757   fd = -2;
758   if (strcmp (category, "LC_MESSAGES") == 0)
759     {
760       struct stat st;
761
762       if (stat (fname, &st) < 0)
763         {
764           if (mkdir (fname, 0777) >= 0)
765             {
766               fd = -1;
767               errno = EISDIR;
768             }
769         }
770       else if (!S_ISREG (st.st_mode))
771         {
772           fd = -1;
773           errno = EISDIR;
774         }
775     }
776
777   /* Create the locale file with nlinks == 1; this avoids crashing processes
778      which currently use the locale and damaging files belonging to other
779      locales as well.  */
780   if (fd == -2)
781     {
782       unlink (fname);
783       fd = creat (fname, 0666);
784     }
785
786   if (fd == -1)
787     {
788       int save_err = errno;
789
790       if (errno == EISDIR)
791         {
792           sprintf (fname, "%1$s%2$s/SYS_%2$s", output_path, category);
793           unlink (fname);
794           fd = creat (fname, 0666);
795           if (fd == -1)
796             save_err = errno;
797         }
798
799       if (fd == -1)
800         {
801           if (!be_quiet)
802             WITH_CUR_LOCALE (error (0, save_err, _("\
803 cannot open output file `%s' for category `%s'"), fname, category));
804           free (fname);
805           return;
806         }
807     }
808
809 #ifdef UIO_MAXIOV
810   maxiov = UIO_MAXIOV;
811 #else
812   maxiov = sysconf (_SC_UIO_MAXIOV);
813 #endif
814
815   /* Write the data using writev.  But we must take care for the
816      limitation of the implementation.  */
817   for (cnt = 0; cnt < n_elem; cnt += step)
818     {
819       step = n_elem - cnt;
820       if (maxiov > 0)
821         step = MIN (maxiov, step);
822
823       if (writev (fd, &vec[cnt], step) < 0)
824         {
825           if (!be_quiet)
826             WITH_CUR_LOCALE (error (0, errno, _("\
827 failure while writing data for category `%s'"), category));
828           break;
829         }
830     }
831
832   close (fd);
833
834   /* Compare the file with the locale data files for the same category in
835      other locales, and see if we can reuse it, to save disk space.  */
836   other_paths = siblings (output_path);
837   if (other_paths != NULL)
838     {
839       struct stat fname_stat;
840
841       if (lstat (fname, &fname_stat) >= 0
842           && S_ISREG (fname_stat.st_mode))
843         {
844           const char *fname_tail = fname + strlen (output_path);
845           const char **other_p;
846           int seen_count;
847           ino_t *seen_inodes;
848
849           seen_count = 0;
850           for (other_p = other_paths; *other_p; other_p++)
851             seen_count++;
852           seen_inodes = (ino_t *) xmalloc (seen_count * sizeof (ino_t));
853           seen_count = 0;
854
855           for (other_p = other_paths; *other_p; other_p++)
856             {
857               const char *other_path = *other_p;
858               size_t other_path_len = strlen (other_path);
859               char *other_fname;
860               struct stat other_fname_stat;
861
862               other_fname =
863                 (char *) xmalloc (other_path_len + strlen (fname_tail) + 1);
864               memcpy (other_fname, other_path, other_path_len);
865               strcpy (other_fname + other_path_len, fname_tail);
866
867               if (lstat (other_fname, &other_fname_stat) >= 0
868                   && S_ISREG (other_fname_stat.st_mode)
869                   /* Consider only files on the same device.
870                      Otherwise hard linking won't work anyway.  */
871                   && other_fname_stat.st_dev == fname_stat.st_dev
872                   /* Consider only files with the same permissions.
873                      Otherwise there are security risks.  */
874                   && other_fname_stat.st_uid == fname_stat.st_uid
875                   && other_fname_stat.st_gid == fname_stat.st_gid
876                   && other_fname_stat.st_mode == fname_stat.st_mode
877                   /* Don't compare fname with itself.  */
878                   && other_fname_stat.st_ino != fname_stat.st_ino
879                   /* Files must have the same size, otherwise they
880                      cannot be the same.  */
881                   && other_fname_stat.st_size == fname_stat.st_size)
882                 {
883                   /* Skip this file if we have already read it (under a
884                      different name).  */
885                   int i;
886
887                   for (i = seen_count - 1; i >= 0; i--)
888                     if (seen_inodes[i] == other_fname_stat.st_ino)
889                       break;
890                   if (i < 0)
891                     {
892                       /* Now compare fname and other_fname for real.  */
893                       blksize_t blocksize;
894
895 #ifdef _STATBUF_ST_BLKSIZE
896                       blocksize = MAX (fname_stat.st_blksize,
897                                        other_fname_stat.st_blksize);
898                       if (blocksize > 8 * 1024)
899                         blocksize = 8 * 1024;
900 #else
901                       blocksize = 8 * 1024;
902 #endif
903
904                       if (compare_files (fname, other_fname,
905                                          fname_stat.st_size, blocksize) == 0)
906                         {
907                           /* Found! other_fname is identical to fname.  */
908                           /* Link other_fname to fname.  But use a temporary
909                              file, in case hard links don't work on the
910                              particular filesystem.  */
911                           char * tmp_fname =
912                             (char *) xmalloc (strlen (fname) + 4 + 1);
913
914                           strcpy (stpcpy (tmp_fname, fname), ".tmp");
915
916                           if (link (other_fname, tmp_fname) >= 0)
917                             {
918                               unlink (fname);
919                               if (rename (tmp_fname, fname) < 0)
920                                 {
921                                   if (!be_quiet)
922                                     WITH_CUR_LOCALE (error (0, errno, _("\
923 cannot create output file `%s' for category `%s'"), fname, category));
924                                 }
925                               free (tmp_fname);
926                               free (other_fname);
927                               break;
928                             }
929                           free (tmp_fname);
930                         }
931
932                       /* Don't compare with this file a second time.  */
933                       seen_inodes[seen_count++] = other_fname_stat.st_ino;
934                     }
935                 }
936               free (other_fname);
937             }
938           free (seen_inodes);
939         }
940     }
941
942   free (fname);
943 }
944
945
946 /* General handling of `copy'.  */
947 void
948 handle_copy (struct linereader *ldfile, const struct charmap_t *charmap,
949              const char *repertoire_name, struct localedef_t *result,
950              enum token_t token, int locale, const char *locale_name,
951              int ignore_content)
952 {
953   struct token *now;
954   int warned = 0;
955
956   now = lr_token (ldfile, charmap, result, NULL, verbose);
957   if (now->tok != tok_string)
958     lr_error (ldfile, _("expecting string argument for `copy'"));
959   else if (!ignore_content)
960     {
961       if (now->val.str.startmb == NULL)
962         lr_error (ldfile, _("\
963 locale name should consist only of portable characters"));
964       else
965         {
966           (void) add_to_readlist (locale, now->val.str.startmb,
967                                   repertoire_name, 1, NULL);
968           result->copy_name[locale] = now->val.str.startmb;
969         }
970     }
971
972   lr_ignore_rest (ldfile, now->tok == tok_string);
973
974   /* The rest of the line must be empty and the next keyword must be
975      `END xxx'.  */
976   while ((now = lr_token (ldfile, charmap, result, NULL, verbose))->tok
977          != tok_end && now->tok != tok_eof)
978     {
979       if (warned == 0)
980         {
981           lr_error (ldfile, _("\
982 no other keyword shall be specified when `copy' is used"));
983           warned = 1;
984         }
985
986       lr_ignore_rest (ldfile, 0);
987     }
988
989   if (now->tok != tok_eof)
990     {
991       /* Handle `END xxx'.  */
992       now = lr_token (ldfile, charmap, result, NULL, verbose);
993
994       if (now->tok != token)
995         lr_error (ldfile, _("\
996 `%1$s' definition does not end with `END %1$s'"), locale_name);
997
998       lr_ignore_rest (ldfile, now->tok == token);
999     }
1000   else
1001     /* When we come here we reached the end of the file.  */
1002     lr_error (ldfile, _("%s: premature end of file"), locale_name);
1003 }