# 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 () { 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;