No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / read-mo.c
bloba5d69f6c8fa219c4bd732186ecf86d5c2e1ddfc8
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)
8 any later version.
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. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 /* Specification. */
24 #include "read-mo.h"
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stddef.h>
29 #include <string.h>
31 /* This include file describes the main part of binary .mo format. */
32 #include "gmo.h"
34 #include "error.h"
35 #include "xalloc.h"
36 #include "binary-io.h"
37 #include "exit.h"
38 #include "message.h"
39 #include "gettext.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. */
46 struct binary_mo_file
48 const char *filename;
49 char *data;
50 size_t size;
51 enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian;
55 /* Read the contents of the given input stream. */
56 static void
57 read_binary_mo_file (struct binary_mo_file *bfp,
58 FILE *fp, const char *filename)
60 char *buf = NULL;
61 size_t alloc = 0;
62 size_t size = 0;
63 size_t count;
65 while (!feof (fp))
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);
76 if (count == 0)
78 if (ferror (fp))
79 error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
80 filename);
82 else
83 size += count;
85 buf = (char *) xrealloc (buf, size);
86 bfp->filename = filename;
87 bfp->data = buf;
88 bfp->size = size;
91 /* Get a 32-bit number from the file, at the given file position. */
92 static nls_uint32
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);
106 else
107 return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
110 /* Get a static string from the file, at the given file position. */
111 static char *
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"),
123 bfp->filename);
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. */
130 static char *
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'. */
135 size_t length;
136 char *string;
137 size_t i;
138 char *p;
139 nls_uint32 s_offset;
141 /* Compute the length. */
142 length = 0;
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;
150 size_t n;
152 length += segsize;
154 if (sysdepref == SEGMENTS_END)
155 break;
156 if (sysdepref >= header->n_sysdep_segments)
157 /* Invalid. */
158 error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
159 bfp->filename);
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'))
168 char location[30];
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);
180 p = string;
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;
189 size_t n;
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);
194 p += segsize;
195 s_offset += segsize;
197 if (sysdepref == SEGMENTS_END)
198 break;
199 if (sysdepref >= header->n_sysdep_segments)
200 abort ();
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)
206 abort ();
207 if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0'))
208 abort ();
209 n = strlen (bfp->data + ss_offset);
210 if (n > 1)
211 *p++ = '<';
212 memcpy (p, bfp->data + ss_offset, n);
213 p += n;
214 if (n > 1)
215 *p++ = '>';
218 if (p != string + length)
219 abort ();
221 *lengthp = length;
222 return string;
225 /* Reads an existing .mo file and adds the messages to mlp. */
226 void
227 read_mo_file (message_list_ty *mlp, const char *filename)
229 FILE *fp;
230 struct binary_mo_file bf;
231 struct mo_file_header header;
232 unsigned int i;
233 static lex_pos_ty pos = { __FILE__, __LINE__ };
235 if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0)
237 fp = stdin;
238 SET_BINARY (fileno (fp));
240 else
242 fp = fopen (filename, "rb");
243 if (fp == NULL)
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)
266 unrecognised:
267 error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
268 filename);
272 header.revision = GET_HEADER_FIELD (revision);
274 /* We support only the major revisions 0 and 1. */
275 switch (header.revision >> 16)
277 case 0:
278 case 1:
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++)
288 message_ty *mp;
289 char *msgid;
290 size_t msgid_len;
291 char *msgstr;
292 size_t msgstr_len;
294 /* Read the msgid. */
295 msgid = get_string (&bf, header.orig_tab_offset + i * 8,
296 &msgid_len);
298 /* Read the msgstr. */
299 msgstr = get_string (&bf, header.trans_tab_offset + i * 8,
300 &msgstr_len);
302 mp = message_alloc (msgid,
303 (strlen (msgid) + 1 < msgid_len
304 ? msgid + strlen (msgid) + 1
305 : NULL),
306 msgstr, msgstr_len,
307 &pos);
308 message_list_append (mlp, mp);
311 switch (header.revision & 0xffff)
313 case 0:
314 break;
315 case 1:
316 default:
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++)
329 message_ty *mp;
330 char *msgid;
331 size_t msgid_len;
332 char *msgstr;
333 size_t msgstr_len;
334 nls_uint32 offset;
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
347 : NULL),
348 msgstr, msgstr_len,
349 &pos);
350 message_list_append (mlp, mp);
352 break;
354 break;
356 default:
357 goto unrecognised;
360 if (fp != stdin)
361 fclose (fp);