1eb411b4bSmrg#! /usr/bin/perl 2eb411b4bSmrg# 35efbdfc3Smrg# Copyright (c) 2009, 2010, Oracle and/or its affiliates. 4eb411b4bSmrg# 5eb411b4bSmrg# Permission is hereby granted, free of charge, to any person obtaining a 6eb411b4bSmrg# copy of this software and associated documentation files (the "Software"), 7eb411b4bSmrg# to deal in the Software without restriction, including without limitation 8eb411b4bSmrg# the rights to use, copy, modify, merge, publish, distribute, sublicense, 9eb411b4bSmrg# and/or sell copies of the Software, and to permit persons to whom the 10eb411b4bSmrg# Software is furnished to do so, subject to the following conditions: 11eb411b4bSmrg# 12eb411b4bSmrg# The above copyright notice and this permission notice (including the next 13eb411b4bSmrg# paragraph) shall be included in all copies or substantial portions of the 14eb411b4bSmrg# Software. 15eb411b4bSmrg# 16eb411b4bSmrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17eb411b4bSmrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18eb411b4bSmrg# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19eb411b4bSmrg# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20eb411b4bSmrg# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21eb411b4bSmrg# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22eb411b4bSmrg# DEALINGS IN THE SOFTWARE. 23eb411b4bSmrg# 24eb411b4bSmrg 25eb411b4bSmrg# 26eb411b4bSmrg# Make a DocBook chart showing compose combinations for a locale 27eb411b4bSmrg# 28eb411b4bSmrg# See perldoc at end (or run with --help or --man options) for details 29eb411b4bSmrg# of command-line options. 30eb411b4bSmrg# 31eb411b4bSmrg 32eb411b4bSmrg# Compose file grammar is defined in modules/im/ximcp/imLcPrs.c 33eb411b4bSmrg 34eb411b4bSmrguse strict; 35eb411b4bSmrguse warnings; 36eb411b4bSmrguse Getopt::Long; 37eb411b4bSmrguse Pod::Usage; 38eb411b4bSmrg 39eb411b4bSmrgmy $error_count = 0; 40eb411b4bSmrg 41eb411b4bSmrgmy $charset; 42eb411b4bSmrgmy $locale_name; 43eb411b4bSmrgmy $output_filename = '-'; 44eb411b4bSmrgmy $man = 0; 45eb411b4bSmrgmy $help = 0; 46eb411b4bSmrgmy $make_index = 0; 47eb411b4bSmrg 48eb411b4bSmrgGetOptions ('charset:s' => \$charset, 49eb411b4bSmrg 'locale=s' => \$locale_name, 50eb411b4bSmrg 'output=s' => \$output_filename, 51eb411b4bSmrg 'index' => \$make_index, 52eb411b4bSmrg 'help|?' => \$help, 53eb411b4bSmrg 'man' => \$man) 54eb411b4bSmrg or pod2usage(2); 55eb411b4bSmrgpod2usage(1) if $help; 56eb411b4bSmrgpod2usage(-exitstatus => 0, -verbose => 2) if $man; 57eb411b4bSmrg 58eb411b4bSmrgif (!defined($charset) || ($charset eq "")) { 59eb411b4bSmrg if (defined($locale_name)) { 60eb411b4bSmrg my $guessed_charset = $locale_name; 61eb411b4bSmrg $guessed_charset =~ s{^.*\.}{}; 62eb411b4bSmrg if ($guessed_charset =~ m{^(utf-8|gbk|gb18030)$}i) { 63eb411b4bSmrg $charset = $1; 64eb411b4bSmrg } elsif ($guessed_charset =~ m{iso8859-(\d+)}i) { 65eb411b4bSmrg $charset = "iso-8859-$1"; 66eb411b4bSmrg } elsif ($guessed_charset =~ m{^microsoft-cp(125\d)$}) { 67eb411b4bSmrg $charset = "windows-$1"; 68eb411b4bSmrg } 69eb411b4bSmrg } 70eb411b4bSmrg if (!defined($charset) || ($charset eq "")) { 71eb411b4bSmrg $charset = "utf-8"; 72eb411b4bSmrg } 73eb411b4bSmrg} 74eb411b4bSmrg 75eb411b4bSmrgif ($make_index) { 76eb411b4bSmrg # Print Docbook output 77eb411b4bSmrg open my $OUTPUT, '>', $output_filename 78eb411b4bSmrg or die "Could not create $output_filename: $!"; 79eb411b4bSmrg 80eb411b4bSmrg print $OUTPUT 81eb411b4bSmrg join ("\n", 82eb411b4bSmrg qq(<?xml version="1.0" encoding="$charset" ?>), 83eb411b4bSmrg q(<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"), 84eb411b4bSmrg q( "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">), 85eb411b4bSmrg q(<article id="libX11-keys">), 86eb411b4bSmrg q( <articleinfo>), 87eb411b4bSmrg q( <title>Xlib Compose Key Charts</title>), 88eb411b4bSmrg q( </articleinfo>), 89eb411b4bSmrg ( map { qq( <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="$_.xml">\ 90eb411b4bSmrg <xi:fallback><section><title>$_</title><para></para></section></xi:fallback>\ 91eb411b4bSmrg </xi:include>) } 92eb411b4bSmrg @ARGV ), 93eb411b4bSmrg q(</article>), 94eb411b4bSmrg "\n" 95eb411b4bSmrg ); 96eb411b4bSmrg 97eb411b4bSmrg close $OUTPUT or die "Couldn't write $output_filename: $!"; 98eb411b4bSmrg 99eb411b4bSmrg exit(0); 100eb411b4bSmrg} 101eb411b4bSmrg 102eb411b4bSmrgforeach my $a (@ARGV) { 103eb411b4bSmrg $error_count += make_compose_chart($a); 104eb411b4bSmrg} 105eb411b4bSmrg 106eb411b4bSmrgexit($error_count); 107eb411b4bSmrg 108eb411b4bSmrgsub make_compose_chart { 109eb411b4bSmrg my ($filename) = @_; 110eb411b4bSmrg my $errors = 0; 111eb411b4bSmrg 112eb411b4bSmrg my @compose_table = (); 113eb411b4bSmrg my @included_files = (); 114eb411b4bSmrg 115eb411b4bSmrg my $line = 0; 116eb411b4bSmrg my $pre_file = ($filename =~ m{\.pre$}) ? 1 : 0; 117eb411b4bSmrg my $in_c_comment = 0; 118eb411b4bSmrg my $in_comment = 0; 119eb411b4bSmrg my $keyseq_count = 0; 120eb411b4bSmrg 121eb411b4bSmrg open my $COMPOSE, '<', $filename or die "Could not open $filename: $!"; 122eb411b4bSmrg 123eb411b4bSmrg COMPOSE_LINE: 124eb411b4bSmrg while (my $cl = <$COMPOSE>) { 125eb411b4bSmrg $line++; 126eb411b4bSmrg chomp($cl); 127eb411b4bSmrg my $original_line = $cl; 128eb411b4bSmrg 129eb411b4bSmrg # Special handling for changes cpp makes to .pre files 130eb411b4bSmrg if ($pre_file == 1) { 131eb411b4bSmrg if ($in_c_comment) { # Look for end of multi-line C comment 132eb411b4bSmrg if ($cl =~ m{\*/(.*)$}) { 133eb411b4bSmrg $cl = $1; 134eb411b4bSmrg $in_c_comment = 0; 135eb411b4bSmrg } else { 136eb411b4bSmrg next; 137eb411b4bSmrg } 138eb411b4bSmrg } 139eb411b4bSmrg $cl =~ s{/\*.\**/}{}; # Remove single line C comments 140eb411b4bSmrg if ($cl =~ m{^(.*)/\*}) { # Start of a multi-line C comment 141eb411b4bSmrg $cl = $1; 142eb411b4bSmrg $in_c_comment = 1; 143eb411b4bSmrg } 144eb411b4bSmrg $cl =~ s{^\s*XCOMM}{#}; # Translate pre-processing comments 145eb411b4bSmrg } 146eb411b4bSmrg 147eb411b4bSmrg chomp($cl); 148eb411b4bSmrg 149eb411b4bSmrg if ($cl =~ m{^\s*#\s*(.*)$}) { # Comment only lines 1509c019ec5Smaya # Combine comment blocks 151eb411b4bSmrg my $comment = $1; 152eb411b4bSmrg 153eb411b4bSmrg if ($in_comment) { 154eb411b4bSmrg my $prev_comment = pop @compose_table; 155eb411b4bSmrg $comment = join(' ', $prev_comment->{-comment}, $comment); 156eb411b4bSmrg } else { 157eb411b4bSmrg $in_comment = 1; 158eb411b4bSmrg } 159eb411b4bSmrg 160eb411b4bSmrg push @compose_table, { -type => 'comment', -comment => $comment }; 161eb411b4bSmrg next COMPOSE_LINE; 162eb411b4bSmrg } 163eb411b4bSmrg 164eb411b4bSmrg $in_comment = 0; 165eb411b4bSmrg 166eb411b4bSmrg if ($cl =~ m{^\s*$}) { # Skip blank lines 167eb411b4bSmrg next COMPOSE_LINE; 168eb411b4bSmrg } 169eb411b4bSmrg elsif ($cl =~ m{^(STATE\s+|END_STATE)}) { 170eb411b4bSmrg # Sun extension to compose file syntax 171eb411b4bSmrg next COMPOSE_LINE; 172eb411b4bSmrg } 173eb411b4bSmrg elsif ($cl =~ m{^([^:]+)\s*:\s*(.+)$}) { 174eb411b4bSmrg my ($seq, $action) = ($1, $2); 175eb411b4bSmrg $seq =~ s{\s+$}{}; 176eb411b4bSmrg 177eb411b4bSmrg my @keys = grep { $_ !~ m/^\s*$/ } split /[\s\<\>]+/, $seq; 178eb411b4bSmrg 179eb411b4bSmrg push @compose_table, { 180eb411b4bSmrg -type => 'keyseq', 181eb411b4bSmrg -keys => [ @keys ], 182eb411b4bSmrg -action => $action 183eb411b4bSmrg }; 184eb411b4bSmrg $keyseq_count++; 185eb411b4bSmrg next COMPOSE_LINE; 186eb411b4bSmrg } elsif ($cl =~ m{^(STATE_TYPE:|\@StartDeadKeyMap|\@EndDeadKeyMap)}) { 187eb411b4bSmrg # ignore 188eb411b4bSmrg next COMPOSE_LINE; 189eb411b4bSmrg } elsif ($cl =~ m{^include "(.*)"}) { 190eb411b4bSmrg my $incpath = $1; 191eb411b4bSmrg $incpath =~ s{^X11_LOCALEDATADIR/(.*)/Compose}{the $1 compose table}; 192eb411b4bSmrg 193eb411b4bSmrg push @included_files, $incpath; 194eb411b4bSmrg next COMPOSE_LINE; 195eb411b4bSmrg } else { 196eb411b4bSmrg print STDERR ('Unrecognized pattern in ', $filename, 197eb411b4bSmrg ' on line #', $line, ":\n ", $cl, "\n"); 198eb411b4bSmrg } 199eb411b4bSmrg } 200eb411b4bSmrg close $COMPOSE; 201eb411b4bSmrg 202eb411b4bSmrg if ($errors > 0) { 203eb411b4bSmrg return $errors; 204eb411b4bSmrg } 205eb411b4bSmrg 206eb411b4bSmrg # Print Docbook output 207eb411b4bSmrg open my $OUTPUT, '>', $output_filename 208eb411b4bSmrg or die "Could not create $output_filename: $!"; 209eb411b4bSmrg 210eb411b4bSmrg print $OUTPUT 211eb411b4bSmrg join ("\n", 212eb411b4bSmrg qq(<?xml version="1.0" encoding="$charset" ?>), 213eb411b4bSmrg q(<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"), 214eb411b4bSmrg q( "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">), 215eb411b4bSmrg qq(<section id="$locale_name">), 216eb411b4bSmrg qq(<title>Xlib Compose Keys for $locale_name</title>), 217eb411b4bSmrg q(<para>Applications using Xlib input handling should recognize), 218eb411b4bSmrg q( these compose key sequences in locales using the), 219eb411b4bSmrg qq( $locale_name compose table.</para>), 220eb411b4bSmrg "\n" 221eb411b4bSmrg ); 222eb411b4bSmrg 223eb411b4bSmrg if (@included_files) { 224eb411b4bSmrg print $OUTPUT 225eb411b4bSmrg q(<para>This compose table includes the non-conflicting), 226eb411b4bSmrg q( entries from: ), 227eb411b4bSmrg join(',', @included_files), 228eb411b4bSmrg q(. Those entries are not shown here - see those charts for the), 229eb411b4bSmrg q( included key sequences.</para>), 230eb411b4bSmrg "\n"; 231eb411b4bSmrg } 232eb411b4bSmrg 233eb411b4bSmrg my @pretable_comments = (); 234eb411b4bSmrg 235eb411b4bSmrg if ($keyseq_count == 0) { 236eb411b4bSmrg @pretable_comments = @compose_table; 237eb411b4bSmrg } elsif ($compose_table[0]->{-type} eq 'comment') { 238eb411b4bSmrg push @pretable_comments, shift @compose_table; 239eb411b4bSmrg } 240eb411b4bSmrg 241eb411b4bSmrg foreach my $comment_ref (@pretable_comments) { 242eb411b4bSmrg print $OUTPUT 243eb411b4bSmrg qq(<para>), xml_escape($comment_ref->{-comment}), qq(</para>\n); 244eb411b4bSmrg } 245eb411b4bSmrg 246eb411b4bSmrg if ($keyseq_count > 0) { 247eb411b4bSmrg start_table($OUTPUT); 248eb411b4bSmrg my $row_count = 0; 249eb411b4bSmrg 250eb411b4bSmrg foreach my $cr (@compose_table) { 251eb411b4bSmrg 252eb411b4bSmrg if ($row_count++ > 750) { 253eb411b4bSmrg # Break tables every 750 rows to avoid overflowing 254eb411b4bSmrg # xmlto/xsltproc limits on the largest tables 255eb411b4bSmrg end_table($OUTPUT); 256eb411b4bSmrg start_table($OUTPUT); 257eb411b4bSmrg $row_count = 0; 258eb411b4bSmrg } 259eb411b4bSmrg 260eb411b4bSmrg if ($cr->{-type} eq 'comment') { 261eb411b4bSmrg print $OUTPUT 262eb411b4bSmrg qq(<row><entry namest='seq' nameend='action'>), 263eb411b4bSmrg xml_escape($cr->{-comment}), qq(</entry></row>\n); 264eb411b4bSmrg } elsif ($cr->{-type} eq 'keyseq') { 265eb411b4bSmrg my $action = join(" ", xml_escape($cr->{-action})); 266eb411b4bSmrg if ($action =~ m{^\s*"\\([0-7]+)"}) { 267eb411b4bSmrg my $char = oct($1); 268eb411b4bSmrg if ($char >= 32) { 269eb411b4bSmrg $action =~ s{^\s*"\\[0-7]+"}{"&#$char;"}; 270eb411b4bSmrg } 271eb411b4bSmrg } 272eb411b4bSmrg $action =~ s{^\s*"(.+)"}{"$1"}; 273eb411b4bSmrg 274eb411b4bSmrg print $OUTPUT 275eb411b4bSmrg qq(<row><entry>), 276eb411b4bSmrg qq(<keycombo action='seq'>), 277eb411b4bSmrg (map { qq(<keysym>$_</keysym>) } xml_escape(@{$cr->{-keys}})), 278eb411b4bSmrg qq(</keycombo>), 279eb411b4bSmrg qq(</entry><entry>), 280eb411b4bSmrg $action, 281eb411b4bSmrg qq(</entry></row>\n); 282eb411b4bSmrg } 283eb411b4bSmrg } 284eb411b4bSmrg 285eb411b4bSmrg end_table($OUTPUT); 286eb411b4bSmrg } else { 287eb411b4bSmrg print $OUTPUT 288eb411b4bSmrg qq(<para><emphasis>), 289eb411b4bSmrg qq(This compose table defines no sequences of its own.), 290eb411b4bSmrg qq(</emphasis></para>\n); 291eb411b4bSmrg } 292eb411b4bSmrg print $OUTPUT "</section>\n"; 293eb411b4bSmrg 294eb411b4bSmrg close $OUTPUT or die "Couldn't write $output_filename: $!"; 295eb411b4bSmrg 296eb411b4bSmrg return $errors; 297eb411b4bSmrg} 298eb411b4bSmrg 299eb411b4bSmrgsub xml_escape { 300eb411b4bSmrg my @output; 301eb411b4bSmrg 302eb411b4bSmrg foreach my $l (@_) { 303eb411b4bSmrg $l =~ s{\&}{&}g; 304eb411b4bSmrg $l =~ s{\<}{<}g; 305eb411b4bSmrg $l =~ s{\>}{>}g; 306eb411b4bSmrg push @output, $l; 307eb411b4bSmrg } 308eb411b4bSmrg return @output; 309eb411b4bSmrg} 310eb411b4bSmrg 311eb411b4bSmrgsub start_table { 312eb411b4bSmrg my ($OUTPUT) = @_; 313eb411b4bSmrg 314eb411b4bSmrg print $OUTPUT 315eb411b4bSmrg join("\n", 316eb411b4bSmrg qq(<table><title>Compose Key Sequences for $locale_name</title>), 317eb411b4bSmrg qq(<tgroup cols='2'>), 318eb411b4bSmrg qq( <colspec colname='seq' /><colspec colname='action' />), 319eb411b4bSmrg qq( <thead><row>), 320eb411b4bSmrg qq( <entry>Key Sequence</entry><entry>Action</entry>), 321eb411b4bSmrg qq( </row></thead>), 322eb411b4bSmrg qq( <tbody>\n), 323eb411b4bSmrg ); 324eb411b4bSmrg} 325eb411b4bSmrg 326eb411b4bSmrgsub end_table { 327eb411b4bSmrg my ($OUTPUT) = @_; 328eb411b4bSmrg 329eb411b4bSmrg print $OUTPUT "</tbody>\n</tgroup>\n</table>\n"; 330eb411b4bSmrg} 331eb411b4bSmrg 332eb411b4bSmrg__END__ 333eb411b4bSmrg 334eb411b4bSmrg=head1 NAME 335eb411b4bSmrg 336eb411b4bSmrgcompose-chart - Make DocBook/XML charts of compose table entries 337eb411b4bSmrg 338eb411b4bSmrg=head1 SYNOPSIS 339eb411b4bSmrg 340eb411b4bSmrgcompose-chart [options] [file ...] 341eb411b4bSmrg 342eb411b4bSmrg Options: 343eb411b4bSmrg --charset[=<cset>] character set to specify in XML doctype 344eb411b4bSmrg --locale=<locale> name of locale to display in chart 345eb411b4bSmrg --output=<file> filename to output chart to 346eb411b4bSmrg --index make index of charts instead of individual chart 347eb411b4bSmrg --help brief help message 348eb411b4bSmrg --man full documentation 349eb411b4bSmrg 350eb411b4bSmrg=head1 OPTIONS 351eb411b4bSmrg 352eb411b4bSmrg=over 8 353eb411b4bSmrg 354eb411b4bSmrg=item B<--charset>[=I<cset>] 355eb411b4bSmrg 356eb411b4bSmrgSpecify a character set to list in the doctype declaration in the XML output. 357eb411b4bSmrgIf not specified, attempts to guess from the locale name, else default to 358eb411b4bSmrg"utf-8". 359eb411b4bSmrg 360eb411b4bSmrg=item B<--locale>=I<locale> 361eb411b4bSmrg 362eb411b4bSmrgSpecify the locale name to use in the chart titles and introductory text. 363eb411b4bSmrg 364eb411b4bSmrg=item B<--output>=I<file> 365eb411b4bSmrg 366eb411b4bSmrgSpecify the output file to write the DocBook output to. 367eb411b4bSmrg 368eb411b4bSmrg=item B<--index> 369eb411b4bSmrg 370eb411b4bSmrgGenerate an index of the listed locale charts instead of a chart for a 371eb411b4bSmrgspecific locale. 372eb411b4bSmrg 373eb411b4bSmrg=item B<--help> 374eb411b4bSmrg 375eb411b4bSmrgPrint a brief help message and exit. 376eb411b4bSmrg 377eb411b4bSmrg=item B<--man> 378eb411b4bSmrg 379eb411b4bSmrgPrint the manual page and exit. 380eb411b4bSmrg 381eb411b4bSmrg=back 382eb411b4bSmrg 383eb411b4bSmrg=head1 DESCRIPTION 384eb411b4bSmrg 385eb411b4bSmrgThis program will read the given compose table file(s) and generate 386eb411b4bSmrgDocBook/XML charts listing the available characters for end-user reference. 387eb411b4bSmrg 388eb411b4bSmrg=cut 389