1 /* Reading binary .mo files.
2 Copyright (C) 1995-1998, 2000-2004 Free Software Foundation, Inc.
3 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
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 by
7 the Free Software Foundation; either version 2, or (at your option)
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.
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. */
31 /* This include file describes the main part of binary .mo format. */
36 #include "binary-io.h"
41 #define _(str) gettext (str)
44 /* We read the file completely into memory. This is more efficient than
45 lots of lseek(). This struct represents the .mo file in memory. */
51 enum { MO_LITTLE_ENDIAN
, MO_BIG_ENDIAN
} endian
;
55 /* Read the contents of the given input stream. */
57 read_binary_mo_file (struct binary_mo_file
*bfp
,
58 FILE *fp
, const char *filename
)
67 const size_t increment
= 4096;
68 if (size
+ increment
> alloc
)
70 alloc
= alloc
+ alloc
/ 2;
71 if (alloc
< size
+ increment
)
72 alloc
= size
+ increment
;
73 buf
= (char *) xrealloc (buf
, alloc
);
75 count
= fread (buf
+ size
, 1, increment
, fp
);
79 error (EXIT_FAILURE
, errno
, _("error while reading \"%s\""),
85 buf
= (char *) xrealloc (buf
, size
);
86 bfp
->filename
= filename
;
91 /* Get a 32-bit number from the file, at the given file position. */
93 get_uint32 (const struct binary_mo_file
*bfp
, size_t offset
)
95 nls_uint32 b0
, b1
, b2
, b3
;
97 if (offset
+ 4 > bfp
->size
)
98 error (EXIT_FAILURE
, 0, _("file \"%s\" is truncated"), bfp
->filename
);
100 b0
= *(unsigned char *) (bfp
->data
+ offset
+ 0);
101 b1
= *(unsigned char *) (bfp
->data
+ offset
+ 1);
102 b2
= *(unsigned char *) (bfp
->data
+ offset
+ 2);
103 b3
= *(unsigned char *) (bfp
->data
+ offset
+ 3);
104 if (bfp
->endian
== MO_LITTLE_ENDIAN
)
105 return b0
| (b1
<< 8) | (b2
<< 16) | (b3
<< 24);
107 return (b0
<< 24) | (b1
<< 16) | (b2
<< 8) | b3
;
110 /* Get a static string from the file, at the given file position. */
112 get_string (const struct binary_mo_file
*bfp
, size_t offset
, size_t *lengthp
)
114 /* See 'struct string_desc'. */
115 nls_uint32 s_length
= get_uint32 (bfp
, offset
);
116 nls_uint32 s_offset
= get_uint32 (bfp
, offset
+ 4);
118 if (s_offset
+ s_length
+ 1 > bfp
->size
)
119 error (EXIT_FAILURE
, 0, _("file \"%s\" is truncated"), bfp
->filename
);
120 if (bfp
->data
[s_offset
+ s_length
] != '\0')
121 error (EXIT_FAILURE
, 0,
122 _("file \"%s\" contains a not NUL terminated string"),
125 *lengthp
= s_length
+ 1;
126 return bfp
->data
+ s_offset
;
129 /* Get a system dependent string from the file, at the given file position. */
131 get_sysdep_string (const struct binary_mo_file
*bfp
, size_t offset
,
132 const struct mo_file_header
*header
, size_t *lengthp
)
134 /* See 'struct sysdep_string'. */
141 /* Compute the length. */
143 for (i
= 4; ; i
+= 8)
145 nls_uint32 segsize
= get_uint32 (bfp
, offset
+ i
);
146 nls_uint32 sysdepref
= get_uint32 (bfp
, offset
+ i
+ 4);
147 nls_uint32 sysdep_segment_offset
;
148 nls_uint32 ss_length
;
149 nls_uint32 ss_offset
;
154 if (sysdepref
== SEGMENTS_END
)
156 if (sysdepref
>= header
->n_sysdep_segments
)
158 error (EXIT_FAILURE
, 0, _("file \"%s\" is not in GNU .mo format"),
160 /* See 'struct sysdep_segment'. */
161 sysdep_segment_offset
= header
->sysdep_segments_offset
+ sysdepref
* 8;
162 ss_length
= get_uint32 (bfp
, sysdep_segment_offset
);
163 ss_offset
= get_uint32 (bfp
, sysdep_segment_offset
+ 4);
164 if (ss_offset
+ ss_length
> bfp
->size
)
165 error (EXIT_FAILURE
, 0, _("file \"%s\" is truncated"), bfp
->filename
);
166 if (!(ss_length
> 0 && bfp
->data
[ss_offset
+ ss_length
- 1] == '\0'))
169 sprintf (location
, "sysdep_segment[%u]", (unsigned int) sysdepref
);
170 error (EXIT_FAILURE
, 0,
171 _("file \"%s\" contains a not NUL terminated string, at %s"),
172 bfp
->filename
, location
);
174 n
= strlen (bfp
->data
+ ss_offset
);
175 length
+= (n
> 1 ? 1 + n
+ 1 : n
);
178 /* Allocate and fill the string. */
179 string
= (char *) xmalloc (length
);
181 s_offset
= get_uint32 (bfp
, offset
);
182 for (i
= 4; ; i
+= 8)
184 nls_uint32 segsize
= get_uint32 (bfp
, offset
+ i
);
185 nls_uint32 sysdepref
= get_uint32 (bfp
, offset
+ i
+ 4);
186 nls_uint32 sysdep_segment_offset
;
187 nls_uint32 ss_length
;
188 nls_uint32 ss_offset
;
191 if (s_offset
+ segsize
> bfp
->size
)
192 error (EXIT_FAILURE
, 0, _("file \"%s\" is truncated"), bfp
->filename
);
193 memcpy (p
, bfp
->data
+ s_offset
, segsize
);
197 if (sysdepref
== SEGMENTS_END
)
199 if (sysdepref
>= header
->n_sysdep_segments
)
201 /* See 'struct sysdep_segment'. */
202 sysdep_segment_offset
= header
->sysdep_segments_offset
+ sysdepref
* 8;
203 ss_length
= get_uint32 (bfp
, sysdep_segment_offset
);
204 ss_offset
= get_uint32 (bfp
, sysdep_segment_offset
+ 4);
205 if (ss_offset
+ ss_length
> bfp
->size
)
207 if (!(ss_length
> 0 && bfp
->data
[ss_offset
+ ss_length
- 1] == '\0'))
209 n
= strlen (bfp
->data
+ ss_offset
);
212 memcpy (p
, bfp
->data
+ ss_offset
, n
);
218 if (p
!= string
+ length
)
225 /* Reads an existing .mo file and adds the messages to mlp. */
227 read_mo_file (message_list_ty
*mlp
, const char *filename
)
230 struct binary_mo_file bf
;
231 struct mo_file_header header
;
233 static lex_pos_ty pos
= { __FILE__
, __LINE__
};
235 if (strcmp (filename
, "-") == 0 || strcmp (filename
, "/dev/stdin") == 0)
238 SET_BINARY (fileno (fp
));
242 fp
= fopen (filename
, "rb");
244 error (EXIT_FAILURE
, errno
,
245 _("error while opening \"%s\" for reading"), filename
);
248 /* Read the file contents into memory. */
249 read_binary_mo_file (&bf
, fp
, filename
);
251 /* Get a 32-bit number from the file header. */
252 # define GET_HEADER_FIELD(field) \
253 get_uint32 (&bf, offsetof (struct mo_file_header, field))
255 /* We must grope the file to determine which endian it is.
256 Perversity of the universe tends towards maximum, so it will
257 probably not match the currently executing architecture. */
258 bf
.endian
= MO_BIG_ENDIAN
;
259 header
.magic
= GET_HEADER_FIELD (magic
);
260 if (header
.magic
!= _MAGIC
)
262 bf
.endian
= MO_LITTLE_ENDIAN
;
263 header
.magic
= GET_HEADER_FIELD (magic
);
264 if (header
.magic
!= _MAGIC
)
267 error (EXIT_FAILURE
, 0, _("file \"%s\" is not in GNU .mo format"),
272 header
.revision
= GET_HEADER_FIELD (revision
);
274 /* We support only the major revisions 0 and 1. */
275 switch (header
.revision
>> 16)
279 /* Fill the header parts that apply to major revisions 0 and 1. */
280 header
.nstrings
= GET_HEADER_FIELD (nstrings
);
281 header
.orig_tab_offset
= GET_HEADER_FIELD (orig_tab_offset
);
282 header
.trans_tab_offset
= GET_HEADER_FIELD (trans_tab_offset
);
283 header
.hash_tab_size
= GET_HEADER_FIELD (hash_tab_size
);
284 header
.hash_tab_offset
= GET_HEADER_FIELD (hash_tab_offset
);
286 for (i
= 0; i
< header
.nstrings
; i
++)
294 /* Read the msgid. */
295 msgid
= get_string (&bf
, header
.orig_tab_offset
+ i
* 8,
298 /* Read the msgstr. */
299 msgstr
= get_string (&bf
, header
.trans_tab_offset
+ i
* 8,
302 mp
= message_alloc (msgid
,
303 (strlen (msgid
) + 1 < msgid_len
304 ? msgid
+ strlen (msgid
) + 1
308 message_list_append (mlp
, mp
);
311 switch (header
.revision
& 0xffff)
317 /* Fill the header parts that apply to minor revision >= 1. */
318 header
.n_sysdep_segments
= GET_HEADER_FIELD (n_sysdep_segments
);
319 header
.sysdep_segments_offset
=
320 GET_HEADER_FIELD (sysdep_segments_offset
);
321 header
.n_sysdep_strings
= GET_HEADER_FIELD (n_sysdep_strings
);
322 header
.orig_sysdep_tab_offset
=
323 GET_HEADER_FIELD (orig_sysdep_tab_offset
);
324 header
.trans_sysdep_tab_offset
=
325 GET_HEADER_FIELD (trans_sysdep_tab_offset
);
327 for (i
= 0; i
< header
.n_sysdep_strings
; i
++)
336 /* Read the msgid. */
337 offset
= get_uint32 (&bf
, header
.orig_sysdep_tab_offset
+ i
* 4);
338 msgid
= get_sysdep_string (&bf
, offset
, &header
, &msgid_len
);
340 /* Read the msgstr. */
341 offset
= get_uint32 (&bf
, header
.trans_sysdep_tab_offset
+ i
* 4);
342 msgstr
= get_sysdep_string (&bf
, offset
, &header
, &msgstr_len
);
344 mp
= message_alloc (msgid
,
345 (strlen (msgid
) + 1 < msgid_len
346 ? msgid
+ strlen (msgid
) + 1
350 message_list_append (mlp
, mp
);