/* Reads an existing .mo file and adds the messages to mlp. */ void read_mo_file (message_list_ty *mlp, const char *filename) { FILE *fp; struct binary_mo_file bf; struct mo_file_header header; unsigned int i; static lex_pos_ty pos = { __FILE__, __LINE__ }; if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0) { fp = stdin; SET_BINARY (fileno (fp)); } else { fp = fopen (filename, "rb"); if (fp == NULL) error (EXIT_FAILURE, errno, _("error while opening \"%s\" for reading"), filename); } /* Read the file contents into memory. */ read_binary_mo_file (&bf, fp, filename); /* Get a 32-bit number from the file header. */ # define GET_HEADER_FIELD(field) \ get_uint32 (&bf, offsetof (struct mo_file_header, field)) /* We must grope the file to determine which endian it is. Perversity of the universe tends towards maximum, so it will probably not match the currently executing architecture. */ bf.endian = MO_BIG_ENDIAN; header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { bf.endian = MO_LITTLE_ENDIAN; header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { unrecognised: error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"), filename); } } header.revision = GET_HEADER_FIELD (revision); /* We support only the major revisions 0 and 1. */ switch (header.revision >> 16) { case 0: case 1: /* Fill the header parts that apply to major revisions 0 and 1. */ header.nstrings = GET_HEADER_FIELD (nstrings); header.orig_tab_offset = GET_HEADER_FIELD (orig_tab_offset); header.trans_tab_offset = GET_HEADER_FIELD (trans_tab_offset); header.hash_tab_size = GET_HEADER_FIELD (hash_tab_size); header.hash_tab_offset = GET_HEADER_FIELD (hash_tab_offset); for (i = 0; i < header.nstrings; i++) { message_ty *mp; char *msgid; size_t msgid_len; char *msgstr; size_t msgstr_len; /* Read the msgid. */ msgid = get_string (&bf, header.orig_tab_offset + i * 8, &msgid_len); /* Read the msgstr. */ msgstr = get_string (&bf, header.trans_tab_offset + i * 8, &msgstr_len); mp = message_alloc (msgid, (strlen (msgid) + 1 < msgid_len ? msgid + strlen (msgid) + 1 : NULL), msgstr, msgstr_len, &pos); message_list_append (mlp, mp); } switch (header.revision & 0xffff) { case 0: break; case 1: default: /* Fill the header parts that apply to minor revision >= 1. */ header.n_sysdep_segments = GET_HEADER_FIELD (n_sysdep_segments); header.sysdep_segments_offset = GET_HEADER_FIELD (sysdep_segments_offset); header.n_sysdep_strings = GET_HEADER_FIELD (n_sysdep_strings); header.orig_sysdep_tab_offset = GET_HEADER_FIELD (orig_sysdep_tab_offset); header.trans_sysdep_tab_offset = GET_HEADER_FIELD (trans_sysdep_tab_offset); for (i = 0; i < header.n_sysdep_strings; i++) { message_ty *mp; char *msgid; size_t msgid_len; char *msgstr; size_t msgstr_len; nls_uint32 offset; /* Read the msgid. */ offset = get_uint32 (&bf, header.orig_sysdep_tab_offset + i * 4); msgid = get_sysdep_string (&bf, offset, &header, &msgid_len); /* Read the msgstr. */ offset = get_uint32 (&bf, header.trans_sysdep_tab_offset + i * 4); msgstr = get_sysdep_string (&bf, offset, &header, &msgstr_len); mp = message_alloc (msgid, (strlen (msgid) + 1 < msgid_len ? msgid + strlen (msgid) + 1 : NULL), msgstr, msgstr_len, &pos); message_list_append (mlp, mp); } break; } break; default: goto unrecognised; } if (fp != stdin) fclose (fp); }
/* Reads an existing .mo file and adds the messages to mlp. */ void read_mo_file (message_list_ty *mlp, const char *filename) { FILE *fp; struct binary_mo_file bf; struct mo_file_header header; unsigned int i; static lex_pos_ty pos = { __FILE__, __LINE__ }; if (strcmp (filename, "-") == 0 || strcmp (filename, "/dev/stdin") == 0) { fp = stdin; SET_BINARY (fileno (fp)); } else { fp = fopen (filename, "rb"); if (fp == NULL) error (EXIT_FAILURE, errno, _("error while opening \"%s\" for reading"), filename); } /* Read the file contents into memory. */ read_binary_mo_file (&bf, fp, filename); /* Get a 32-bit number from the file header. */ # define GET_HEADER_FIELD(field) \ get_uint32 (&bf, offsetof (struct mo_file_header, field)) /* We must grope the file to determine which endian it is. Perversity of the universe tends towards maximum, so it will probably not match the currently executing architecture. */ bf.endian = MO_BIG_ENDIAN; header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { bf.endian = MO_LITTLE_ENDIAN; header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { unrecognised: error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"), filename); } } header.revision = GET_HEADER_FIELD (revision); /* We support only the major revisions 0 and 1. */ switch (header.revision >> 16) { case 0: case 1: /* Fill the header parts that apply to major revisions 0 and 1. */ header.nstrings = GET_HEADER_FIELD (nstrings); header.orig_tab_offset = GET_HEADER_FIELD (orig_tab_offset); header.trans_tab_offset = GET_HEADER_FIELD (trans_tab_offset); header.hash_tab_size = GET_HEADER_FIELD (hash_tab_size); header.hash_tab_offset = GET_HEADER_FIELD (hash_tab_offset); for (i = 0; i < header.nstrings; i++) { message_ty *mp; char *msgctxt; char *msgid; size_t msgid_len; char *separator; char *msgstr; size_t msgstr_len; /* Read the msgctxt and msgid. */ msgid = get_string (&bf, header.orig_tab_offset + i * 8, &msgid_len); /* Split into msgctxt and msgid. */ separator = strchr (msgid, MSGCTXT_SEPARATOR); if (separator != NULL) { /* The part before the MSGCTXT_SEPARATOR is the msgctxt. */ *separator = '\0'; msgctxt = msgid; msgid = separator + 1; msgid_len -= msgid - msgctxt; } else msgctxt = NULL; /* Read the msgstr. */ msgstr = get_string (&bf, header.trans_tab_offset + i * 8, &msgstr_len); mp = message_alloc (msgctxt, msgid, (strlen (msgid) + 1 < msgid_len ? msgid + strlen (msgid) + 1 : NULL), msgstr, msgstr_len, &pos); message_list_append (mlp, mp); } switch (header.revision & 0xffff) { case 0: break; case 1: default: /* Fill the header parts that apply to minor revision >= 1. */ header.n_sysdep_segments = GET_HEADER_FIELD (n_sysdep_segments); header.sysdep_segments_offset = GET_HEADER_FIELD (sysdep_segments_offset); header.n_sysdep_strings = GET_HEADER_FIELD (n_sysdep_strings); header.orig_sysdep_tab_offset = GET_HEADER_FIELD (orig_sysdep_tab_offset); header.trans_sysdep_tab_offset = GET_HEADER_FIELD (trans_sysdep_tab_offset); for (i = 0; i < header.n_sysdep_strings; i++) { message_ty *mp; char *msgctxt; char *msgid; size_t msgid_len; char *separator; char *msgstr; size_t msgstr_len; nls_uint32 offset; size_t f; /* Read the msgctxt and msgid. */ offset = get_uint32 (&bf, header.orig_sysdep_tab_offset + i * 4); msgid = get_sysdep_string (&bf, offset, &header, &msgid_len); /* Split into msgctxt and msgid. */ separator = strchr (msgid, MSGCTXT_SEPARATOR); if (separator != NULL) { /* The part before the MSGCTXT_SEPARATOR is the msgctxt. */ *separator = '\0'; msgctxt = msgid; msgid = separator + 1; msgid_len -= msgid - msgctxt; } else msgctxt = NULL; /* Read the msgstr. */ offset = get_uint32 (&bf, header.trans_sysdep_tab_offset + i * 4); msgstr = get_sysdep_string (&bf, offset, &header, &msgstr_len); mp = message_alloc (msgctxt, msgid, (strlen (msgid) + 1 < msgid_len ? msgid + strlen (msgid) + 1 : NULL), msgstr, msgstr_len, &pos); /* Only messages with c-format or objc-format annotation are recognized as having system-dependent strings by msgfmt. Which one of the two, we don't know. We have to guess, assuming that c-format is more probable than objc-format and that the .mo was likely produced by "msgfmt -c". */ for (f = format_c; ; f = format_objc) { bool valid = true; struct formatstring_parser *parser = formatstring_parsers[f]; const char *str_end; const char *str; str_end = msgid + msgid_len; for (str = msgid; str < str_end; str += strlen (str) + 1) { char *invalid_reason = NULL; void *descr = parser->parse (str, false, NULL, &invalid_reason); if (descr != NULL) parser->free (descr); else { free (invalid_reason); valid = false; break; } } if (valid) { str_end = msgstr + msgstr_len; for (str = msgstr; str < str_end; str += strlen (str) + 1) { char *invalid_reason = NULL; void *descr = parser->parse (str, true, NULL, &invalid_reason); if (descr != NULL) parser->free (descr); else { free (invalid_reason); valid = false; break; } } } if (valid) { /* Found the most likely among c-format, objc-format. */ mp->is_format[f] = yes; break; } /* Try next f. */ if (f == format_objc) break; } message_list_append (mlp, mp); } break; } break; default: goto unrecognised; } if (fp != stdin) fclose (fp); }