1#!/usr/bin/perl -w
2#
3#   <LANG>.po to faqomatic's Language_<LANG>.pm converter.
4#
5#   Usage:
6#       po2pm [-h] [{inputfile|-} [outfile]]
7#
8#   Copyright (c) Andrew W. Nosenko <awn@bcs.zp.ua>, 2001
9#
10#   This program is free software; you can redistribute it and/or modify
11#   it under the terms of either:
12#
13#       a) the GNU General Public License as published by the Free
14#       Software Foundation; either version 2, or (at your option) any
15#       later version, or
16#       b) the "Artistic License" which comes with Perl Kit.
17#
18#   This program is distributed in the hope that it will be useful,
19#   but WITHOUT ANY WARRANTY; without even the implied warranty of
20#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
21#   the GNU General Public License or the Artistic License for more details.
22#
23
24use strict;
25use IO;
26use File::Basename;
27use Getopt::Long;
28
29my $NAME = 'po2pm';
30my $PROG;
31
32sub Help()
33{
34    printf("<LANG>.po to faqomatic's Language_<LANG>.pm converter.\n");
35    printf("Copyright (c) Andrew W. Nosenko <awn\@bcs.zp.ua>, 2001\n");
36    printf("Usage:\n");
37    printf("    %s [-h] [{inputfile|-} [outfile]]\n", $PROG);
38    printf("Where:\n");
39    printf("    inputfile      .po file for building Language_<LANG>.pm from which.\n");
40    printf("                   If omited or have magic name \`-' (minus sign)\n");
41    printf("                   then STDIN will be used\n");
42    printf("    outfile        Name of taget .pm file.  If omited then STDOUT\n");
43    printf("                   will be used.\n");
44    printf("Options:\n");
45    printf("    --help | -h    this Help\n");
46}
47
48sub UseHelp()
49{
50    printf(STDERR "%s: try \`%s --help' for help\n", $PROG, $PROG);
51}
52
53sub perlify( $ )
54{
55    my $s = $_[0];
56    if (defined($s))
57    {
58        $s =~ s/\\\\/\\/g;
59    }
60    return $s;
61}
62
63sub po_to_pm( $$$$ )
64{
65    my ($inhandle, $infilename, $outhandle, $outfilename) = @_;
66    my $state = 'initial';  # 'initial', 'msgid', 'msgstr'
67
68    my $line;
69    my $lineno = 0;
70    my $fuzzy = 0;
71    my ($msgid, $msgstr);
72
73    while(defined($line = $inhandle->getline()))
74    {
75        chomp($line);
76        ++$lineno;
77
78        if ($line =~ m/^\s*$/ || $line =~ m/^#/)
79        {
80            if ($state ne 'initial' && $state ne 'msgstr')
81            {
82                die("$infilename:$lineno: syntax error: current state \`$state', expected state \`initial' or \`msgstr'");
83            }
84
85            if (defined($msgid) && defined($msgstr))
86            {
87                $outhandle->printf(" \"%s\"\n", $msgid)
88                    or die("error output into \`$outfilename': $!");
89                if ($fuzzy)
90                {
91                    $outhandle->printf("# fuzzy # => \"%s\",\n",
92                                   $msgstr)
93                        or die("error output into \`$outfilename': $!");
94                    $outhandle->printf(" => \"\",\n")
95                        or die("error output into \`$outfilename': $!");
96                }
97                else
98                {
99                    $outhandle->printf(" => \"%s\",\n", $msgstr)
100                        or die("error output into \`$outfilename': $!");
101                }
102                undef($msgid);
103                undef($msgstr);
104            }
105            $state = 'initial';
106
107            if ($line =~ m/^#,/ && $line =~ m/, fuzzy\b/)
108            {
109                $fuzzy = 1;
110            }
111            else
112            {
113                $fuzzy = 0;
114            }
115            $outhandle->printf("%s\n", $line)
116                    or die("error output into \`$outfilename': $!");
117            next;
118        }
119
120        if ($line =~ m/^msgid "/)
121        {
122            if ($state ne 'initial')
123            {
124                die("$infilename:$lineno: unexpected msgid");
125            }
126            $line =~ m/^msgid "(.*)"$/
127                or die("$infilename:$lineno: syntax error");
128            $msgid = perlify($1);
129            $state = 'msgid';
130            next;
131        }
132
133        if ($line =~ m/^msgstr "/)
134        {
135            if ($state ne 'msgid')
136            {
137                die("$infilename:$lineno: unexpected msgstr");
138            }
139            $line =~ m/^msgstr "(.*)"$/
140                or die("$infilename:$lineno: syntax error");
141            $msgstr = perlify($1);
142            $state = 'msgstr';
143            next;
144        }
145
146        if ($line =~ m/^"/)
147        {
148            if ($state ne 'msgid' && $state ne 'msgstr')
149            {
150                die("$infilename:$lineno: unexpected string");
151            }
152            $line =~ m/^"(.*)"$/
153                or die("$infilename:$lineno: syntax error");
154            if ($state eq 'msgid')
155            {
156                $msgid .= perlify($1);
157            }
158            elsif ($state eq 'msgstr')
159            {
160                $msgstr .= perlify($1);
161            }
162            next;
163        }
164
165        die("assert! state=$state, infilename=$infilename, lineno=$lineno, line=\`$line'");
166    }
167
168    if ($inhandle->error())
169    {
170        die("error reading from \`$infilename': $!");
171    }
172
173    if ($state ne 'initial' && $state ne 'msgstr')
174    {
175        die("$infilename:$lineno: syntax error at EOF: current state \`$state', expected state \`initial' or \`msgstr'");
176    }
177
178    if (defined($msgid) && defined($msgstr))
179    {
180        $outhandle->printf(" \"%s\"\n", $msgid)
181            or die("error output into \`$outfilename': $!");
182        if ($fuzzy)
183        {
184            $outhandle->printf("# fuzzy # => \"%s\",\n",
185                               $msgstr)
186                or die("error output into \`$outfilename': $!");
187            $outhandle->printf(" => \"\",\n")
188                or die("error output into \`$outfilename': $!");
189        }
190        else
191        {
192            $outhandle->printf(" => \"%s\",\n", $msgstr)
193                or die("error output into \`$outfilename': $!");
194        }
195        undef($msgid);
196        undef($msgstr);
197    }
198
199    return 1;
200}
201
202sub output_pm_header( $$$ )
203{
204    my ($infilename, $outhandle, $outfilename) = @_;
205    my ($inbasename, $outbasename);
206
207    $inbasename = basename($infilename);
208    $outbasename = basename($outfilename);
209
210    $outhandle->printf('#--------------------------------------------------------------------
211#   %s
212#   Generated automatically from %s by %s
213#--------------------------------------------------------------------
214
215sub translations {
216    my $tx = shift;
217
218    my %%data = (
219
220', $outbasename, $inbasename, $NAME)
221        or die("error output into \`$outfilename' : $!");
222    return 1;
223}
224
225sub output_pm_footer( $$ )
226{
227    my ($outhandle, $outfilename) = @_;
228
229    $outhandle->print('
230
231);  # end of %data hash
232
233    my $msgid;
234
235    foreach $msgid (keys(%data))
236    {
237        $tx->{$msgid} = $data{$msgid};
238    }
239    return 1;
240}
241')
242        or die("error output into \`$outfilename' : $!");
243    return 1;
244}
245
246sub main()
247{
248    my $success;
249    my $opt_help;
250    my ($infilename, $inhandle, $in_close_on_exit);
251    my ($outfilename, $outhandle, $out_close_on_exit);
252
253    $PROG = basename($0);
254    Getopt::Long::Configure('bundling', 'no_ignore_case', 'no_auto_abbrev');
255    $success = GetOptions('help|h' => \$opt_help);
256
257    if (!$success)
258    {
259        UseHelp();
260        exit(1);
261    }
262    if ($opt_help)
263    {
264        Help();
265        exit(0);
266    }
267
268    if (@ARGV > 2)
269    {
270        printf(STDERR
271               "%s: too many command line arguments\n",
272               $PROG);
273        UseHelp();
274        exit(1);
275    }
276
277    #
278    #   Open input/output handles
279    #
280    $infilename = $ARGV[0];
281    if ($infilename && $infilename ne '-')
282    {
283        $inhandle = IO::File->new($infilename, 'r')
284            or die("error opening \`$infilename' for reading: $!\n");
285        $in_close_on_exit = 1;
286    }
287    else
288    {
289        $infilename = '(stdin)';
290        $inhandle = IO::Handle->new_from_fd(fileno(STDIN), 'r')
291            or die("error reopening STDIN for reading: $!\n");
292        $in_close_on_exit = 0;
293    }
294
295    $outfilename = $ARGV[1];
296    if ($outfilename)
297    {
298        $outhandle = IO::File->new($outfilename, 'w')
299            or die("error opening \`$outfilename' for writing: $!\n");
300        $out_close_on_exit = 1;
301    }
302    else
303    {
304        $outfilename = '(stdout)';
305        $outhandle = IO::Handle->new_from_fd(fileno(STDOUT), 'w')
306            or die("error reopening STDOUT for writing: $!\n");
307        $out_close_on_exit = 0;
308    }
309
310    #
311    #   Main work
312    #
313
314    $success = output_pm_header($infilename, $outhandle, $outfilename)
315        && po_to_pm($inhandle, $infilename, $outhandle, $outfilename)
316        && output_pm_footer($outhandle, $outfilename);
317
318    #
319    #   Finish
320    #
321    if ($in_close_on_exit)
322    {
323        $inhandle->close()
324            or die("error closing input file \`$infilename': $!\n");
325    }
326    if ($out_close_on_exit)
327    {
328        $outhandle->close()
329            or die("error closing output file \`$outfilename': $!\n");
330    }
331
332    return $success ? 0 : 1;
333}
334
335my $rc = main();
336exit($rc);
337