412 lines
10 KiB
C
412 lines
10 KiB
C
/* msgunfmt - converts binary .mo files to Uniforum style .po files
|
|
Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
|
|
Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef STDC_HEADERS
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_LOCALE_H
|
|
# include <locale.h>
|
|
#endif
|
|
|
|
#include "hash.h"
|
|
|
|
#include "error.h"
|
|
#include "getline.h"
|
|
#include "printf.h"
|
|
#include <system.h>
|
|
|
|
#include "gettext.h"
|
|
#include "domain.h"
|
|
#include "hash-string.h"
|
|
#include <libintl.h>
|
|
#include "message.h"
|
|
|
|
#define _(str) gettext (str)
|
|
|
|
#ifndef errno
|
|
extern int errno;
|
|
#endif
|
|
|
|
|
|
/* String containing name the program is called with. */
|
|
const char *program_name;
|
|
|
|
/* Force output of PO file even if empty. */
|
|
static int force_po;
|
|
|
|
/* Long options. */
|
|
static const struct option long_options[] =
|
|
{
|
|
{ "escape", no_argument, NULL, 'E' },
|
|
{ "force-po", no_argument, &force_po, 1 },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "indent", no_argument, NULL, 'i' },
|
|
{ "no-escape", no_argument, NULL, 'e' },
|
|
{ "output-file", required_argument, NULL, 'o' },
|
|
{ "strict", no_argument, NULL, 'S' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ "width", required_argument, NULL, 'w', },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
|
|
/* This defines the byte order within the file. It needs to be set
|
|
appropriately once we have the file open. */
|
|
static enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian;
|
|
|
|
|
|
/* Prototypes for local functions. */
|
|
static void usage PARAMS ((int __status));
|
|
static void error_print PARAMS ((void));
|
|
static nls_uint32 read32 PARAMS ((FILE *__fp, const char *__fn));
|
|
static void seek32 PARAMS ((FILE *__fp, const char *__fn, long __offset));
|
|
static char *string32 PARAMS ((FILE *__fp, const char *__fn, long __offset));
|
|
static message_list_ty *read_mo_file PARAMS ((message_list_ty *__mlp,
|
|
const char *__fn));
|
|
|
|
|
|
int
|
|
main (argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int optchar;
|
|
int do_help = 0;
|
|
int do_version = 0;
|
|
const char *output_file = "-";
|
|
message_list_ty *mlp = NULL;
|
|
|
|
/* Set program name for messages. */
|
|
program_name = argv[0];
|
|
error_print_progname = error_print;
|
|
|
|
#ifdef HAVE_SETLOCALE
|
|
/* Set locale via LC_ALL. */
|
|
setlocale (LC_ALL, "");
|
|
#endif
|
|
|
|
/* Set the text message domain. */
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
textdomain (PACKAGE);
|
|
|
|
while ((optchar = getopt_long (argc, argv, "hio:Vw:", long_options, NULL))
|
|
!= EOF)
|
|
switch (optchar)
|
|
{
|
|
case '\0':
|
|
/* long option */
|
|
break;
|
|
|
|
case 'e':
|
|
message_print_style_escape (0);
|
|
break;
|
|
|
|
case 'E':
|
|
message_print_style_escape (1);
|
|
break;
|
|
|
|
case 'h':
|
|
do_help = 1;
|
|
break;
|
|
|
|
case 'i':
|
|
message_print_style_indent ();
|
|
break;
|
|
|
|
case 'o':
|
|
output_file = optarg;
|
|
break;
|
|
|
|
case 'S':
|
|
message_print_style_uniforum ();
|
|
break;
|
|
|
|
case 'V':
|
|
do_version = 1;
|
|
break;
|
|
|
|
case 'w':
|
|
{
|
|
int value;
|
|
char *endp;
|
|
value = strtol (optarg, &endp, 10);
|
|
if (endp != optarg)
|
|
message_page_width_set (value);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
usage (EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
/* Version information is requested. */
|
|
if (do_version)
|
|
{
|
|
printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
|
|
/* xgettext: no-wrap */
|
|
printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
|
|
This is free software; see the source for copying conditions. There is NO\n\
|
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
|
|
"),
|
|
"1995, 1996, 1997, 1998");
|
|
printf (_("Written by %s.\n"), "Ulrich Drepper");
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
/* Help is requested. */
|
|
if (do_help)
|
|
usage (EXIT_SUCCESS);
|
|
|
|
/* Read the given .mo file. */
|
|
if (optind < argc)
|
|
do
|
|
mlp = read_mo_file (mlp, argv[optind]);
|
|
while (++optind < argc);
|
|
else
|
|
mlp = read_mo_file (NULL, "-");
|
|
|
|
/* Write the resulting message list to the given .po file. */
|
|
message_list_print (mlp, output_file, force_po, 0);
|
|
|
|
/* No problems. */
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
|
|
/* Display usage information and exit. */
|
|
static void
|
|
usage (status)
|
|
int status;
|
|
{
|
|
if (status != EXIT_SUCCESS)
|
|
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
program_name);
|
|
else
|
|
{
|
|
/* xgettext: no-wrap */
|
|
printf (_("\
|
|
Usage: %s [OPTION] [FILE]...\n\
|
|
Mandatory arguments to long options are mandatory for short options too.\n\
|
|
-e, --no-escape do not use C escapes in output (default)\n\
|
|
-E, --escape use C escapes in output, no extended chars\n\
|
|
--force-po write PO file even if empty\n\
|
|
-h, --help display this help and exit\n\
|
|
-i, --indent write indented output style\n\
|
|
-o, --output-file=FILE write output into FILE instead of standard output\n\
|
|
--strict write strict uniforum style\n\
|
|
-V, --version output version information and exit\n\
|
|
-w, --width=NUMBER set output page width\n"),
|
|
program_name);
|
|
/* xgettext: no-wrap */
|
|
fputs (_("\n\
|
|
Convert binary .mo files to Uniforum style .po files.\n\
|
|
Both little-endian and big-endian .mo files are handled.\n\
|
|
If no input file is given or it is -, standard input is read.\n\
|
|
By default the output is written to standard output.\n"), stdout);
|
|
fputs (_("Report bugs to <bug-gnu-utils@gnu.org>.\n"),
|
|
stdout);
|
|
}
|
|
|
|
exit (status);
|
|
}
|
|
|
|
|
|
/* The address of this function will be assigned to the hook in the error
|
|
functions. */
|
|
static void
|
|
error_print ()
|
|
{
|
|
/* We don't want the program name to be printed in messages. Emacs'
|
|
compile.el does not like this. */
|
|
}
|
|
|
|
|
|
/* This function reads a 32-bit number from the file, and assembles it
|
|
according to the current ``endian'' setting. */
|
|
static nls_uint32
|
|
read32 (fp, fn)
|
|
FILE *fp;
|
|
const char *fn;
|
|
{
|
|
int c1, c2, c3, c4;
|
|
|
|
c1 = getc (fp);
|
|
if (c1 == EOF)
|
|
{
|
|
bomb:
|
|
if (ferror (fp))
|
|
error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn);
|
|
error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn);
|
|
}
|
|
c2 = getc (fp);
|
|
if (c2 == EOF)
|
|
goto bomb;
|
|
c3 = getc (fp);
|
|
if (c3 == EOF)
|
|
goto bomb;
|
|
c4 = getc (fp);
|
|
if (c4 == EOF)
|
|
goto bomb;
|
|
if (endian == MO_LITTLE_ENDIAN)
|
|
return (((nls_uint32) c1)
|
|
| ((nls_uint32) c2 << 8)
|
|
| ((nls_uint32) c3 << 16)
|
|
| ((nls_uint32) c4 << 24));
|
|
|
|
return (((nls_uint32) c1 << 24)
|
|
| ((nls_uint32) c2 << 16)
|
|
| ((nls_uint32) c3 << 8)
|
|
| ((nls_uint32) c4));
|
|
}
|
|
|
|
|
|
static void
|
|
seek32 (fp, fn, offset)
|
|
FILE *fp;
|
|
const char *fn;
|
|
long offset;
|
|
{
|
|
if (fseek (fp, offset, 0) < 0)
|
|
error (EXIT_FAILURE, errno, _("seek \"%s\" offset %ld failed"),
|
|
fn, offset);
|
|
}
|
|
|
|
|
|
static char *
|
|
string32 (fp, fn, offset)
|
|
FILE *fp;
|
|
const char *fn;
|
|
long offset;
|
|
{
|
|
long length;
|
|
char *buffer;
|
|
long n;
|
|
|
|
/* Read the string_desc structure, describing where in the file to
|
|
find the string. */
|
|
seek32 (fp, fn, offset);
|
|
length = read32 (fp, fn);
|
|
offset = read32 (fp, fn);
|
|
|
|
/* Allocate memory for the string to be read into. Leave space for
|
|
the NUL on the end. */
|
|
buffer = xmalloc (length + 1);
|
|
|
|
/* Read in the string. Complain if there is an error or it comes up
|
|
short. Add the NUL ourselves. */
|
|
seek32 (fp, fn, offset);
|
|
n = fread (buffer, 1, length, fp);
|
|
if (n != length)
|
|
{
|
|
if (ferror (fp))
|
|
error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn);
|
|
error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn);
|
|
}
|
|
buffer[length] = 0;
|
|
|
|
/* Return the string to the caller. */
|
|
return buffer;
|
|
}
|
|
|
|
|
|
/* This function reads and existing .mo file. Return a message list. */
|
|
static message_list_ty *
|
|
read_mo_file (mlp, fn)
|
|
message_list_ty *mlp;
|
|
const char *fn;
|
|
{
|
|
FILE *fp;
|
|
struct mo_file_header header;
|
|
int j;
|
|
|
|
if (strcmp (fn, "-") == 0 || strcmp (fn, "/dev/stdout") == 0)
|
|
fp = stdin;
|
|
else
|
|
{
|
|
fp = fopen (fn, "rb");
|
|
if (fp == NULL)
|
|
error (EXIT_FAILURE, errno,
|
|
_("error while opening \"%s\" for reading"), fn);
|
|
}
|
|
|
|
/* 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. */
|
|
endian = MO_BIG_ENDIAN;
|
|
header.magic = read32 (fp, fn);
|
|
if (header.magic != _MAGIC)
|
|
{
|
|
endian = MO_LITTLE_ENDIAN;
|
|
seek32 (fp, fn, 0L);
|
|
header.magic = read32 (fp, fn);
|
|
if (header.magic != _MAGIC)
|
|
{
|
|
unrecognised:
|
|
error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
|
|
fn);
|
|
}
|
|
}
|
|
|
|
/* Fill the structure describing the header. */
|
|
header.revision = read32 (fp, fn);
|
|
if (header.revision != MO_REVISION_NUMBER)
|
|
goto unrecognised;
|
|
header.nstrings = read32 (fp, fn);
|
|
header.orig_tab_offset = read32 (fp, fn);
|
|
header.trans_tab_offset = read32 (fp, fn);
|
|
header.hash_tab_size = read32 (fp, fn);
|
|
header.hash_tab_offset = read32 (fp, fn);
|
|
|
|
if (mlp == NULL)
|
|
mlp = message_list_alloc ();
|
|
for (j = 0; j < header.nstrings; ++j)
|
|
{
|
|
static lex_pos_ty pos = { __FILE__, __LINE__ };
|
|
message_ty *mp;
|
|
char *msgid;
|
|
char *msgstr;
|
|
|
|
/* Read the msgid. */
|
|
msgid = string32 (fp, fn, header.orig_tab_offset + j * 8);
|
|
|
|
/* Read the msgstr. */
|
|
msgstr = string32 (fp, fn, header.trans_tab_offset + j * 8);
|
|
|
|
mp = message_alloc (msgid);
|
|
message_variant_append (mp, MESSAGE_DOMAIN_DEFAULT, msgstr, &pos);
|
|
message_list_append (mlp, mp);
|
|
}
|
|
|
|
if (fp != stdin)
|
|
fclose (fp);
|
|
return mlp;
|
|
}
|