1#! /usr/bin/env perl
2
3# gpinyin - European-like Chinese writing 'pinyin' into 'groff'
4
5# Source file position: <groff-source>/contrib/gpinyin/gpinyin.pl
6# Installed position: <prefix>/bin/gpinyin
7
8# Copyright (C) 2014-2018 Free Software Foundation, Inc.
9
10# Written by Bernd Warken <groff-bernd.warken-72@web.de>.
11
12# This file is part of 'gpinyin', which is part of 'groff'.
13
14# 'groff' is free software; you can redistribute it and/or modify it
15# under the terms of the GNU General Public License as published by
16# the Free Software Foundation, either version 2 of the License, or
17# (at your option) any later version.
18
19# 'groff' is distributed in the hope that it will be useful, but
20# WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22# General Public License for more details.
23
24# You can find a copy of the GNU General Public License in the internet
25# at <http://www.gnu.org/licenses/gpl-2.0.html>.
26
27########################################################################
28
29########################################################################
30# All Pinyin syllables from wikipedia
31########################################################################
32
33my %syllables =
34  (
35   'a' => 1, 'ai' => 1, 'an' => 1, 'ang' => 1, 'ao' => 1,
36   'ba' => 1, 'bai' => 1, 'ban' => 1, 'bang' => 1, 'bao' => 1,
37   'bei' => 1, 'ben' => 1, 'beng' => 1,
38   'bi' => 1, 'bian' => 1, 'biao' => 1, 'bie' => 1, 'bin' => 1,
39   'bing' => 1, 'bo' => 1, 'bu' => 1,
40   'ca' => 1, 'cai' => 1, 'can' => 1, 'cang' => 1, 'cao' => 1,
41   'ce' => 1, 'cen' => 1, 'ceng' => 1,
42   'cha' => 1, 'chai' => 1, 'chan' => 1, 'chang' => 1, 'chao' => 1,
43   'che' => 1, 'chen' => 1, 'cheng' => 1, 'chi' => 1,
44   'chong' => 1, 'chou' => 1, 'chu' => 1,
45   'chua' => 1, 'chuai' => 1, 'chuan' => 1, 'chuang' => 1,
46   'chui' => 1, 'chun' => 1, 'chuo' => 1,
47   'ci' => 1, 'cong' => 1, 'cou' => 1,
48   'cu' => 1, 'cuan' => 1, 'cui' => 1, 'cun' => 1, 'cuo' => 1,
49   'da' => 1, 'dai' => 1, 'dan' => 1, 'dang' => 1, 'dao' => 1,
50   'de' => 1, 'dei' => 1, 'den' => 1, 'deng' => 1,
51   'di' => 1, 'dian' => 1, 'diao' => 1, 'die' => 1,
52   'ding' => 1, 'diu' => 1, 'dong' => 1, 'dou' => 1,
53   'du' => 1, 'duan' => 1, 'dui' => 1, 'dun' => 1, 'duo' => 1,
54   'e' => 1, 'ei' => 1, 'en' => 1,  'eng' => 1, 'er' => 1,
55   'fa' => 1, 'fan' => 1, 'fang' => 1,
56   'fei' => 1, 'fen' => 1, 'feng' => 1, 'fiao' => 1,
57   'fo' => 1, 'fou' => 1, 'fu' => 1,
58   'ga' => 1, 'gai' => 1, 'gan' => 1, 'gang' => 1, 'gao' => 1,
59   'ge' => 1, 'gei' => 1, 'gen' => 1, 'geng' => 1,
60   'gong' => 1, 'gou' => 1, 'gu' => 1,
61   'gua' => 1, 'guai' => 1, 'guan' => 1, 'guang' => 1, 'gui' => 1,
62   'gun' => 1, 'guo' => 1,
63   'ha' => 1, 'hai' => 1, 'han' => 1, 'hang' => 1, 'hao' => 1,
64   'he' => 1, 'hei' => 1, 'hen' => 1, 'heng' => 1,
65   'hong' => 1, 'hou' => 1,
66   'hu' => 1, 'hua' => 1, 'huai' => 1, 'huan' => 1, 'huang' => 1,
67   'hui' => 1, 'hun' => 1, 'huo' => 1,
68   'ji' => 1, 'jia' => 1, 'jian' => 1, 'jiang' => 1, 'jiao' => 1,
69   'jie' => 1, 'jin' => 1, 'jing' => 1, 'jiong' => 1, 'jiu' => 1,
70   'ju' => 1, 'juan' => 1, 'jue' => 1, 'jun' => 1,
71   'ka' => 1, 'kai' => 1, 'kan' => 1, 'kang' => 1, 'kao' => 1,
72   'ke' => 1, 'kei' => 1, 'ken' => 1, 'keng' => 1,
73   'kong' => 1, 'kou' => 1, 'ku' => 1, 'kua' => 1, 'kuai' => 1,
74   'kuan' => 1, 'kuang' => 1, 'kui' => 1, 'kun' => 1, 'kuo' => 1,
75   'la' => 1, 'lai' => 1, 'lan' => 1, 'lang' => 1, 'lao' => 1,
76   'le' => 1, 'lei' => 1, 'leng' => 1,
77   'li' => 1, 'lia' => 1, 'lian' => 1, 'liang' => 1, 'liao' => 1,
78   'lie' => 1, 'lin' => 1, 'ling' => 1, 'liu' => 1,
79   'lo' => 1, 'long' => 1, 'lou' => 1,
80   'lu' => 1, 'luan' => 1, 'lun' => 1, 'luo' => 1,
81   'lü' => 1, 'lüe' => 1,
82   'ma' => 1, 'mai' => 1, 'man' => 1, 'mang' => 1, 'mao' => 1,
83   'me' => 1, 'mei' => 1, 'men' => 1, 'meng' => 1,
84   'mi' => 1, 'mian' => 1, 'miao' => 1, 'mie' => 1,
85   'min' => 1, 'ming' => 1, 'miu' => 1,
86   'mo' => 1, 'mou' => 1, 'mu' => 1,
87   'na' => 1, 'nai' => 1, 'nan' => 1, 'nang' => 1, 'nao' => 1,
88   'ne' => 1, 'nei' => 1, 'nen' => 1, 'neng' => 1,
89   'ni' => 1, 'nian' => 1, 'niang' => 1, 'niao' => 1, 'nie' => 1,
90   'nin' => 1, 'ning' => 1, 'niu' => 1, 'nong' => 1, 'nou' => 1,
91   'nu' => 1, 'nuan' => 1, 'nun' => 1, 'nuo' => 1,
92   'nü' => 1, 'nüe' => 1,
93   'o' => 1, 'ong' => 1, 'ou' => 1,
94   'pa' => 1, 'pai' => 1, 'pan' => 1, 'pang' => 1, 'pao' => 1,
95   'pei' => 1, 'pen' => 1, 'peng' => 1,
96   'pi' => 1, 'pian' => 1, 'piao' => 1, 'pie' => 1, 'pin' => 1,
97   'ping' => 1, 'po' => 1, 'pou' => 1, 'pu' => 1,
98   'qi' => 1, 'qia' => 1, 'qian' => 1, 'qiang' => 1, 'qiao' => 1, 'qie' => 1,
99   'qin' => 1, 'qing' => 1, 'qiong' => 1, 'qiu' => 1,
100   'qu' => 1, 'quan' => 1, 'que' => 1, 'qun' => 1,
101   'ran' => 1, 'rang' => 1, 'rao' => 1, 're' => 1, 'ren' => 1,
102   'ri' => 1, 'rong' => 1, 'rou' => 1,
103   'ru' => 1, 'ruan' => 1, 'rui' => 1, 'run' => 1, 'ruo' => 1,
104   'sa' => 1, 'sai' => 1, 'san' => 1, 'sang' => 1, 'sao' => 1,
105   'se' => 1, 'sen' => 1, 'seng' => 1,
106   'sha' => 1, 'shai' => 1, 'shan' => 1, 'shang' => 1, 'shao' => 1,
107   'she' => 1, 'shei' => 1, 'shen' => 1, 'sheng' => 1, 'shi' => 1,
108   'shou' => 1, 'shu' => 1, 'shua' => 1, 'shuai' => 1, 'shuan' => 1,
109   'shuang' => 1, 'shui' => 1, 'shun' => 1, 'shuo' => 1,
110   'si' => 1, 'song' => 1, 'sou' => 1, 'su' => 1, 'suan' => 1, 'sui' => 1,
111   'sun' => 1, 'suo' => 1,
112   'ta' => 1, 'tai' => 1, 'tan' => 1, 'tang' => 1, 'tao' => 1,
113   'te' => 1, 'teng' => 1,
114   'ti' => 1, 'tian' => 1, 'tiao' => 1, 'tie' => 1, 'ting' => 1,
115   'tong' => 1, 'tou' => 1,
116   'tu' => 1, 'tuan' => 1, 'tui' => 1, 'tun' => 1, 'tuo' => 1,
117   'wa' => 1, 'wai' => 1, 'wan' => 1, 'wang' => 1,
118   'wei' => 1, 'wen' => 1, 'weng' => 1, 'wo' => 1, 'wu' => 1,
119   'xi' => 1, 'xia' => 1, 'xian' => 1, 'xiang' => 1, 'xiao' => 1,
120   'xie' => 1, 'xin' => 1, 'xing' => 1, 'xiong' => 1, 'xiu' => 1,
121   'xu' => 1, 'xuan' => 1, 'xue' => 1, 'xun' => 1,
122   'ya' => 1, 'yai' => 1, 'yan' => 1, 'yang' => 1, 'yao' => 1,
123   'ye' => 1, 'yi' => 1, 'yin' => 1, 'ying' => 1,
124   'yo' => 1, 'yong' => 1, 'you' => 1,
125   'yu' => 1, 'yuan' => 1, 'yue' => 1, 'yun' => 1,
126   'za' => 1, 'zai' => 1, 'zan' => 1, 'zang' => 1, 'zao' => 1,
127   'ze' => 1, 'zei' => 1, 'zen' => 1, 'zeng' => 1,
128   'zha' => 1, 'zhai' => 1, 'zhan' => 1, 'zhang' => 1, 'zhao' => 1,
129   'zhe' => 1, 'zhei' => 1, 'zhen' => 1, 'zheng' => 1, 'zhi' => 1,
130   'zhong' => 1, 'zhou' => 1, 'zhu' => 1, 'zhua' => 1, 'zhuai' => 1,
131   'zhuan' => 1, 'zhuang' => 1, 'zhui' => 1, 'zhun' => 1, 'zhuo' => 1,
132   'zi' => 1, 'zong' => 1, 'zou' => 1, 'zu' => 1, 'zuan' => 1,
133   'zui' => 1, 'zun' => 1, 'zuo' => 1,
134  );
135
136########################################################################
137# Unicode variables for utf8 tty (nroff)
138########################################################################
139
140my %tones1_Unicode =
141  (
142   'A' => q(\\[u0100]),
143   'E' => q(\\[u0112]),
144   'I' => q(\\[u012A]),
145   'O' => q(\\[u014C]),
146   'U' => q(\\[u016A]),
147   'Ü' => q(\\[u016A]),
148   'a' => q(\\[u0101]),
149   'e' => q(\\[u0113]),
150   'i' => q(\\[u012B]),
151   'o' => q(\\[u014D]),
152   'u' => q(\\[u016B]),
153   'ü' => q(\\[u01D6]),
154  );
155
156my %tones2_Unicode =
157  (
158   'A' => q(\\[u00C1]),
159   'E' => q(\\[u00C9]),
160   'I' => q(\\[u00CD]),
161   'O' => q(\\[u00D3]),
162   'U' => q(\\[u00DA]),
163   'Ü' => q(\\[u01D7]),
164   'a' => q(\\[u00E1]),
165   'e' => q(\\[u00E9]),
166   'i' => q(\\[u00ED]),
167   'o' => q(\\[u00F3]),
168   'u' => q(\\[u00FA]),
169   'ü' => q(\\[u01D8]),
170  );
171
172my %tones3_Unicode =
173  (
174   'A' => q(\\[u01CD]),
175   'E' => q(\\[u011A]),
176   'I' => q(\\[u01CF]),
177   'O' => q(\\[u01D1]),
178   'U' => q(\\[u01D3]),
179   'Ü' => q(\\[u01D9]),
180   'a' => q(\\[u01CE]),
181   'e' => q(\\[u011B]),
182   'i' => q(\\[u01D0]),
183   'o' => q(\\[u01D2]),
184   'u' => q(\\[u01D4]),
185   'ü' => q(\\[u01DA]),
186  );
187
188my %tones4_Unicode =
189  (
190   'A' => q(\\[u00C0]),
191   'E' => q(\\[u00C8]),
192   'I' => q(\\[u00CC]),
193   'O' => q(\\[u00D2]),
194   'U' => q(\\[u00D9]),
195   'Ü' => q(\\[u01DB]),
196   'a' => q(\\[u00E0]),
197   'e' => q(\\[u00E8]),
198   'i' => q(\\[u00EC]),
199   'o' => q(\\[u00F2]),
200   'u' => q(\\[u00F9]),
201   'ü' => q(\\[u01DC]),
202  );
203
204
205########################################################################
206# glyph variables for troff
207########################################################################
208
209#my $tone1_macron = '\\[a-]';
210#my $tone2_acute = '\\[aa]';
211#my $tone3_caron = '\\[ah]';
212#my $tone4_grave = '\\[ga]';
213my @accents = ( '', '\\[a-]', '\\[aa]', '\\[ah]', '\\[ga]', );
214
215my %tones2_glyphs =
216  (
217   'A' => q(\\['A]),
218   'E' => q(\\['E]),
219   'I' => q(\\['I]),
220   'O' => q(\\['O]),
221   'U' => q(\\['U]),
222   'a' => q(\\['a]),
223   'e' => q(\\['e]),
224   'i' => q(\\['i]),
225   'o' => q(\\['o]),
226   'u' => q(\\['u]),
227  );
228
229my %tones4_glyphs =
230  (
231   'A' => q(\\[`A]),
232   'E' => q(\\[`E]),
233   'I' => q(\\[`I]),
234   'O' => q(\\[`O]),
235   'U' => q(\\[`U]),
236   'a' => q(\\[`a]),
237   'e' => q(\\[`e]),
238   'i' => q(\\[`i]),
239   'o' => q(\\[`o]),
240   'u' => q(\\[`u]),
241  );
242
243
244
245########################################################################
246# subs
247########################################################################
248
249# Pinyin consists of syllables with a final number to be translated
250# into an accent.  Such numbered syllables are combined into words.
251# Such words can have a final punctuation.  A line is a collection of
252# such words.
253
254my @roffs = ( 'n',
255	      't',
256	    );
257
258########################################################################
259sub err {
260  my $s = shift;
261  print STDERR $s;
262  1;
263} # err()
264
265
266########################################################################
267sub handle_line {
268  my $starting_blanks = shift;
269  my $line = shift;
270
271#&err('handle_line start: ' . $line);
272
273  my %outline = ( 'n' => $starting_blanks, 't' => $starting_blanks, );
274
275  # transform to Ü only for inside of Perl
276  $line =~ s/\\
277	     \(:U
278	    /Ü/gx;
279  $line =~ s/\\
280	     \[:U\]
281	    /Ü/gx;
282
283# handle_line()
284
285  # transform to ü only for inside of Perl
286  $line =~ s/\\
287	     \(:u
288	    /ü/gx;
289  $line =~ s/\\
290	     \[:u\]
291	    /ü/gx;
292
293  $line =~ s/U[eE]/Ü/g;
294  $line =~ s/u[eE]/ü/g;
295
296  $line =~ s/\\\(aq/'/g;	# \(aq is an apostrophe
297  $line =~ s/\\\[aq\]/'/g;	# \[aq] is an apostrophe
298  $line =~ s/^[']//;		# remove leading apostrophe
299  $line =~ s/[']$//;		# remove final apostrophe
300  $line =~ s/['][']+/'/g;	# combine apostrophe groups
301  $line =~ s/([0-4])'/$1/;
302  $line =~ s/([^0-4])'/${1}0/;
303
304  my @words = split /\s+/, $line;
305
306
307# handle_line()
308  for my $word ( @words ) {
309#&err('handle_line word: ' . $word);
310
311    next unless ( $word );
312
313    # this is a word, maybe composed of several syllables
314    my $punctuation = $1 if ( $word =~ s/([,.?!:;]*)$// );
315    # '$word' is now without punctuation
316
317    my %outword = &handle_word($word);
318    next unless ( %outword );
319
320    for my $roff ( @roffs ) {
321#&err('handle_line roff ' . $roff .  ': ' . $outword{$roff});
322
323      # combine words to line
324      next unless ( $outword{$roff} );
325
326      # non-initial space
327      $outline{$roff} .= ' ' if ( $outline{$roff} );
328
329      $outline{$roff} .= $outword{$roff};
330      $outline{$roff} .= $punctuation;
331    }
332  }
333#for my $roff ( @roffs ) {
334#&err('handle_line end ' . $roff .  ': ' . $outline{$roff});
335#}
336  return %outline;
337} # handle_line()
338
339
340########################################################################
341sub handle_word {
342  my $word = shift;
343#&err('handle_word start: ' . $word);
344
345  $word =~ s/5/0/g;		# transform 5 to 0
346  $word =~ s/([^0-4])$/${1}0/;	# add lacking final no-tone
347
348  # remove apostrophes with tone
349  $word =~ s/
350	      ([0-4])
351	      [']
352	    /$1/gx;
353  # replace apostrophes without tone by 0
354  $word =~ s/
355	      ([^0-4])
356	      [']
357	    /${1}0/gx;
358
359# handle_word()
360  # detect wrong tone numbers
361  if ( $word =~ s/[5-9]/0/g ) {
362    &err('word ' . $word . ': wrong tone number ' . $1);
363    return {};
364  }
365
366  $word =~ s/[']//g;		# remove apostrophes
367
368  # remove starting apostrophe or number
369  $word =~ s/^(['0-4])+//;
370
371  # add 0 for final no-tone
372  $word .= '0' if ( $word =~ /[^0-4]$/ );
373
374  if ( $word =~ /^[0-9]/ ) {	# word starts with number
375    print 'word: ' . $word . ' starts with tone number';
376    $word =~ s/^[0-9]+//;
377  }
378#&err('handle_word 0: ' . $word);
379
380# handle_word()
381
382  my %outword = ( 'n' => '', 't' => '', );
383
384  # split word into syllables
385  while ( $word =~ /^[a-zA-ZüÜ']/ ) {
386    $word =~ s/^([a-zA-ZüÜ']+)([0-4])//;
387    my $syll = $1;
388    my $tone = $2;
389#err('handle_word split: ' . $syll . ' ' . $tone);
390    my %outsyll = &handle_syll( $syll, $tone );
391    next unless ( %outsyll );
392    for my $roff ( @roffs ) {
393      my $out = $outsyll{$roff};
394      $out = '\\[aq]' . $out if ( $out && $out =~ /^[aeo]/ );
395      $outword{$roff} .= $out;
396#&err('handle_word ' . $roff . ': ' . $outword{$roff});
397    }
398  }
399  return %outword;
400} # handle_word()
401
402
403########################################################################
404sub handle_syll {
405  my $syll = shift;
406  my $tone = shift;
407#&err( 'handle_syll start: ' . $syll . ' ' . $tone);
408
409  my $lower_case = lc($syll);
410  $lower_case =~ s/Ü/ü/g;
411  unless ( exists($syllables{$lower_case}) ) {
412    err('The syllable ' . $syll . ' is not a Chinese syllable.');
413    return {};
414  }
415
416  my %outsyll = ( 'n' => '', 't' => '', );
417
418  if ( $tone == 0 ) {	# no accent
419    # use u umlaut without accent
420    $syll =~ s/Ü/\\[:U]/g;
421    $syll =~ s/ü/\\[:u]/g;
422
423    for my $roff ( @roffs ) {
424      $outsyll{$roff} = $syll;
425#&err('handle_syll 0 outsyll ' . $roff . ': ' . $outsyll{$roff});
426    }
427    return %outsyll;
428  }	# end of tone 0
429
430# handle_syll()
431
432  # split syllable
433  $syll =~
434    /^
435     ([a-zA-Z]*)
436     ([aeiouAEIOUüÜ]+)
437     ([a-zA-Z]*)
438     $/x;
439  my $initial = $1;
440  my $vowels = $2;
441  my $final = $3;
442  unless ( $vowels ) {
443    &err( 'Syllable ' . $syll . ' does not have vowels' );
444    return {};
445  }
446
447  # split vowels
448  my $vowels_before = '';
449  my $vowel = '';
450  my $vowels_after = '';
451
452# handle_syll()
453
454  # find vowel for accent
455  if ( $vowels =~ /^[aeiouAEIOU]$/ ) {		# only 1 vowel
456#&err('handle_syll single vowel ' . $vowels);
457    $vowel = $vowels;
458  } elsif ( $vowels eq 'ü' ) {
459    $vowel = $vowels;
460  } elsif ( $vowels eq 'Ü' ) {
461    $vowel = $vowels;
462  } elsif ( $vowels =~ /^([^aeAE]*)([aeAE])(.*)$/ ) {	# a, A, e or E
463    $vowels_before = $1;
464    $vowel = $2;
465    $vowels_after = $3;
466  } elsif ( $vowels =~ /^([^oO]*)(oO)(.*)$/ ) {		# o or O
467    $vowels_before = $1;
468    $vowel = $2;
469    $vowels_after = $3;
470  } elsif ( $vowels =~ /^(\w)(\w)(.*)$/ ) {		# take 2nd vowel
471    $vowels_before = $1;
472    $vowel = $2;
473    $vowels_after = $3;
474  } else {
475    &err( 'Unknown vowels: ' . $vowels . ' in syllable: ' . $syll );
476    return {};
477  }
478
479#  unless ( $vowel =~ /^[aeiouAEIOU]$/ ) {
480#    print STDERR q(The argument ') . $vowel . q(' is not a vowel!);
481#    return {};
482#  }
483
484# handle_syll()
485
486  $outsyll{'n'} = &vowel_n($vowel, $tone);
487  $outsyll{'t'} = &vowel_t($vowel, $tone);
488
489  for my $roff ( @roffs ) {
490    $outsyll{$roff} = $initial .  $vowels_before .
491      $outsyll{$roff} . $vowels_after .  $final;
492#&err('handle_syll out ' . $roff . ': ' . $outsyll{$roff});
493  }
494
495  return %outsyll;
496} # handle_syll()
497
498
499########################################################################
500sub vowel_n {	# Unicode for nroff
501  my $vowel = shift;
502  my $tone = shift;
503#&err('vowel_n: ' . $vowel . ' ' . $tone);
504
505  return '' unless ( $vowel );
506
507  if ( $tone == 1 ) {		# macron
508    $vowel = $tones1_Unicode{$vowel};
509  } elsif ( $tone == 2 ) {	# acute
510    $vowel = $tones2_Unicode{$vowel};
511  } elsif ( $tone == 3 ) {	# caron
512    $vowel = $tones3_Unicode{$vowel};
513  } elsif ( $tone == 4 ) {	# grave
514    $vowel = $tones4_Unicode{$vowel};
515  }
516  return $vowel;
517} # vowel_nr()
518
519
520########################################################################
521sub vowel_t {	# named glyphs for troff
522  my $vowel = shift;
523  my $tone = shift;
524#&err( 'vowel_t: ' . $vowel . ' ' . $tone);
525
526  return '' unless ( $vowel );
527
528  # \o'\s-2\[:u]\s0\[a-]'
529  if ( $vowel =~ /[üÜ]/ ) {
530    my $smaller = 2;
531    $vowel = q(\\o'\\s-) . $smaller . q(\\[:u]\\s0) .
532      $accents[$tone] . q(');
533    return $vowel;
534  }
535
536  $vowel = q(\\[.i]) if ( $vowel eq 'i' );
537
538  if ( $tone == 1 ) {		# macron
539    $vowel = q(\\o') . $vowel . $accents[$tone] . q(');
540  } elsif ( $tone == 2 ) {	# acute
541    $vowel = $tones2_glyphs{$vowel};
542  } elsif ( $tone == 3 ) {	# caron
543    $vowel = q(\\o') . $vowel . $accents[$tone] . q(');
544  } elsif ( $tone == 4 ) {	# grave
545    $vowel = $tones4_glyphs{$vowel};
546  }
547  return $vowel;
548} # vowel_t()
549
550
551########################################################################
552sub finish_pinyin_mode {
553#&err( 'finish' );
554  my $n = shift;
555  my $t = shift;
556  push @$n, '\\}';
557  push @$t, '\\}';
558
559  for ( @$n ) {	# Unicode for nroff
560    print;
561  }
562
563  for ( @$t ) {	# glyphs for troff
564    print;
565  }
566
567  1;
568} # finish_pinyin_mode()
569
570
5711;
572########################################################################
573### Emacs settings
574# Local Variables:
575# mode: CPerl
576# End:
577