1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
28 #include <sys/param.h>
33 #include "../../crypt/md5.h"
34 #include "localedef.h"
35 #include "localeinfo.h"
37 #include "simple-hash.h"
39 #include "locfile-kw.h"
41 #define obstack_chunk_alloc xmalloc
42 #define obstack_chunk_free free
44 /* Temporary storage of the locale data before writing it to the archive. */
45 static locale_data_t to_archive
;
49 locfile_read (struct localedef_t
*result
, const struct charmap_t
*charmap
)
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
;
57 /* If no repertoire name was specified use the global one. */
58 if (repertoire_name
== NULL
)
59 repertoire_name
= repertoire_global
;
61 /* Open the locale definition file. */
62 ldfile
= lr_open (filename
, locfile_hash
);
65 if (filename
!= NULL
&& filename
[0] != '/')
67 char *i18npath
= getenv ("I18NPATH");
68 if (i18npath
!= NULL
&& *i18npath
!= '\0')
70 const size_t pathlen
= strlen (i18npath
);
71 char i18npathbuf
[pathlen
+ 1];
72 char path
[strlen (filename
) + 1 + pathlen
73 + sizeof ("/locales/") - 1];
75 i18npath
= memcpy (i18npathbuf
, i18npath
, pathlen
+ 1);
78 && (next
= strsep (&i18npath
, ":")) != NULL
)
80 stpcpy (stpcpy (stpcpy (path
, next
), "/locales/"), filename
);
82 ldfile
= lr_open (path
, locfile_hash
);
86 stpcpy (stpcpy (stpcpy (path
, next
), "/"), filename
);
88 ldfile
= lr_open (path
, locfile_hash
);
93 /* Test in the default directory. */
96 char path
[strlen (filename
) + 1 + sizeof (LOCSRCDIR
)];
98 stpcpy (stpcpy (stpcpy (path
, LOCSRCDIR
), "/"), filename
);
99 ldfile
= lr_open (path
, locfile_hash
);
107 /* Parse locale definition file and store result in RESULT. */
110 struct token
*now
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
111 enum token_t nowtok
= now
->tok
;
114 if (nowtok
== tok_eof
)
117 if (nowtok
== tok_eol
)
118 /* Ignore empty lines. */
123 case tok_escape_char
:
124 case tok_comment_char
:
125 /* We need an argument. */
126 arg
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
128 if (arg
->tok
!= tok_ident
)
130 SYNTAX_ERROR (_("bad argument"));
134 if (arg
->val
.str
.lenmb
!= 1)
136 lr_error (ldfile
, _("\
137 argument to `%s' must be a single character"),
138 nowtok
== tok_escape_char
139 ? "escape_char" : "comment_char");
141 lr_ignore_rest (ldfile
, 0);
145 if (nowtok
== tok_escape_char
)
146 ldfile
->escape_char
= *arg
->val
.str
.startmb
;
148 ldfile
->comment_char
= *arg
->val
.str
.startmb
;
151 case tok_repertoiremap
:
152 /* We need an argument. */
153 arg
= lr_token (ldfile
, charmap
, NULL
, NULL
, verbose
);
155 if (arg
->tok
!= tok_ident
)
157 SYNTAX_ERROR (_("bad argument"));
161 if (repertoire_name
== NULL
)
163 char *newp
= alloca (arg
->val
.str
.lenmb
+ 1);
165 *((char *) mempcpy (newp
, arg
->val
.str
.startmb
,
166 arg
->val
.str
.lenmb
)) = '\0';
167 repertoire_name
= newp
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
257 syntax error: not inside a locale definition section"));
261 /* The rest of the line must be empty. */
262 lr_ignore_rest (ldfile
, 1);
265 /* We read all of the file. */
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
;
276 /* Semantic checking of locale specifications. */
278 static void (*const check_funcs
[]) (struct localedef_t
*,
279 const struct charmap_t
*) =
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
296 check_all_categories (struct localedef_t
*definitions
,
297 const struct charmap_t
*charmap
)
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
);
307 /* Writing the locale data files. All files use the same output_path. */
309 static void (*const write_funcs
[]) (struct localedef_t
*,
310 const struct charmap_t
*, const char *) =
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
328 write_all_categories (struct localedef_t
*definitions
,
329 const struct charmap_t
*charmap
, const char *locname
,
330 const char *output_path
)
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
);
340 /* The data has to be added to the archive. Do this now. */
341 struct locarhandle ah
;
343 /* Open the archive. This call never returns if we cannot
344 successfully open the archive. */
346 open_archive (&ah
, false);
348 if (add_locale_to_archive (&ah
, locname
, to_archive
, true) != 0)
349 error (EXIT_FAILURE
, errno
, _("cannot add to locale archive"));
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. */
360 siblings_uncached (const char *output_path
)
364 struct stat64 output_stat
;
369 /* Remove trailing slashes and trailing pathname component. */
370 len
= strlen (output_path
);
371 base
= (char *) alloca (len
);
372 memcpy (base
, output_path
, len
);
374 while (p
> base
&& p
[-1] == '/')
380 while (p
> base
&& p
[-1] != '/');
386 /* Get the properties of output_path. */
387 if (lstat64 (output_path
, &output_stat
) < 0 || !S_ISDIR (output_stat
.st_mode
))
390 /* Iterate through the directories in base directory. */
391 dirp
= opendir (base
);
398 struct dirent64
*other_dentry
;
399 const char *other_name
;
401 struct stat64 other_stat
;
403 other_dentry
= readdir64 (dirp
);
404 if (other_dentry
== NULL
)
407 other_name
= other_dentry
->d_name
;
408 if (strcmp (other_name
, ".") == 0 || strcmp (other_name
, "..") == 0)
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
);
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
)
423 /* Found a subdirectory. Add a trailing slash and store it. */
424 p
= other_path
+ len
+ 1 + strlen (other_name
);
427 elems
= (const char **) xrealloc ((char *) elems
,
428 (nelems
+ 2) * sizeof (char **));
429 elems
[nelems
++] = other_path
;
437 elems
[nelems
] = NULL
;
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. */
446 siblings (const char *output_path
)
448 static const char *last_output_path
;
449 static const char **last_result
;
451 if (output_path
!= last_output_path
)
453 if (last_result
!= NULL
)
457 for (p
= last_result
; *p
!= NULL
; p
++)
462 last_output_path
= output_path
;
463 last_result
= siblings_uncached (output_path
);
469 /* Read as many bytes from a file descriptor as possible. */
471 full_read (int fd
, void *bufarea
, size_t nbyte
)
473 char *buf
= (char *) bufarea
;
477 ssize_t retval
= read (fd
, buf
, nbyte
);
486 else if (errno
!= EINTR
)
489 return buf
- (char *) bufarea
;
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. */
496 compare_files (const char *filename1
, const char *filename2
, size_t size
,
502 fd1
= open (filename1
, O_RDONLY
);
505 fd2
= open (filename2
, O_RDONLY
);
508 char *buf1
= (char *) xmalloc (2 * blocksize
);
509 char *buf2
= buf1
+ blocksize
;
514 size_t bytes
= (size
< blocksize
? size
: blocksize
);
516 if (full_read (fd1
, buf1
, bytes
) < (ssize_t
) bytes
)
521 if (full_read (fd2
, buf2
, bytes
) < (ssize_t
) bytes
)
526 if (memcmp (buf1
, buf2
, bytes
) != 0)
542 /* True if the locale files use the opposite endianness to the
543 machine running localedef. */
544 bool swap_endianness_p
;
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. */
552 record_offset (struct locale_file
*file
)
554 if (file
->structure_stage
< 2)
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;
565 /* Initialize FILE for a new output file. N_ELEMENTS is the number
566 of elements in the file. */
568 init_locale_data (struct locale_file
*file
, size_t n_elements
)
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;
577 /* Align the size of FILE's obstack object to BOUNDARY bytes. */
579 align_locale_data (struct locale_file
*file
, size_t boundary
)
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
);
586 /* Record that FILE's next element contains no data. */
588 add_locale_empty (struct locale_file
*file
)
590 record_offset (file
);
593 /* Record that FILE's next element consists of SIZE bytes starting at DATA. */
595 add_locale_raw_data (struct locale_file
*file
, const void *data
, size_t size
)
597 record_offset (file
);
598 obstack_grow (&file
->data
, data
, size
);
601 /* Finish the current object on OBSTACK and use it as the data for FILE's
604 add_locale_raw_obstack (struct locale_file
*file
, struct obstack
*obstack
)
606 size_t size
= obstack_object_size (obstack
);
607 record_offset (file
);
608 obstack_grow (&file
->data
, obstack_finish (obstack
), size
);
611 /* Use STRING as FILE's next element. */
613 add_locale_string (struct locale_file
*file
, const char *string
)
615 record_offset (file
);
616 obstack_grow (&file
->data
, string
, strlen (string
) + 1);
619 /* Likewise for wide strings. */
621 add_locale_wstring (struct locale_file
*file
, const uint32_t *string
)
623 add_locale_uint32_array (file
, string
, wcslen ((const wchar_t *) string
) + 1);
626 /* Record that FILE's next element is the 32-bit integer VALUE. */
628 add_locale_uint32 (struct locale_file
*file
, uint32_t value
)
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
));
636 /* Record that FILE's next element is an array of N_ELEMS integers
639 add_locale_uint32_array (struct locale_file
*file
,
640 const uint32_t *data
, size_t n_elems
)
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
);
648 /* Record that FILE's next element is the single byte given by VALUE. */
650 add_locale_char (struct locale_file
*file
, char value
)
652 record_offset (file
);
653 obstack_1grow (&file
->data
, value
);
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. */
661 start_locale_structure (struct locale_file
*file
)
663 assert (file
->structure_stage
== 0);
664 file
->structure_stage
= 1;
667 /* Finish a structure element that was started by start_locale_structure.
668 Empty structures are OK and behave like add_locale_empty. */
670 end_locale_structure (struct locale_file
*file
)
672 record_offset (file
);
673 assert (file
->structure_stage
== 2);
674 file
->structure_stage
= 0;
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. */
682 start_locale_prelude (struct locale_file
*file
)
684 assert (file
->structure_stage
== 0);
685 file
->structure_stage
= 3;
688 /* End a block started by start_locale_prelude. */
690 end_locale_prelude (struct locale_file
*file
)
692 assert (file
->structure_stage
== 3);
693 file
->structure_stage
= 0;
696 /* Write a locale file, with contents given by FILE. */
698 write_locale_data (const char *output_path
, int catidx
, const char *category
,
699 struct locale_file
*file
)
701 size_t cnt
, step
, maxiov
;
704 const char **other_paths
= NULL
;
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
);
723 /* The data will be added to the archive. For now we simply
724 generate the image which will be written. First determine
729 to_archive
[catidx
].size
= 0;
730 for (cnt
= 0; cnt
< n_elem
; ++cnt
)
731 to_archive
[catidx
].size
+= vec
[cnt
].iov_len
;
733 /* Allocate the memory for it. */
734 to_archive
[catidx
].addr
= xmalloc (to_archive
[catidx
].size
);
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
);
740 /* Compute the MD5 sum for the data. */
741 __md5_buffer (to_archive
[catidx
].addr
, to_archive
[catidx
].size
,
742 to_archive
[catidx
].sum
);
747 fname
= xmalloc (strlen (output_path
) + 2 * strlen (category
) + 7);
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
);
755 if (strcmp (category
, "LC_MESSAGES") == 0)
759 if (stat64 (fname
, &st
) < 0)
761 if (mkdir (fname
, 0777) >= 0)
767 else if (!S_ISREG (st
.st_mode
))
774 /* Create the locale file with nlinks == 1; this avoids crashing processes
775 which currently use the locale and damaging files belonging to other
780 fd
= creat (fname
, 0666);
785 int save_err
= errno
;
789 sprintf (fname
, "%1$s%2$s/SYS_%2$s", output_path
, category
);
791 fd
= creat (fname
, 0666);
798 record_error (0, save_err
, _("\
799 cannot open output file `%s' for category `%s'"), fname
, category
);
808 maxiov
= sysconf (_SC_UIO_MAXIOV
);
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
)
817 step
= MIN (maxiov
, step
);
819 if (writev (fd
, &vec
[cnt
], step
) < 0)
821 record_error (0, errno
, _("\
822 failure while writing data for category `%s'"), category
);
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). */
840 other_paths
= siblings (output_path
);
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
845 if (other_paths
!= NULL
)
847 struct stat64 fname_stat
;
849 if (lstat64 (fname
, &fname_stat
) >= 0
850 && S_ISREG (fname_stat
.st_mode
))
852 const char *fname_tail
= fname
+ strlen (output_path
);
853 const char **other_p
;
858 for (other_p
= other_paths
; *other_p
; other_p
++)
860 seen_inodes
= (ino_t
*) xmalloc (seen_count
* sizeof (ino_t
));
863 for (other_p
= other_paths
; *other_p
; other_p
++)
865 const char *other_path
= *other_p
;
866 size_t other_path_len
= strlen (other_path
);
868 struct stat64 other_fname_stat
;
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
);
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
)
891 /* Skip this file if we have already read it (under a
895 for (i
= seen_count
- 1; i
>= 0; i
--)
896 if (seen_inodes
[i
] == other_fname_stat
.st_ino
)
900 /* Now compare fname and other_fname for real. */
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;
909 blocksize
= 8 * 1024;
912 if (compare_files (fname
, other_fname
,
913 fname_stat
.st_size
, blocksize
) == 0)
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. */
920 (char *) xmalloc (strlen (fname
) + 4 + 1);
922 strcpy (stpcpy (tmp_fname
, fname
), ".tmp");
924 if (link (other_fname
, tmp_fname
) >= 0)
927 if (rename (tmp_fname
, fname
) < 0)
929 record_error (0, errno
, _("\
930 cannot create output file `%s' for category `%s'"), fname
, category
);
939 /* Don't compare with this file a second time. */
940 seen_inodes
[seen_count
++] = other_fname_stat
.st_ino
;
953 /* General handling of `copy'. */
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
,
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
)
968 if (now
->val
.str
.startmb
== NULL
)
969 lr_error (ldfile
, _("\
970 locale name should consist only of portable characters"));
973 (void) add_to_readlist (locale
, now
->val
.str
.startmb
,
974 repertoire_name
, 1, NULL
);
975 result
->copy_name
[locale
] = now
->val
.str
.startmb
;
979 lr_ignore_rest (ldfile
, now
->tok
== tok_string
);
981 /* The rest of the line must be empty and the next keyword must be
983 while ((now
= lr_token (ldfile
, charmap
, result
, NULL
, verbose
))->tok
984 != tok_end
&& now
->tok
!= tok_eof
)
988 lr_error (ldfile
, _("\
989 no other keyword shall be specified when `copy' is used"));
993 lr_ignore_rest (ldfile
, 0);
996 if (now
->tok
!= tok_eof
)
998 /* Handle `END xxx'. */
999 now
= lr_token (ldfile
, charmap
, result
, NULL
, verbose
);
1001 if (now
->tok
!= token
)
1002 lr_error (ldfile
, _("\
1003 `%1$s' definition does not end with `END %1$s'"), locale_name
);
1005 lr_ignore_rest (ldfile
, now
->tok
== token
);
1008 /* When we come here we reached the end of the file. */
1009 lr_error (ldfile
, _("%s: premature end of file"), locale_name
);