intltool-extract.in
上传用户:ledjyj
上传日期:2014-08-27
资源大小:2639k
文件大小:22k
源码类别:

驱动编程

开发平台:

Unix_Linux

  1. #!@INTLTOOL_PERL@ -w 
  2. # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4  -*-
  3. #
  4. #  The Intltool Message Extractor
  5. #
  6. #  Copyright (C) 2000-2001, 2003 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 as
  10. #  published by the Free Software Foundation; either version 2 of the
  11. #  License, or (at your option) any later version.
  12. #
  13. #  Intltool is distributed in the hope that it will be useful,
  14. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. #  General Public License for more details.
  17. #
  18. #  You should have received a copy of the GNU General Public License
  19. #  along with this program; if not, write to the Free Software
  20. #  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. #
  22. #  As a special exception to the GNU General Public License, if you
  23. #  distribute this file as part of a program that contains a
  24. #  configuration script generated by Autoconf, you may include it under
  25. #  the same distribution terms that you use for the rest of that program.
  26. #
  27. #  Authors: Kenneth Christiansen <kenneth@gnu.org>
  28. #           Darin Adler <darin@bentspoon.com>
  29. #
  30. ## Release information
  31. my $PROGRAM      = "intltool-extract";
  32. my $PACKAGE      = "intltool";
  33. my $VERSION      = "0.35.0";
  34. ## Loaded modules
  35. use strict; 
  36. use File::Basename;
  37. use Getopt::Long;
  38. ## Scalars used by the option stuff
  39. my $TYPE_ARG = "0";
  40. my $LOCAL_ARG = "0";
  41. my $HELP_ARG  = "0";
  42. my $VERSION_ARG = "0";
  43. my $UPDATE_ARG  = "0";
  44. my $QUIET_ARG   = "0";
  45. my $SRCDIR_ARG = ".";
  46. my $FILE;
  47. my $OUTFILE;
  48. my $gettext_type = "";
  49. my $input;
  50. my %messages = ();
  51. my %loc = ();
  52. my %count = ();
  53. my %comments = ();
  54. my $strcount = 0;
  55. my $XMLCOMMENT = "";
  56. ## Use this instead of w for XML files to handle more possible characters.
  57. my $w = "[-A-Za-z0-9._:]";
  58. ## Always print first
  59. $| = 1;
  60. ## Handle options
  61. GetOptions (
  62.     "type=s"     => $TYPE_ARG,
  63.             "local|l"    => $LOCAL_ARG,
  64.             "help|h"     => $HELP_ARG,
  65.             "version|v"  => $VERSION_ARG,
  66.             "update"     => $UPDATE_ARG,
  67.     "quiet|q"    => $QUIET_ARG,
  68.     "srcdir=s"  => $SRCDIR_ARG,
  69.             ) or &error;
  70. &split_on_argument;
  71. ## Check for options. 
  72. ## This section will check for the different options.
  73. sub split_on_argument {
  74.     if ($VERSION_ARG) {
  75.         &version;
  76.     } elsif ($HELP_ARG) {
  77. &help;
  78.         
  79.     } elsif ($LOCAL_ARG) {
  80.         &place_local;
  81.         &extract;
  82.     } elsif ($UPDATE_ARG) {
  83. &place_normal;
  84. &extract;
  85.     } elsif (@ARGV > 0) {
  86. &place_normal;
  87. &message;
  88. &extract;
  89.     } else {
  90. &help;
  91.     }  
  92. }    
  93. sub place_normal {
  94.     $FILE  = $ARGV[0];
  95.     $OUTFILE     = "$FILE.h";
  96. }   
  97. sub place_local {
  98.     $FILE  = $ARGV[0];
  99.     $OUTFILE     = fileparse($FILE, ());
  100.     if (!-e "tmp/") { 
  101.         system("mkdir tmp/"); 
  102.     }
  103.     $OUTFILE     = "./tmp/$OUTFILE.h"
  104. }
  105. sub determine_type {
  106.    if ($TYPE_ARG =~ /^gettext/(.*)/) {
  107. $gettext_type=$1
  108.    }
  109. }
  110. ## Sub for printing release information
  111. sub version{
  112.     print <<_EOF_;
  113. ${PROGRAM} (${PACKAGE}) $VERSION
  114. Copyright (C) 2000, 2003 Free Software Foundation, Inc.
  115. Written by Kenneth Christiansen, 2000.
  116. This is free software; see the source for copying conditions.  There is NO
  117. warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  118. _EOF_
  119.     exit;
  120. }
  121. ## Sub for printing usage information
  122. sub help {
  123.     print <<_EOF_;
  124. Usage: ${PROGRAM} [OPTION]... [FILENAME]
  125. Generates a header file from an XML source file.
  126. It grabs all strings between <_translatable_node> and its end tag in
  127. XML files. Read manpage (man ${PROGRAM}) for more info.
  128.       --type=TYPE   Specify the file type of FILENAME. Currently supports:
  129.                     "gettext/glade", "gettext/ini", "gettext/keys"
  130.                     "gettext/rfc822deb", "gettext/schemas",
  131.                     "gettext/scheme", "gettext/xml"
  132.   -l, --local       Writes output into current working directory
  133.                     (conflicts with --update)
  134.       --update      Writes output into the same directory the source file 
  135.                     reside (conflicts with --local)
  136.       --srcdir      Root of the source tree
  137.   -v, --version     Output version information and exit
  138.   -h, --help        Display this help and exit
  139.   -q, --quiet       Quiet mode
  140. Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE")
  141. or send email to <xml-i18n-tools@gnome.org>.
  142. _EOF_
  143.     exit;
  144. }
  145. ## Sub for printing error messages
  146. sub error{
  147.     print STDERR "Try `${PROGRAM} --help' for more information.n";
  148.     exit;
  149. }
  150. sub message {
  151.     print "Generating C format header file for translation.n" unless $QUIET_ARG;
  152. }
  153. sub extract {
  154.     &determine_type;
  155.     &convert;
  156.     open OUT, ">$OUTFILE";
  157.     binmode (OUT) if $^O eq 'MSWin32';
  158.     &msg_write;
  159.     close OUT;
  160.     print "Wrote $OUTFILEn" unless $QUIET_ARG;
  161. }
  162. sub convert {
  163.     ## Reading the file
  164.     {
  165. local (*IN);
  166. local $/; #slurp mode
  167. open (IN, "<$SRCDIR_ARG/$FILE") || die "can't open $SRCDIR_ARG/$FILE: $!";
  168. $input = <IN>;
  169.     }
  170.     &type_ini if $gettext_type eq "ini";
  171.     &type_keys if $gettext_type eq "keys";
  172.     &type_xml if $gettext_type eq "xml";
  173.     &type_glade if $gettext_type eq "glade";
  174.     &type_scheme if $gettext_type eq "scheme";
  175.     &type_schemas  if $gettext_type eq "schemas";
  176.     &type_rfc822deb  if $gettext_type eq "rfc822deb";
  177. }
  178. sub entity_decode_minimal
  179. {
  180.     local ($_) = @_;
  181.     s/&apos;/'/g; # '
  182.     s/&quot;/"/g; # "
  183.     s/&amp;/&/g;
  184.     return $_;
  185. }
  186. sub entity_decode
  187. {
  188.     local ($_) = @_;
  189.     s/&apos;/'/g; # '
  190.     s/&quot;/"/g; # "
  191.     s/&amp;/&/g;
  192.     s/&lt;/</g;
  193.     s/&gt;/>/g;
  194.     return $_;
  195. }
  196. sub escape_char
  197. {
  198.     return '"' if $_ eq '"';
  199.     return 'n' if $_ eq "n";
  200.     return '\' if $_ eq '\';
  201.     return $_;
  202. }
  203. sub escape
  204. {
  205.     my ($string) = @_;
  206.     return join "", map &escape_char, split //, $string;
  207. }
  208. sub type_ini {
  209.     ### For generic translatable desktop files ###
  210.     while ($input =~ /^_.*=(.*)$/mg) {
  211.         $messages{$1} = [];
  212.     }
  213. }
  214. sub type_keys {
  215.     ### For generic translatable mime/keys files ###
  216.     while ($input =~ /^s*_w+=(.*)$/mg) {
  217.         $messages{$1} = [];
  218.     }
  219. }
  220. sub type_xml {
  221.     ### For generic translatable XML files ###
  222.     my $tree = readXml($input);
  223.     parseTree(0, $tree);
  224. }
  225. sub print_var {
  226.     my $var = shift;
  227.     my $vartype = ref $var;
  228.     
  229.     if ($vartype =~ /ARRAY/) {
  230.         my @arr = @{$var};
  231.         print "[ ";
  232.         foreach my $el (@arr) {
  233.             print_var($el);
  234.             print ", ";
  235.         }
  236.         print "] ";
  237.     } elsif ($vartype =~ /HASH/) {
  238.         my %hash = %{$var};
  239.         print "{ ";
  240.         foreach my $key (keys %hash) {
  241.             print "$key => ";
  242.             print_var($hash{$key});
  243.             print ", ";
  244.         }
  245.         print "} ";
  246.     } else {
  247.         print $var;
  248.     }
  249. }
  250. # Same syntax as getAttributeString in intltool-merge.in.in, similar logic (look for ## differences comment)
  251. sub getAttributeString
  252. {
  253.     my $sub = shift;
  254.     my $do_translate = shift || 1;
  255.     my $language = shift || "";
  256.     my $translate = shift;
  257.     my $result = "";
  258.     foreach my $e (reverse(sort(keys %{ $sub }))) {
  259. my $key    = $e;
  260. my $string = $sub->{$e};
  261. my $quote = '"';
  262. $string =~ s/^[s]+//;
  263. $string =~ s/[s]+$//;
  264. if ($string =~ /^'.*'$/)
  265. {
  266.     $quote = "'";
  267. }
  268. $string =~ s/^['"]//g;
  269. $string =~ s/['"]$//g;
  270.         ## differences from intltool-merge.in.in
  271. if ($key =~ /^_/) {
  272.             $comments{entity_decode($string)} = $XMLCOMMENT if $XMLCOMMENT;
  273.             $messages{entity_decode($string)} = [];
  274.             $$translate = 2;
  275. }
  276.         ## differences end here from intltool-merge.in.in
  277. $result .= " $key=$quote$string$quote";
  278.     }
  279.     return $result;
  280. }
  281. # Verbatim copy from intltool-merge.in.in
  282. sub getXMLstring
  283. {
  284.     my $ref = shift;
  285.     my $spacepreserve = shift || 0;
  286.     my @list = @{ $ref };
  287.     my $result = "";
  288.     my $count = scalar(@list);
  289.     my $attrs = $list[0];
  290.     my $index = 1;
  291.     $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/));
  292.     $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/));
  293.     while ($index < $count) {
  294. my $type = $list[$index];
  295. my $content = $list[$index+1];
  296.         if (! $type ) {
  297.     # We've got CDATA
  298.     if ($content) {
  299. # lets strip the whitespace here, and *ONLY* here
  300.                 $content =~ s/s+/ /gs if (!$spacepreserve);
  301. $result .= $content;
  302.     }
  303. } elsif ( "$type" ne "1" ) {
  304.     # We've got another element
  305.     $result .= "<$type";
  306.     $result .= getAttributeString(@{$content}[0], 0); # no nested translatable elements
  307.     if ($content) {
  308. my $subresult = getXMLstring($content, $spacepreserve);
  309. if ($subresult) {
  310.     $result .= ">".$subresult . "</$type>";
  311. } else {
  312.     $result .= "/>";
  313. }
  314.     } else {
  315. $result .= "/>";
  316.     }
  317. }
  318. $index += 2;
  319.     }
  320.     return $result;
  321. }
  322. # Verbatim copy from intltool-merge.in.in, except for MULTIPLE_OUTPUT handling removed
  323. # Translate list of nodes if necessary
  324. sub translate_subnodes
  325. {
  326.     my $fh = shift;
  327.     my $content = shift;
  328.     my $language = shift || "";
  329.     my $singlelang = shift || 0;
  330.     my $spacepreserve = shift || 0;
  331.     my @nodes = @{ $content };
  332.     my $count = scalar(@nodes);
  333.     my $index = 0;
  334.     while ($index < $count) {
  335.         my $type = $nodes[$index];
  336.         my $rest = $nodes[$index+1];
  337.         traverse($fh, $type, $rest, $language, $spacepreserve);
  338.         $index += 2;
  339.     }
  340. }
  341. # Based on traverse() in intltool-merge.in.in
  342. sub traverse
  343. {
  344.     my $fh = shift; # unused, to allow us to sync code between -merge and -extract
  345.     my $nodename = shift;
  346.     my $content = shift;
  347.     my $language = shift || "";
  348.     my $spacepreserve = shift || 0;
  349.     if ($nodename && "$nodename" eq "1") {
  350.         $XMLCOMMENT = $content;
  351.     } elsif ($nodename) {
  352. # element
  353. my @all = @{ $content };
  354. my $attrs = shift @all;
  355. my $translate = 0;
  356. my $outattr = getAttributeString($attrs, 1, $language, $translate);
  357. if ($nodename =~ /^_/) {
  358.     $translate = 1;
  359.     $nodename =~ s/^_//;
  360. }
  361. my $lookup = '';
  362.         $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/));
  363.         $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/));
  364. if ($translate) {
  365.     $lookup = getXMLstring($content, $spacepreserve);
  366.             if (!$spacepreserve) {
  367.                 $lookup =~ s/^s+//s;
  368.                 $lookup =~ s/s+$//s;
  369.             }
  370.     if ($lookup && $translate != 2) {
  371.                 $comments{$lookup} = $XMLCOMMENT if $XMLCOMMENT;
  372.                 $messages{$lookup} = [];
  373.             } elsif ($translate == 2) {
  374.                 translate_subnodes($fh, @all, $language, 1, $spacepreserve);
  375.     }
  376. } else {
  377.             $XMLCOMMENT = "";
  378.     my $count = scalar(@all);
  379.     if ($count > 0) {
  380.                 my $index = 0;
  381.                 while ($index < $count) {
  382.                     my $type = $all[$index];
  383.                     my $rest = $all[$index+1];
  384.                     traverse($fh, $type, $rest, $language, $spacepreserve);
  385.                     $index += 2;
  386.                 }
  387.     }
  388. }
  389.         $XMLCOMMENT = "";
  390.     }
  391. }
  392. # Verbatim copy from intltool-merge.in.in, $fh for compatibility
  393. sub parseTree
  394. {
  395.     my $fh        = shift;
  396.     my $ref       = shift;
  397.     my $language  = shift || "";
  398.     my $name = shift @{ $ref };
  399.     my $cont = shift @{ $ref };
  400.     while (!$name || "$name" eq "1") {
  401.         $name = shift @{ $ref };
  402.         $cont = shift @{ $ref };
  403.     }
  404.     my $spacepreserve = 0;
  405.     my $attrs = @{$cont}[0];
  406.     $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/));
  407.     traverse($fh, $name, $cont, $language, $spacepreserve);
  408. }
  409. # Verbatim copy from intltool-merge.in.in
  410. sub intltool_tree_comment
  411. {
  412.     my $expat = shift;
  413.     my $data  = $expat->original_string();
  414.     my $clist = $expat->{Curlist};
  415.     my $pos   = $#$clist;
  416.     $data =~ s/^<!--//s;
  417.     $data =~ s/-->$//s;
  418.     push @$clist, 1 => $data;
  419. }
  420. # Verbatim copy from intltool-merge.in.in
  421. sub intltool_tree_cdatastart
  422. {
  423.     my $expat    = shift;
  424.     my $clist = $expat->{Curlist};
  425.     my $pos   = $#$clist;
  426.     push @$clist, 0 => $expat->original_string();
  427. }
  428. # Verbatim copy from intltool-merge.in.in
  429. sub intltool_tree_cdataend
  430. {
  431.     my $expat    = shift;
  432.     my $clist = $expat->{Curlist};
  433.     my $pos   = $#$clist;
  434.     $clist->[$pos] .= $expat->original_string();
  435. }
  436. # Verbatim copy from intltool-merge.in.in
  437. sub intltool_tree_char
  438. {
  439.     my $expat = shift;
  440.     my $text  = shift;
  441.     my $clist = $expat->{Curlist};
  442.     my $pos   = $#$clist;
  443.     # Use original_string so that we retain escaped entities
  444.     # in CDATA sections.
  445.     #
  446.     if ($pos > 0 and $clist->[$pos - 1] eq '0') {
  447.         $clist->[$pos] .= $expat->original_string();
  448.     } else {
  449.         push @$clist, 0 => $expat->original_string();
  450.     }
  451. }
  452. # Verbatim copy from intltool-merge.in.in
  453. sub intltool_tree_start
  454. {
  455.     my $expat    = shift;
  456.     my $tag      = shift;
  457.     my @origlist = ();
  458.     # Use original_string so that we retain escaped entities
  459.     # in attribute values.  We must convert the string to an
  460.     # @origlist array to conform to the structure of the Tree
  461.     # Style.
  462.     #
  463.     my @original_array = split /x/, $expat->original_string();
  464.     my $source         = $expat->original_string();
  465.     # Remove leading tag.
  466.     #
  467.     $source =~ s|^s*<s*(S+)||s;
  468.     # Grab attribute key/value pairs and push onto @origlist array.
  469.     #
  470.     while ($source)
  471.     {
  472.        if ($source =~ /^s*([w:-]+)s*[=]s*["]/)
  473.        {
  474.            $source =~ s|^s*([w:-]+)s*[=]s*["]([^"]*)["]||s;
  475.            push @origlist, $1;
  476.            push @origlist, '"' . $2 . '"';
  477.        }
  478.        elsif ($source =~ /^s*([w:-]+)s*[=]s*[']/)
  479.        {
  480.            $source =~ s|^s*([w:-]+)s*[=]s*[']([^']*)[']||s;
  481.            push @origlist, $1;
  482.            push @origlist, "'" . $2 . "'";
  483.        }
  484.        else
  485.        {
  486.            last;
  487.        }
  488.     }
  489.     my $ol = [ { @origlist } ];
  490.     push @{ $expat->{Lists} }, $expat->{Curlist};
  491.     push @{ $expat->{Curlist} }, $tag => $ol;
  492.     $expat->{Curlist} = $ol;
  493. }
  494. # Copied from intltool-merge.in.in and added comment handler.
  495. sub readXml
  496. {
  497.     my $xmldoc = shift || return;
  498.     my $ret = eval 'require XML::Parser';
  499.     if(!$ret) {
  500.         die "You must have XML::Parser installed to run $0nn";
  501.     }
  502.     my $xp = new XML::Parser(Style => 'Tree');
  503.     $xp->setHandlers(Char => &intltool_tree_char);
  504.     $xp->setHandlers(Start => &intltool_tree_start);
  505.     $xp->setHandlers(CdataStart => &intltool_tree_cdatastart);
  506.     $xp->setHandlers(CdataEnd => &intltool_tree_cdataend);
  507.     ## differences from intltool-merge.in.in
  508.     $xp->setHandlers(Comment => &intltool_tree_comment);
  509.     ## differences end here from intltool-merge.in.in
  510.     my $tree = $xp->parse($xmldoc);
  511.     #print_var($tree);
  512. # <foo><!-- comment --><head id="a">Hello <em>there</em></head><bar>Howdy<ref/></bar>do</foo>
  513. # would be:
  514. # [foo, [{}, 1, "comment", head, [{id => "a"}, 0, "Hello ",  em, [{}, 0, "there"]], bar, 
  515. # [{}, 0, "Howdy",  ref, [{}]], 0, "do" ] ]
  516.     return $tree;
  517. }
  518. sub type_schemas {
  519.     ### For schemas XML files ###
  520.          
  521.     # FIXME: We should handle escaped < (less than)
  522.     while ($input =~ /
  523.                       <locale name="C">s*
  524.                           (<default>s*(?:<!--([^>]*?)-->s*)?(.*?)s*</default>s*)?
  525.                           (<short>s*(?:<!--([^>]*?)-->s*)?(.*?)s*</short>s*)?
  526.                           (<long>s*(?:<!--([^>]*?)-->s*)?(.*?)s*</long>s*)?
  527.                       </locale>
  528.                      /sgx) {
  529.         my @totranslate = ($3,$6,$9);
  530.         my @eachcomment = ($2,$5,$8);
  531.         foreach (@totranslate) {
  532.             my $currentcomment = shift @eachcomment;
  533.             next if !$_;
  534.             s/s+/ /g;
  535.             $messages{entity_decode_minimal($_)} = [];
  536.             $comments{entity_decode_minimal($_)} = $currentcomment if (defined($currentcomment));
  537.         }
  538.     }
  539. }
  540. sub type_rfc822deb {
  541.     ### For rfc822-style Debian configuration files ###
  542.     my $lineno = 1;
  543.     my $type = '';
  544.     while ($input =~ /G(.*?)(^|n)(_+)([^:]+):[ t]*(.*?)(?=nS|$)/sg)
  545.     {
  546.         my ($pre, $newline, $underscore, $tag, $text) = ($1, $2, $3, $4, $5);
  547.         while ($pre =~ m/n/g)
  548.         {
  549.             $lineno ++;
  550.         }
  551.         $lineno += length($newline);
  552.         my @str_list = rfc822deb_split(length($underscore), $text);
  553.         for my $str (@str_list)
  554.         {
  555.             $strcount++;
  556.             $messages{$str} = [];
  557.             $loc{$str} = $lineno;
  558.             $count{$str} = $strcount;
  559.             my $usercomment = '';
  560.             while($pre =~ s/(^|n)#([^n]*)$//s)
  561.             {
  562.                 $usercomment = "n" . $2 . $usercomment;
  563.             }
  564.             $comments{$str} = $tag . $usercomment;
  565.         }
  566.         $lineno += ($text =~ s/n//g);
  567.     }
  568. }
  569. sub rfc822deb_split {
  570.     # Debian defines a special way to deal with rfc822-style files:
  571.     # when a value contain newlines, it consists of
  572.     #   1.  a short form (first line)
  573.     #   2.  a long description, all lines begin with a space,
  574.     #       and paragraphs are separated by a single dot on a line
  575.     # This routine returns an array of all paragraphs, and reformat
  576.     # them.
  577.     # When first argument is 2, the string is a comma separated list of
  578.     # values.
  579.     my $type = shift;
  580.     my $text = shift;
  581.     $text =~ s/^[ t]//mg;
  582.     return (split(/, */, $text, 0)) if $type ne 1;
  583.     return ($text) if $text !~ /n/;
  584.     $text =~ s/([^n]*)n//;
  585.     my @list = ($1);
  586.     my $str = '';
  587.     for my $line (split (/n/, $text))
  588.     {
  589.         chomp $line;
  590.         if ($line =~ /^.s*$/)
  591.         {
  592.             #  New paragraph
  593.             $str =~ s/s*$//;
  594.             push(@list, $str);
  595.             $str = '';
  596.         }
  597.         elsif ($line =~ /^s/)
  598.         {
  599.             #  Line which must not be reformatted
  600.             $str .= "n" if length ($str) && $str !~ /n$/;
  601.             $line =~ s/s+$//;
  602.             $str .= $line."n";
  603.         }
  604.         else
  605.         {
  606.             #  Continuation line, remove newline
  607.             $str .= " " if length ($str) && $str !~ /n$/;
  608.             $str .= $line;
  609.         }
  610.     }
  611.     $str =~ s/s*$//;
  612.     push(@list, $str) if length ($str);
  613.     return @list;
  614. }
  615. sub type_glade {
  616.     ### For translatable Glade XML files ###
  617.     my $tags = "label|title|text|format|copyright|comments|preview_text|tooltip|message";
  618.     while ($input =~ /<($tags)>([^<]+)</($tags)>/sg) {
  619. # Glade sometimes uses tags that normally mark translatable things for
  620.         # little bits of non-translatable content. We work around this by not
  621.         # translating strings that only includes something like label4 or window1.
  622. $messages{entity_decode($2)} = [] unless $2 =~ /^(window|label|dialog)[0-9]+$/;
  623.     }
  624.     
  625.     while ($input =~ /<items>(..[^<]*)</items>/sg) {
  626. for my $item (split (/n/, $1)) {
  627.     $messages{entity_decode($item)} = [];
  628. }
  629.     }
  630.     ## handle new glade files
  631.     while ($input =~ /<(property|atkproperty)s+[^>]*translatables*=s*"yes"(?:s+[^>]*commentss*=s*"([^"]*)")?[^>]*>([^<]+)</1>/sg) {
  632. $messages{entity_decode($3)} = [] unless $3 =~ /^(window|label)[0-9]+$/;
  633.         if (defined($2) and !($3 =~ /^(window|label)[0-9]+$/)) {
  634.    $comments{entity_decode($3)} = entity_decode($2) ;
  635.         }
  636.     }
  637.     while ($input =~ /<atkactions+action_name="([^>]*)"s+description="([^>]+)"/>/sg) {
  638.         $messages{entity_decode_minimal($2)} = [];
  639.     }
  640. }
  641. sub type_scheme {
  642.     my ($line, $i, $state, $str, $trcomment, $char);
  643.     for $line (split(/n/, $input)) {
  644.         $i = 0;
  645.         $state = 0; # 0 - nothing, 1 - string, 2 - translatable string
  646.         while ($i < length($line)) {
  647.             if (substr($line,$i,1) eq """) {
  648.                 if ($state == 2) {
  649.                     $comments{$str} = $trcomment if ($trcomment);
  650.                     $messages{$str} = [];
  651.                     $str = '';
  652.                     $state = 0; $trcomment = "";
  653.                 } elsif ($state == 1) {
  654.                     $str = '';
  655.                     $state = 0; $trcomment = "";
  656.                 } else {
  657.                     $state = 1;
  658.                     $str = '';
  659.                     if ($i>0 && substr($line,$i-1,1) eq '_') {
  660.                         $state = 2;
  661.                     }
  662.                 }
  663.             } elsif (!$state) {
  664.                 if (substr($line,$i,1) eq ";") {
  665.                     $trcomment = substr($line,$i+1);
  666.                     $trcomment =~ s/^;*s*//;
  667.                     $i = length($line);
  668.                 } elsif ($trcomment && substr($line,$i,1) !~ /s|(|)|_/) {
  669.                     $trcomment = "";
  670.                 }
  671.             } else {
  672.                 if (substr($line,$i,1) eq "\") {
  673.                     $char = substr($line,$i+1,1);
  674.                     if ($char ne """ && $char ne "\") {
  675.                        $str = $str . "\";
  676.                     }
  677.                     $i++;
  678.                 }
  679.                 $str = $str . substr($line,$i,1);
  680.             }
  681.             $i++;
  682.         }
  683.     }
  684. }
  685. sub msg_write {
  686.     my @msgids;
  687.     if (%count)
  688.     {
  689.         @msgids = sort { $count{$a} <=> $count{$b} } keys %count;
  690.     }
  691.     else
  692.     {
  693.         @msgids = sort keys %messages;
  694.     }
  695.     for my $message (@msgids)
  696.     {
  697. my $offsetlines = 1;
  698. $offsetlines++ if $message =~ /%/;
  699. if (defined ($comments{$message}))
  700. {
  701. while ($comments{$message} =~ m/n/g)
  702. {
  703.     $offsetlines++;
  704. }
  705. }
  706. print OUT "# ".($loc{$message} - $offsetlines).  " "$FILE"n"
  707.         if defined $loc{$message};
  708.     print OUT "/* ".$comments{$message}." */n"
  709.                 if defined $comments{$message};
  710.     print OUT "/* xgettext:no-c-format */n" if $message =~ /%/;
  711.         
  712.      my @lines = split (/n/, $message, -1);
  713.      for (my $n = 0; $n < @lines; $n++)
  714. {
  715.             if ($n == 0)
  716.             {
  717.   print OUT "char *s = N_(""; 
  718.             }
  719.             else
  720.             {  
  721.                 print OUT "             ""; 
  722.             }
  723.             print OUT escape($lines[$n]);
  724.             if ($n < @lines - 1)
  725.             {
  726.                 print OUT "\n"n"; 
  727.             }
  728.             else
  729.             {
  730.                 print OUT "");n";  
  731.     }
  732.         }
  733.     }
  734. }