intltool-extract.in
Upload User: shyika
Upload Date: 2017-11-25
Package Size: 1227k
Code Size: 23k
Category:

Video Capture

Development Platform:

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