ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / intltool-update.in
1 #!@INTLTOOL_PERL@ -w
2
3 #
4 #  The Intltool Message Updater
5 #
6 #  Copyright (C) 2000-2002 Free Software Foundation.
7 #
8 #  Intltool is free software; you can redistribute it and/or
9 #  modify it under the terms of the GNU General Public License 
10 #  version 2 published by the Free Software Foundation.
11 #
12 #  Intltool is distributed in the hope that it will be useful,
13 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 #  General Public License for more details.
16 #
17 #  You should have received a copy of the GNU General Public License
18 #  along with this program; if not, write to the Free Software
19 #  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #
21 #  As a special exception to the GNU General Public License, if you
22 #  distribute this file as part of a program that contains a
23 #  configuration script generated by Autoconf, you may include it under
24 #  the same distribution terms that you use for the rest of that program.
25 #
26 #  Authors: Kenneth Christiansen <kenneth@gnu.org>
27 #           Maciej Stachowiak
28 #           Darin Adler <darin@bentspoon.com>
29
30 ## Release information
31 my $PROGRAM = "intltool-update";
32 my $VERSION = "0.26";
33 my $PACKAGE = "intltool";
34
35 ## Loaded modules
36 use strict;
37 use Getopt::Long;
38 use Cwd;
39 use File::Copy;
40 use File::Find;
41
42 ## Scalars used by the option stuff
43 my $HELP_ARG       = 0;
44 my $VERSION_ARG    = 0;
45 my $DIST_ARG       = 0;
46 my $POT_ARG        = 0;
47 my $HEADERS_ARG    = 0;
48 my $MAINTAIN_ARG   = 0;
49 my $REPORT_ARG     = 0;
50 my $VERBOSE        = 0;
51 my $GETTEXT_PACKAGE = "";
52 my $OUTPUT_FILE    = "";
53
54 my @languages;
55 my $conf_file; # remove later
56 my %varhash = ();
57 my %po_files_by_lang = ();
58
59 # Regular expressions to categorize file types.
60 # FIXME: Please check if the following is correct
61
62 my $xml_extension =
63 "xml(\.in)*|".          # .in is not required
64 "ui|".
65 "lang|".
66 "glade2?(\.in)*|".      # .in is not required
67 "scm(\.in)*|".          # .in is not required
68 "oaf(\.in)+|".
69 "etspec|".
70 "sheet(\.in)+|".
71 "schemas(\.in)+|".
72 "pong(\.in)+";
73
74 my $ini_extension =
75 "desktop(\.in)+|".
76 "caves(\.in)+|". 
77 "directory(\.in)+|".
78 "soundlist(\.in)+|".
79 "keys(\.in)+|".
80 "theme(\.in)+|".
81 "server(\.in)+";
82
83 ## Always print as the first thing
84 $| = 1;
85
86 ## Handle options
87 GetOptions 
88 (
89  "help"                => \$HELP_ARG,
90  "version"             => \$VERSION_ARG,
91  "dist|d"              => \$DIST_ARG,
92  "pot|p"               => \$POT_ARG,
93  "headers|s"           => \$HEADERS_ARG,
94  "maintain|m"          => \$MAINTAIN_ARG,
95  "report|r"            => \$REPORT_ARG,
96  "verbose|x"           => \$VERBOSE,
97  "gettext-package|g=s" => \$GETTEXT_PACKAGE,
98  "output-file|o=s"     => \$OUTPUT_FILE,
99  ) or &print_error_invalid_option;
100
101 &print_help if $HELP_ARG;
102 &print_version if $VERSION_ARG;
103
104 my $arg_count = ($DIST_ARG > 0)
105     + ($POT_ARG > 0)
106     + ($HEADERS_ARG > 0)
107     + ($MAINTAIN_ARG > 0)
108     + ($REPORT_ARG > 0);
109
110 &print_help if $arg_count > 1;
111
112 # --version and --help don't require a module name
113 my $MODULE = $GETTEXT_PACKAGE || &find_package_name;
114
115 if ($DIST_ARG) 
116 {
117     if ($ARGV[0] =~ /^[a-z]/)
118     {
119         &update_po_file ($ARGV[0], $OUTPUT_FILE);
120         &print_status ($ARGV[0], $OUTPUT_FILE);
121     } 
122     else 
123     {
124         &print_help;
125     }
126
127 elsif ($POT_ARG) 
128 {
129     &generate_headers;
130     &generate_po_template;
131
132 elsif ($HEADERS_ARG) 
133 {
134     &generate_headers;
135
136 elsif ($MAINTAIN_ARG) 
137 {
138     &find_leftout_files;
139
140 elsif ($REPORT_ARG) 
141 {
142     &print_report;
143
144 else 
145 {
146     if ($ARGV[0] =~ /^[a-z]/) 
147     {
148         &main ($ARGV[0], $OUTPUT_FILE);
149     } 
150     else 
151     {
152         &print_help;
153     }
154 }
155
156 exit;
157
158 #########
159
160 sub print_version
161 {
162     print <<_EOF_;
163 ${PROGRAM} (${PACKAGE}) $VERSION
164 Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler.
165
166 Copyright (C) 2000-2003 Free Software Foundation, Inc.
167 This is free software; see the source for copying conditions.  There is NO
168 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
169 _EOF_
170     exit;
171 }
172
173 sub print_help
174 {
175     print <<_EOF_;
176 Usage: ${PROGRAM} [OPTION]... LANGCODE
177 Updates PO template files and merge them with the translations.
178
179 Mode of operation (only one is allowed):
180   -p, --pot                   generate the PO template only
181   -s, --headers               generate the header files in POTFILES.in
182   -m, --maintain              search for left out files from POTFILES.in
183   -r, --report                display a status report for the module
184   -d, --dist                  merge LANGCODE.po with existing PO template
185
186 Extra options:
187   -g, --gettext-package=NAME  override PO template name, useful with --pot
188   -o, --output-file=FILE      write merged translation to FILE
189   -x, --verbose               display lots of feedback
190       --help                  display this help and exit
191       --version               output version information and exit
192
193 Examples of use:
194 ${PROGRAM} --pot    just create a new PO template
195 ${PROGRAM} xy       create new PO template and merge xy.po with it
196
197 Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE")
198 or send email to <xml-i18n-tools\@gnome.org>.
199 _EOF_
200     exit;
201 }
202
203 sub main
204 {
205     my ($lang, $output_file) = @_;
206     
207     ## Report error if the language file supplied
208     ## to the command line is non-existent
209     &print_error_not_existing("$lang.po") if ! -s "$lang.po";
210
211     print "Working, please wait..." if $VERBOSE;
212     &generate_headers;
213     &generate_po_template;
214     &update_po_file ($lang, $output_file);
215     &print_status ($lang, $output_file);
216 }
217
218 sub determine_type ($) 
219 {
220    my $type = $_;
221    my $gettext_type;
222
223    # FIXME: Use $xml_extentions, and maybe do all this even nicer
224    my $xml_regex = 
225        "(?:xml(\.in)*|ui|lang|oaf(?:\.in)+|server(?:\.in)+|sheet(?:\.in)+|".
226        "pong(?:\.in)+|etspec|schemas(?:\.in)+)";
227    my $ini_regex =
228        "(?:desktop(?:\.in)+|theme(?:\.in)+|caves(?:\.in)+|directory(?:\.in)+|".
229        "soundlist(?:\.in)+)";
230
231    if ($type =~ /\[type: gettext\/([^\]].*)]/) 
232    {
233         $gettext_type=$1;
234    }
235    elsif ($type =~ /schemas(\.in)+$/) 
236    {
237         $gettext_type="schemas";
238    }
239    elsif ($type =~ /$xml_regex$/) 
240    {
241        $gettext_type="xml";
242    }
243    elsif ($type =~ /glade2?(\.in)*$/) 
244    {
245        $gettext_type="glade";
246    }
247    elsif ($type =~ /$ini_regex$/) 
248    { 
249        $gettext_type="ini";
250    }
251    elsif ($type =~ /scm(\.in)*$/) 
252    {
253        $gettext_type="scheme";
254    }
255    elsif ($type =~ /keys(\.in)+$/) 
256    {
257        $gettext_type="keys";
258    }
259    else 
260    { 
261        $gettext_type=""; 
262    }
263
264    return "gettext\/$gettext_type";
265 }
266
267 sub find_leftout_files
268 {
269     my (@buf_i18n_plain,
270         @buf_i18n_xml,
271         @buf_i18n_xml_unmarked,
272         @buf_i18n_ini,
273         @buf_potfiles,
274         @buf_potfiles_ignore,
275         @buf_allfiles,
276         @buf_allfiles_sorted,
277         @buf_potfiles_sorted
278     );
279
280     ## Search and find all translatable files
281     find sub { 
282         push @buf_i18n_plain, "$File::Find::name" if /\.(c|y|cc|cpp|c\+\+|h|gob)$/ 
283         }, "..";
284     find sub { 
285         push @buf_i18n_xml, "$File::Find::name" if /\.($xml_extension)$/ 
286         }, "..";
287     find sub {
288         push @buf_i18n_ini, "$File::Find::name" if /\.($ini_extension)$/ 
289         }, "..";
290     find sub {
291         push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/
292         }, "..";
293
294
295     open POTFILES, "POTFILES.in" or die "$PROGRAM:  there's no POTFILES.in!\n";
296
297     @buf_potfiles = grep /^[^#]/, <POTFILES>;
298     foreach (@buf_potfiles) {
299         s/^\[.*]\s*//;
300     }
301                             
302     print "Searching for missing translatable files...\n" if $VERBOSE;
303
304     ## Check if we should ignore some found files, when
305     ## comparing with POTFILES.in
306     foreach my $ignore ("POTFILES.skip", "POTFILES.ignore") 
307     {
308         if (-s $ignore) 
309         {
310             open FILE, $ignore;
311             
312             while (<FILE>) 
313             {
314                 if (/^[^#]/)
315                 {
316                     push @buf_potfiles_ignore, $_;
317                 }
318             }
319             print "Found $ignore: Ignoring files...\n" if $VERBOSE;
320             @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles);
321         }
322     }
323
324     foreach my $file (@buf_i18n_plain)
325     {
326         my $in_comment = 0;
327         my $in_macro = 0;
328
329         open FILE, "<$file";
330         while (<FILE>)
331         {
332             # Handle continued multi-line comment.
333             if ($in_comment)
334             {
335                 next unless s-.*\*/--;
336                 $in_comment = 0;
337             }
338             
339             # Handle continued macro.
340             if ($in_macro)
341             {
342                 $in_macro = 0 unless /\\$/;
343                 next;
344             }
345             
346             # Handle start of macro (or any preprocessor directive).
347             if (/^\s*\#/)
348             {
349                 $in_macro = 1 if /^([^\\]|\\.)*\\$/;
350                 next;
351             }
352             
353             # Handle comments and quoted text.
354             while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
355             {
356                 my $match = $1;
357                 if ($match eq "/*")
358                 {
359                     if (!s-/\*.*?\*/--)
360                     {
361                         s-/\*.*--;
362                         $in_comment = 1;
363                     }
364                 }
365                 elsif ($match eq "//")
366                 {
367                     s-//.*--;
368                 }
369                 else # ' or "
370                 {
371                     if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-)
372                     {
373                         warn "mismatched quotes at line $. in $file\n";
374                         s-$match.*--;
375                     }
376                 }
377             }       
378             
379             if (/_\(QUOTEDTEXT/)
380             {
381                 ## Remove the first 3 chars and add newline
382                 push @buf_allfiles, unpack("x3 A*", $file) . "\n";
383                 last;
384             }
385         }
386         close FILE;
387     }
388             
389     foreach my $file (@buf_i18n_xml) 
390     {
391         open FILE, "<$file";
392         
393         while (<FILE>) 
394         {
395             if (/\s_(.*)=\"/ || /translatable=\"yes\"/)
396             {
397                 push @buf_allfiles, unpack("x3 A*", $file) . "\n";
398                 last;
399             }
400         }
401     }
402
403     foreach my $file (@buf_i18n_ini)
404     {
405         open FILE, "<$file";
406         while (<FILE>) 
407         {
408             if (/_(.*)=/)
409             {
410                 push @buf_allfiles, unpack("x3 A*", $file) . "\n";
411                 last;
412             }
413         }
414     }
415
416     foreach my $file (@buf_i18n_xml_unmarked)
417     {
418         push @buf_allfiles, unpack("x3 A*", $file) . "\n";
419     }
420
421
422     @buf_allfiles_sorted = sort (@buf_allfiles);
423     @buf_potfiles_sorted = sort (@buf_potfiles);
424
425     my %in2;
426     foreach (@buf_potfiles_sorted) 
427     {
428         $in2{$_} = 1;
429     }
430
431     my @result;
432
433     foreach (@buf_allfiles_sorted)
434     {
435         if (!exists($in2{$_}))
436         {
437             push @result, $_
438         }
439     }
440
441     ## Save file with information about the files missing
442     ## if any, and give information about this procedure.
443     if (@result) 
444     {
445         print "\n" if $VERBOSE;
446         open OUT, ">missing";
447         print OUT @result;
448         print STDERR "The following files contain translations and are currently not in use. Please\n";
449         print STDERR "consider adding these to the POTFILES.in file, located in the po/ directory.\n\n";
450         print STDERR @result, "\n";
451         print STDERR "If some of these files are left out on purpose then please add them to\n";
452         print STDERR "POTFILES.skip instead of POTFILES.in. A file 'missing' containing this list\n";
453         print STDERR "of left out files has been written in the current directory.\n";
454     }
455
456     ## If there is nothing to complain about, notify the user
457     else {
458         print "\nAll files containing translations are present in POTFILES.in.\n" if $VERBOSE;
459     }
460 }
461
462 sub print_error_invalid_option
463 {
464     ## Handle invalid arguments
465     print STDERR "Try `${PROGRAM} --help' for more information.\n";
466     exit 1;
467 }
468
469 sub generate_headers
470 {
471     my $EXTRACT = `which intltool-extract 2>/dev/null`;
472     chomp $EXTRACT;
473
474     $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"};
475
476     ## Generate the .h header files, so we can allow glade and
477     ## xml translation support
478     if (! -s $EXTRACT)
479     {
480         print STDERR "\n *** The intltool-extract script wasn't found!"
481              ."\n *** Without it, intltool-update can not generate files.\n";
482         exit;
483     }
484     else
485     {
486         open (FILE, "<POTFILES.in") or die "$PROGRAM: POTFILES.in not found.\n";
487         
488         while (<FILE>) 
489         {
490            chomp;
491
492            ## Find xml files in POTFILES.in and generate the
493            ## files with help from the extract script
494
495            my $gettext_type= &determine_type ($1);
496
497            if (/\.($xml_extension|$ini_extension)$/ || /^\[/)
498            {
499                s/^\[[^\[].*]\s*//;
500
501                my $filename = "../$_";
502
503                if ($VERBOSE)
504                {
505                    system ($EXTRACT, "--update", 
506                            "--type=$gettext_type", $filename);
507                } 
508                else 
509                {
510                    system ($EXTRACT, "--update", "--type=$gettext_type", 
511                            "--quiet", $filename);
512                }
513            }
514        }
515        close FILE;
516    }
517 }
518
519 sub generate_po_template
520 {
521     ## Generate the potfiles from the POTFILES.in file
522
523     print "Building the $MODULE.pot...\n" if $VERBOSE;
524
525     move ("POTFILES.in", "POTFILES.in.old");
526
527     open INFILE, "<POTFILES.in.old";
528     open OUTFILE, ">POTFILES.in";
529
530     while (<INFILE>) 
531     {
532         chomp;
533         if (/\.($xml_extension|$ini_extension)$/ || /^\[/) 
534         {
535             s/^\[.*]\s*//;
536             print OUTFILE "$_.h\n";
537         } 
538         else 
539         {
540             print OUTFILE "$_\n";
541         }
542     }
543     
544     close OUTFILE;
545     close INFILE;
546
547     system ("xgettext", "--default-domain\=$MODULE", 
548                         "--directory\=\.\.",
549                         "--add-comments", 
550                         "--keyword\=\_", 
551                         "--keyword\=N\_", 
552                         "--keyword\=U\_",
553                         "--files-from\=\.\/POTFILES\.in");
554
555     move ("POTFILES.in.old", "POTFILES.in");
556
557     print "Removing generated header (.h) files..." if $VERBOSE;
558
559     open FILE, "<POTFILES.in";
560
561     while (<FILE>)
562     {
563         chomp;
564         unlink "../$_.h" if /\.($xml_extension|$ini_extension)$/;
565     }
566
567     close FILE;
568     print "done\n" if $VERBOSE;
569
570     if (!-e "$MODULE.po") 
571     {
572         print STDERR "WARNING: Either none of the files in POTFILES.in contain marked strings,\n".
573                      "         or xgettext failed to generate PO template file. Please consult\n".
574                      "         error message above if there is any.\n";
575         exit (1);
576     }
577
578     system ("rm", "-f", "$MODULE.pot");
579     move ("$MODULE.po", "$MODULE.pot") or 
580     die "$PROGRAM: couldn't move $MODULE.po to $MODULE.pot.\n";
581
582     print "Wrote $MODULE.pot\n" if $VERBOSE;
583 }
584
585 sub update_po_file
586 {
587     -f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n";
588
589     my ($lang, $output_file) = @_;
590     my ($infile, $outfile);
591
592     print "Merging $lang.po with $MODULE.pot..." if $VERBOSE;
593
594     if ($output_file ne "")
595     {
596         $infile = "$lang.po";
597         $outfile = $output_file;
598     }
599     else
600     {
601         copy ("$lang.po", "$lang.po.old") || die "copy failed: $!";
602         $infile = "$lang.po.old";
603         $outfile = "$lang.po";
604     }
605
606     # Perform merge, remove backup file and the "messages" trash file 
607     # generated by gettext
608     system ("msgmerge", "-o", $outfile, $infile, "$MODULE.pot");
609
610     if ($output_file eq "")
611     {
612         unlink $infile;
613     }
614     unlink "messages";
615 }
616
617 sub print_error_not_existing
618 {
619     my ($file) = @_;
620
621     ## Report error if supplied language file is non-existing
622     print STDERR "$PROGRAM: $file does not exist!\n";
623     print STDERR "Try '$PROGRAM --help' for more information.\n";
624     exit;
625 }
626
627 sub gather_po_files
628 {
629     my @po_files = glob ("./*.po");
630
631     @languages = map (&po_file2lang, @po_files);
632
633     foreach my $lang (@languages) 
634     {
635         $po_files_by_lang{$lang} = shift (@po_files);
636     }
637 }
638
639 sub po_file2lang ($)
640 {
641     my $tmp = $_;
642     $tmp =~ s/^.*\/(.*)\.po$/$1/;
643     return $tmp;
644 }
645
646 sub print_status
647 {
648     my ($lang, $output_file) = @_;
649
650     if ($output_file eq "")
651     {
652         $output_file = "$lang.po";
653     }
654
655     system ("msgfmt", "-o", "/dev/null", "--statistics", $output_file);
656     print "\n";
657 }
658
659 sub print_report
660 {
661     &generate_headers;
662     &generate_po_template;
663     &gather_po_files;
664
665     foreach my $lang (@languages) 
666     {
667         print "$lang: ";
668         &update_po_file ($lang, "");
669     }
670
671     print "\n\n * Current translation support in $MODULE \n\n";
672
673     foreach my $lang (@languages)
674     {
675         print "$lang: ";
676         system ("msgfmt", "-o", "/dev/null", "--statistics", "$lang.po");
677     }
678 }
679
680 sub substitute_var
681 {
682     my ($str) = @_;
683     
684     # cache each variable. varhash is global to we can add
685     # variables elsewhere.
686     while (<CONF>)
687     {
688         if (/^(\w+)=(\S+)/)
689         {
690             $varhash{$1} = $2;
691         }
692     }
693     
694     if ($str =~ /^(.*)\${?([A-Z]+)}?(.*)$/)
695     {
696         my $rest = $3;
697         my $untouched = $1;
698         my $sub = $varhash{$2};
699         
700         return substitute_var ("$untouched$sub$rest");
701     }
702     return $str;
703 }
704
705 sub open_CONF_handle
706 {
707     my $base_dirname = getcwd();
708     $base_dirname =~ s@.*/@@;
709
710     my ($conf_in, $src_dir);
711
712     if ($base_dirname =~ /^po(-.+)?$/) 
713     {
714         if (-f "../configure.in") 
715         {
716             $conf_in = "../configure.in";
717         } 
718         elsif (-f "../configure.ac") 
719         {
720             $conf_in = "../configure.ac";
721         } 
722         else 
723         {
724             my $makefile_source;
725             
726             local (*IN);
727             open IN, "<Makefile" || die "can't open Makefile: $!";
728             
729             while (<IN>) 
730             {
731                 if (/^top_srcdir[ \t]*=/) 
732                 {
733                     $src_dir = $_;                  
734                     $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
735                     
736                     chomp $src_dir;
737                     $conf_in = "$src_dir" . "/configure.in" . "\n";
738                 
739                     last;
740                 }
741             }
742             
743             $conf_file || die "Cannot find top_srcdir in Makefile.";
744         }
745         
746         $conf_file = $conf_in;
747         open (CONF, "<$conf_in") || die "can't open $conf_in: $!";
748     }
749     else
750     {
751         print STDERR "$PROGRAM: Unable to proceed.\n" .
752                      "Make sure to run this script inside the po directory.\n";
753         exit;
754     }
755 }
756
757 sub find_package_name
758 {
759     &open_CONF_handle;
760     
761     my $conf_source; {
762         local (*IN);
763         open (IN, "<$conf_file") || die "can't open $conf_file: $!";
764         seek (IN, 0, 0);
765         local $/; # slurp mode
766         $conf_source = <IN>;
767     }
768
769     my $name = "untitled";
770
771     # /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m 
772     # the \s makes this not work, why?
773     if ($conf_source =~ /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\]]+)/m)
774     {
775         $name = $1;
776     }
777     
778     if ($conf_source =~ /^AC_INIT\([\s\[]*([^,\)\s\]]+)\]?\s*,/m) 
779     {
780         $name = $1;
781         $varhash{"AC_PACKAGE_NAME"} = $1;
782     }
783
784     # \s makes this not work, why?
785     $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m;
786     
787     $name = "\$AC_PACKAGE_NAME" if "$name" eq "AC_PACKAGE_NAME";
788     
789     $name = substitute_var ($name);
790
791     return $name if $name;
792 }