3 # Copyright 2009 Oracle and/or its affiliates. All rights reserved.
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the "Software"),
7 # to deal in the Software without restriction, including without limitation
8 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 # and/or sell copies of the Software, and to permit persons to whom the
10 # Software is furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice (including the next
13 # paragraph) shall be included in all copies or substantial portions of the
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 # DEALINGS IN THE SOFTWARE.
26 # Check a compose file for duplicate/conflicting entries and other common errors
29 # Compose file grammar is defined in modules/im/ximcp/imLcPrs.c
36 if (scalar(@ARGV) == 0) {
38 push @ARGV, 'Compose';
40 push @ARGV, glob '*/Compose';
44 foreach my $cf (@ARGV) {
45 # print "Checking $cf\n";
46 $error_count += check_compose_file($cf);
51 sub check_compose_file {
55 my %compose_table = ();
57 my $pre_file = ($filename =~ m{\.pre$}) ? 1 : 0;
60 open my $COMPOSE, '<', $filename or die "Could not open $filename: $!";
63 while (my $cl = <$COMPOSE>) {
66 my $original_line = $cl;
68 # Special handling for changes cpp makes to .pre files
70 if ($in_c_comment) { # Look for end of multi-line C comment
71 if ($cl =~ m{\*/(.*)$}) {
78 $cl =~ s{/\*.\**/}{}; # Remove single line C comments
79 if ($cl =~ m{^(.*)/\*}) { # Start of a multi-line C comment
83 next if $cl =~ m{^\s*XCOMM}; # Skip pre-processing comments
86 $cl =~ s{#.*$}{}; # Remove comments
87 next if $cl =~ m{^\s*$}; # Skip blank (or comment-only) lines
90 if ($cl =~ m{^(STATE\s+|END_STATE)}) { # Sun extension to compose file syntax
93 elsif ($cl =~ m{^([^:]+)\s*:\s*(.+)$}) {
94 my ($seq, $action) = ($1, $2);
97 my @keys = grep { $_ !~ m/^\s*$/ } split /[\s\<\>]+/, $seq;
99 my $final_key = pop @keys;
100 my $keytable = \%compose_table;
102 foreach my $k (@keys) {
103 if ($k =~ m{^U([[:xdigit:]]+)$}) {
106 if (exists $keytable->{$k}) {
107 $keytable = $keytable->{$k};
108 if (ref($keytable) ne 'HASH') {
110 "Clash with existing sequence in $filename on line $line: $seq\n";
111 print_sequences([$line, $original_line]);
112 print_sequences($keytable);
117 my $new_keytable = {};
118 $keytable->{$k} = $new_keytable;
119 $keytable = $new_keytable;
123 if (exists $keytable->{$final_key}) {
124 print "Clash with existing sequence in $filename on line $line: $seq\n";
125 print_sequences([$line, $original_line]);
126 print_sequences($keytable->{$final_key});
129 $keytable->{$final_key} = [$line, $original_line];
131 } elsif ($cl =~ m{^(STATE_TYPE:|\@StartDeadKeyMap|\@EndDeadKeyMap)}) {
133 } elsif ($cl =~ m{^include "(.*)"}) {
135 if (($pre_file == 1) && ($incpath !~ m{^X11_LOCALEDATADIR/})) {
136 print "Include path starts with $incpath instead of X11_LOCALEDATADIR\n",
137 " -- may not find include files when installed in alternate paths\n\n";
140 print 'Unrecognized pattern in ', $filename, ' on line #', $line, ":\n ",
149 sub print_sequences {
150 my ($entry_ref) = @_;
152 if (ref($entry_ref) eq 'HASH') {
153 foreach my $h (values %{$entry_ref}) {
157 my ($line, $seq) = @{$entry_ref};
159 print " line #", $line, ": ", $seq, "\n";