Index: intltool/intltool-merge.in.in
===================================================================
RCS file: /cvs/gnome/intltool/intltool-merge.in.in,v
retrieving revision 1.70
diff -u -p -r1.70 intltool-merge.in.in
--- intltool/intltool-merge.in.in	13 Sep 2003 15:33:21 -0000	1.70
+++ intltool/intltool-merge.in.in	28 Oct 2003 16:12:20 -0000
@@ -41,6 +41,18 @@ use strict; 
 use Getopt::Long;
 use Text::Wrap;
 
+my $ret = eval 'require XML::Parser';
+if(!$ret) {
+    die "You must have XML::Parser installed to run $0\n";
+} 
+
+#use vars qw($must_end_tag, $last_depth, $translation_depth, @tag_stack, @entered_tag);
+my $must_end_tag      = -1;
+my $last_depth        = -1;
+my $translation_depth = -1;
+my @tag_stack = ();
+my @entered_tag = ();
+
 ## Scalars used by the option stuff
 my $HELP_ARG = 0;
 my $VERSION_ARG = 0;
@@ -112,14 +124,7 @@ elsif ($XML_STYLE_ARG && @ARGV > 2) 
 	&preparation;
 	&print_message;
 	
-	if ($MULTIPLE_OUTPUT)
-	{
-	    &xml_merge_multiple_output;
-	}
-	else
-	{
-	    &xml_merge_single_output;
-	}
+    &xml_merge_output;
 
 	&finalize;
 } 
@@ -594,110 +599,278 @@ sub ba_merge_translations
 
 ## XML (non-bonobo-activation) merge code
 
-sub xml_merge_multiple_output
+sub parseTree
 {
-    my $source;
-
-    {
-       local $/; # slurp mode
-       open INPUT, "<$FILE" or die "can't open $FILE: $!";
-       $source = <INPUT>;
-       close INPUT;
+    my $fh        = shift;
+    my $ref       = shift;
+    my $depth     = shift || 0;
+    my $language  = shift;
+    my $indent    = 3;
+    my $not_cdata = 0;
+
+    # Set flags for situation where this function is first called and passed in a 
+    # language to handle.
+    #
+    if ($depth == 0 && $language) {
+       $translation_depth = 0;
+    }
+
+    $entered_tag[$depth + 1] = 0;
+
+    # Check to see if this is not a CDATA element.
+    #
+    foreach my $sub (@{ $ref }) {
+        if (ref $sub eq 'ARRAY') {
+            $not_cdata = 1;
+        }
     }
 
-    # FIXME: support attribute translations
+    foreach my $sub (@{ $ref }) {
 
-    # Empty nodes never need translation, so unmark all of them.
-    # For example, <_foo/> is just replaced by <foo/>.
-    $source =~ s|<\s*_($w+)\s*/>|<$1/>|g;
+        # Handle empty nodes.
+        #
+        if (! $sub || $sub =~ /^[\s]*$/) {
+            next;
+        }
 
-    for my $lang (sort keys %po_files_by_lang) 
-    {
-	my $source_copy = $source;
+        if (ref $sub eq 'ARRAY') {
 
-	open OUTPUT, ">$OUTFILE-$lang" or die;     
-	
-	# Support for <_foo>blah</_foo> style translations.
-	while ($source_copy =~ s|^(.*?)([ \t]*)<\s*_($w+)\s*>(.*?)<\s*/_\3\s*>([ \t]*\n)?||s) 
-	{
-	    print OUTPUT $1;
-	    
-	    my $spaces = $2;
-	    my $tag = $3;
-	    my $string = $4;
-	    
-	    $string =~ s/\s+/ /g;
-	    $string =~ s/^ //;
-	    $string =~ s/ $//;
-	    $string = entity_decode($string);
-	    
-	    my $translation = $translations{$lang, $string};
-
-	    if (!$translation)
-	    {
-		print OUTPUT "$spaces<$tag>$string</$tag>\n";
-	    }
-	    else
-	    {
-		$translation = entity_encode($translation);
-		
-		print OUTPUT "$spaces<$tag>$translation</$tag>\n";
-	    }
-	}
-	print OUTPUT $source_copy;
+            # Process subnodes
 
-	print "CREATED $OUTFILE-$lang\n";
-	close OUTPUT;
-    }
-}
+            # Check to see if current tag has any elements that need to be translated
+            #
+            if ($translation_depth == -1) {
+                foreach my $subsub (@{ $sub } ) {
+                    if (ref $subsub eq 'HASH') {
+                        foreach my $e (reverse(keys %{ $subsub })) {
+                            if ($e =~ /^_/) {
+                                $translation_depth = $depth;
+                            }
+                        }
+                    }
+                }
+            }
 
-sub xml_merge_single_output
-{
-    my $source;
+            my $current_tag = pop(@tag_stack);
+            push @tag_stack, $current_tag;
 
-    {
-       local $/; # slurp mode
-       open INPUT, "<$FILE" or die "can't open $FILE: $!";
-       $source = <INPUT>;
-       close INPUT;
-    }
+            $must_end_tag = $depth;
+            print $fh " " x ($last_depth * $indent), "<", $current_tag;
+
+            parseTree($fh, $sub, $depth + 1, $language);
 
-    open OUTPUT, ">$OUTFILE" or die;
+            # Close any open tags
+            #
+            if ($must_end_tag != -1) {
+                if ($must_end_tag < $depth) {
+                    print $fh ">\n";
+                } else {
+                    print $fh " />\n";
+                        pop(@tag_stack);
+                    if ($depth == $translation_depth) {
+                        $translation_depth = -1;
+                    }
+                }
+                $must_end_tag = -1;
+            }
 
-    # FIXME: support attribute translations
+            # Add ending tag(s), if needed
+            #
+            if ($entered_tag[$depth + 1] == 1) {
+                while ($last_depth > $depth) {
+                    $last_depth--;
+ 
+                    print $fh " " x ($last_depth * $indent), "</", pop(@tag_stack), ">\n";
+                }
+            }
+            $last_depth = $depth;
 
-    # Empty nodes never need translation, so unmark all of them.
-    # For example, <_foo/> is just replaced by <foo/>.
-    $source =~ s|<\s*_($w+)\s*/>|<$1/>|g;
+            # If beginning a translation block, then process for each language
+            #
+            if ($translation_depth == $depth) {
+
+                for my $lang (sort keys %po_files_by_lang) {
+
+                    print $fh " " x ($depth * $indent), "<", $current_tag, " xml:lang=\"", $lang, "\"";
+                    $must_end_tag = $depth;
+
+                    parseTree($fh, $sub, $depth + 1, $lang);
+
+                    # Close any open tags
+                    #
+                    if ($must_end_tag != -1) {
+                        if ($must_end_tag < $depth) {
+                            print $fh ">\n";
+                        } else {
+                            print $fh " />\n";
+                            pop(@tag_stack);
+                            if ($depth == $translation_depth) {
+                                $translation_depth = -1;
+                            }
+                        }
+                        $must_end_tag = -1;
+                    }
+
+                    # Add ending tag(s), if needed
+                    #
+                    if ($entered_tag[$depth + 1] == 1) {
+                        while ($last_depth > $depth + 1) {
+                            $last_depth--;
+     
+                            print $fh " " x ($last_depth * $indent), "</", pop(@tag_stack), ">\n";
+                        }
+                        print $fh " " x ($depth * $indent), "</", $current_tag, ">\n";
+                    }
+                }
+                $translation_depth = -1;
+                $last_depth = $depth;
+            }
 
-    # Support for <_foo>blah</_foo> style translations.
-    while ($source =~ s|^(.*?)([ \t]*)<\s*_($w+)\s*>(.*?)<\s*/_\3\s*>([ \t]*\n)?||s) 
-    {
-        print OUTPUT $1;
+        } elsif (ref $sub eq 'HASH') {
 
-        my $spaces = $2;
-        my $tag = $3;
-        my $string = $4;
-
-	print OUTPUT "$spaces<$tag>$string</$tag>\n";
-
-	$string =~ s/\s+/ /g;
-	$string =~ s/^ //;
-	$string =~ s/ $//;
-	$string = entity_decode($string);
+            # Process tag elements
+            #
+            foreach my $e (reverse(keys %{ $sub })) {
+                my $key    = $e;
+                my $string = $sub->{$e};
+
+                $string =~ s/\s+/ /g;
+                $string =~ s/^ //;
+                $string =~ s/ $//;
+                $string = entity_decode($string);
+
+                if ($key =~ /^_/) {
+                    $key =~ s|^_||g;
+
+                    if ($language) {
+
+                        # Handle translation
+                        #
+                        my $translation = $translations{$language, $string};
+                        if ($translation) {
+                            $translation = entity_encode($translation);
+                            $string = $translation;
+                        }
+                    }
+                }
 
-	for my $lang (sort keys %po_files_by_lang) 
-        {
-	    my $translation = $translations{$lang, $string};
-	    next if !$translation;
-	    $translation = entity_encode($translation);
-	    print OUTPUT "$spaces<$tag xml:lang=\"$lang\">$translation</$tag>\n";
+                print $fh " $key=\"$string\"";
+            }
+
+        } else {
+
+            # Handle tags and CDATA values
+
+            # Mark parent tag as having been entered.
+            #
+            $entered_tag[$depth] = 1;
+
+            # The last_depth flag allows us to determine if this tag should be
+            # closed with "/>" or ">"
+            #
+            $last_depth = $depth;
+
+            # Close any open tags
+            #
+            if ($must_end_tag != -1) {
+                if ($must_end_tag < $depth) {
+                    print $fh ">\n";
+                } else {
+                    print $fh " />\n";
+                    pop(@tag_stack);
+                }
+                $must_end_tag = -1;
+            }
+
+            if ($not_cdata) {
+
+                # Handle tags
+                #
+                my $temp_tag = $sub;
+
+                # Display key
+                #
+                if ($sub =~ /^_/) {
+
+                    $temp_tag =~ s|^_||g;
+
+                    if ($translation_depth == -1) {
+                        $translation_depth = $depth;
+                    }
+                }
+
+                # Push the tag on the stack, it will get handled when the ARRAY
+                # for this tag is processed.
+                #
+                push(@tag_stack, $temp_tag);
+
+            } else {
+
+                # Handle CDATA
+                #
+                my $string = $sub;
+
+                $string =~ s/\s+/ /g;
+                $string =~ s/^ //;
+                $string =~ s/ $//;
+                $string = entity_decode($string);
+
+                # Display CDATA
+                #
+                if ($language) {
+
+                    # Handle translation
+                    #
+                    my $translation = $translations{$language, $string};
+                    if ($translation) {
+                        $translation = entity_encode($translation);
+                        $string = $translation;
+                    }
+                }
+
+                print $fh " " x ($depth * $indent), $string, "\n";
+            }
         }
     }
+}
 
-    print OUTPUT $source;
+sub readXml
+{
+    my $filename = shift || return;
+    if(!-f $filename) {
+        die "ERROR Cannot find filename: $filename\n";
+    }
 
-    close OUTPUT;
+    my $xp = new XML::Parser(Style => 'Tree');
+    my $tree = $xp->parsefile($filename);
+
+# <foo><head id="a">Hello <em>there</em></head><bar>Howdy<ref/></bar>do</foo>
+# would be:
+# [foo, [{}, head, [{id => "a"}, 0, "Hello ",  em, [{}, 0, "there"]], bar, [{},
+# 0, "Howdy",  ref, [{}]], 0, "do" ] ]
+
+    return $tree;
+}
+
+sub xml_merge_output
+{
+    my $source;
+
+	if ($MULTIPLE_OUTPUT) {
+        for my $lang (sort keys %po_files_by_lang) {
+            open OUTPUT, ">$OUTFILE-$lang" or die "Cannot open $OUTFILE-$lang: $!\n";
+        	my $tree = readXml($FILE);
+        	parseTree(\*OUTPUT, $tree, 0, $lang);
+            close OUTPUT;
+            print "CREATED $OUTFILE-$lang\n";
+        }
+    } else {
+        open OUTPUT, ">$OUTFILE" or die "Cannot open $OUTFILE: $!\n";
+    	my $tree = readXml($FILE);
+    	parseTree(\*OUTPUT, $tree, 0);
+        close OUTPUT;
+        print "CREATED $OUTFILE\n";
+    }
 }
 
 sub keys_merge_translations
