| # Copyright (C) 2006, 2007, 2009, 2010, 2013, 2015 Apple Inc. All rights reserved. | 
 | # | 
 | # Redistribution and use in source and binary forms, with or without | 
 | # modification, are permitted provided that the following conditions | 
 | # are met: | 
 | # 1. Redistributions of source code must retain the above copyright | 
 | #    notice, this list of conditions and the following disclaimer. | 
 | # 2. Redistributions in binary form must reproduce the above copyright | 
 | #    notice, this list of conditions and the following disclaimer in the | 
 | #    documentation and/or other materials provided with the distribution. | 
 | # | 
 | # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 
 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | 
 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 
 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | 
 | # THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | use strict; | 
 |  | 
 | my $treatWarningsAsErrors = 0; | 
 |  | 
 | sub setTreatWarningsAsErrors($) | 
 | { | 
 |     ($treatWarningsAsErrors) = @_; | 
 | } | 
 |  | 
 | my $sawError = 0; | 
 |  | 
 | sub sawError() | 
 | { | 
 |     return $sawError; | 
 | } | 
 |  | 
 | sub emitError($$$) | 
 | { | 
 |     my ($file, $line, $message) = @_; | 
 |     print "$file:$line: $message\n"; | 
 |     $sawError = 1; | 
 | } | 
 |  | 
 | sub emitWarning($$$) | 
 | { | 
 |     my ($file, $line, $message) = @_; | 
 |     my $prefix = $treatWarningsAsErrors ? "" : "warning: "; | 
 |     print "$file:$line: $prefix$message\n"; | 
 |     $sawError = 1 if $treatWarningsAsErrors; | 
 | } | 
 |  | 
 | # Unescapes C language hexadecimal escape sequences. | 
 | sub unescapeHexSequence($) | 
 | { | 
 |     my ($originalStr) = @_; | 
 |  | 
 |     my $escapedStr = $originalStr; | 
 |     my $unescapedStr = ""; | 
 |  | 
 |     for (;;) { | 
 |         if ($escapedStr =~ s-^\\x([[:xdigit:]]+)--) { | 
 |             if (256 <= hex($1)) { | 
 |                 print "Hexadecimal escape sequence out of range: \\x$1\n"; | 
 |                 return undef; | 
 |             } | 
 |             $unescapedStr .= pack("H*", $1); | 
 |         } elsif ($escapedStr =~ s-^(.)--) { | 
 |             $unescapedStr .= $1; | 
 |         } else { | 
 |             return $unescapedStr; | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | my $keyCollisionCount = 0; | 
 |  | 
 | sub keyCollisionCount() | 
 | { | 
 |     return $keyCollisionCount; | 
 | } | 
 |  | 
 | my $localizedCount = 0; | 
 |  | 
 | sub localizedCount() | 
 | { | 
 |     return $localizedCount; | 
 | } | 
 |  | 
 | my %stringByKey; | 
 | my %commentByKey; | 
 | my %fileByKey; | 
 | my %lineByKey; | 
 |  | 
 | sub HandleUIString | 
 | { | 
 |     my ($string, $key, $comment, $file, $line) = @_; | 
 |  | 
 |     $localizedCount++; | 
 |  | 
 |     my $bad = 0; | 
 |     $string = unescapeHexSequence($string); | 
 |     if (!defined($string)) { | 
 |         print "$file:$line: string has an illegal hexadecimal escape sequence\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     $key = unescapeHexSequence($key); | 
 |     if (!defined($key)) { | 
 |         print "$file:$line: key has an illegal hexadecimal escape sequence\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     $comment = unescapeHexSequence($comment); | 
 |     if (!defined($comment)) { | 
 |         print "$file:$line: comment has an illegal hexadecimal escape sequence\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     if (grep { $_ == 0xFFFD } unpack "U*", $string) { | 
 |         print "$file:$line: string for translation has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     if ($string ne $key && grep { $_ == 0xFFFD } unpack "U*", $key) { | 
 |         print "$file:$line: key has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     if (grep { $_ == 0xFFFD } unpack "U*", $comment) { | 
 |         print "$file:$line: comment for translation has illegal UTF-8 -- most likely a problem with the Text Encoding of the source file\n"; | 
 |         $bad = 1; | 
 |     } | 
 |     if ($bad) { | 
 |         $sawError = 1; | 
 |         return; | 
 |     } | 
 |      | 
 |     if ($stringByKey{$key} && $stringByKey{$key} ne $string) { | 
 |         emitWarning($file, $line, "encountered the same key, \"$key\", twice, with different strings"); | 
 |         emitWarning($fileByKey{$key}, $lineByKey{$key}, "previous occurrence"); | 
 |         $keyCollisionCount++; | 
 |         return; | 
 |     } | 
 |     if ($commentByKey{$key} && $commentByKey{$key} ne $comment) { | 
 |         emitWarning($file, $line, "encountered the same key, \"$key\", twice, with different comments"); | 
 |         emitWarning($fileByKey{$key}, $lineByKey{$key}, "previous occurrence"); | 
 |         $keyCollisionCount++; | 
 |         return; | 
 |     } | 
 |  | 
 |     $fileByKey{$key} = $file; | 
 |     $lineByKey{$key} = $line; | 
 |     $stringByKey{$key} = $string; | 
 |     $commentByKey{$key} = $comment; | 
 | } | 
 |  | 
 | sub writeStringsFile($) | 
 | { | 
 |     my ($file) = @_; | 
 |  | 
 |     my $localizedStrings = ""; | 
 |  | 
 |     for my $key (sort keys %commentByKey) { | 
 |         $localizedStrings .= "/* $commentByKey{$key} */\n\"$key\" = \"$stringByKey{$key}\";\n\n"; | 
 |     } | 
 |  | 
 |     # Write out the strings file as UTF-8 | 
 |     open STRINGS, ">", $file or die; | 
 |     print STRINGS $localizedStrings; | 
 |     close STRINGS; | 
 | } | 
 |  | 
 | sub verifyStringsFile($) | 
 | { | 
 |     my ($file) = @_; | 
 |  | 
 |     open STRINGS, $file or die; | 
 |  | 
 |     my $lastComment; | 
 |     my $line; | 
 |     my $sawError; | 
 |  | 
 |     while (<STRINGS>) { | 
 |         chomp; | 
 |  | 
 |         next if (/^\s*$/); | 
 |  | 
 |         if (/^\/\* (.*) \*\/$/) { | 
 |             $lastComment = $1; | 
 |         } elsif (/^"((?:[^\\]|\\[^"])*)"\s*=\s*"((?:[^\\]|\\[^"])*)";$/) # | 
 |         { | 
 |             my $string = delete $stringByKey{$1}; | 
 |             if (!defined $string) { | 
 |                 print "$file:$.: unused key \"$1\"\n"; | 
 |                 $sawError = 1; | 
 |             } else { | 
 |                 if (!($string eq $2)) { | 
 |                     print "$file:$.: unexpected value \"$2\" for key \"$1\"\n"; | 
 |                     print "$fileByKey{$1}:$lineByKey{$1}: expected value \"$string\" defined here\n"; | 
 |                     $sawError = 1; | 
 |                 } | 
 |                 if (!($lastComment eq $commentByKey{$1})) { | 
 |                     print "$file:$.: unexpected comment /* $lastComment */ for key \"$1\"\n"; | 
 |                     print "$fileByKey{$1}:$lineByKey{$1}: expected comment /* $commentByKey{$1} */ defined here\n"; | 
 |                     $sawError = 1; | 
 |                 } | 
 |             } | 
 |         } else { | 
 |             print "$file:$.: line with unexpected format: $_\n"; | 
 |             $sawError = 1; | 
 |         } | 
 |     } | 
 |  | 
 |     for my $missing (keys %stringByKey) { | 
 |         print "$fileByKey{$missing}:$lineByKey{$missing}: missing key \"$missing\"\n"; | 
 |         $sawError = 1; | 
 |     } | 
 |  | 
 |     if ($sawError) { | 
 |         print "\n$file:0: file is not up to date.\n"; | 
 |         exit 1; | 
 |     } | 
 | } | 
 |  | 
 | 1; |