/
wnnclient.c
1092 lines (924 loc) · 49.4 KB
/
wnnclient.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/****************************************************************************
* wnnclient.c *
* Interface to the IME engine (FreeWnn). *
* NOTE: This module must be compiled with _cdecl linkage (with ICC: /Mc). *
* *
****************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *
* 02111-1307 USA *
* *
****************************************************************************/
#define INCL_DOSERRORS
#define INCL_WINATOM
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <uconv.h>
#include <wnn/jllib.h>
#include <wnn/rk_spclval.h>
#include "codepage.h"
#include "wnnhook.h"
#include "wnnim.h"
#include "wnnclient.h"
// (In wnnconv.c) Convert between fixed-width 2-byte EUC and normal (packed) EUC
int _cdecl wnn_sStrcpy ( register char *c, register w_char *w );
int _cdecl wnn_Sstrcpy ( w_char *w, unsigned char *c );
// Romkan API functions exported by WNN library (just the ones we actually use).
//
extern void _cdecl romkan_set_lang( char * );
extern int _cdecl romkan_init( char*, letter, char, letter (* _cdecl keyinfn)(),int (* _cdecl bytcntfn)() );
// extern letter _cdecl romkan_getc( void );
extern letter * _cdecl romkan_henkan( letter );
extern void _cdecl romkan_clear( void );
// JLIB and other API functions exported by WNN library. (Again, this is not
// comprehensive, just the ones we need.)
//
extern struct wnn_buf * _cdecl jl_open_lang( const char *, const char *, const char *, const char *, int (* _cdecl)(const char *), int (* _cdecl)(const char *), int );
extern int _cdecl jl_isconnect_e( register struct wnn_env *env );
extern struct wnn_env * _cdecl jl_env_get( register struct wnn_buf *buf );
extern int _cdecl jl_fuzokugo_get_e( register struct wnn_env *env, char *fname );
extern int _cdecl jl_set_env_wnnrc( register struct wnn_env *, const char *, int (* _cdecl)(const char *), int (* _cdecl)(const char *) );
extern int _cdecl jl_fuzokugo_set_e( struct wnn_env *env, char *fname );
extern void _cdecl jl_close( register struct wnn_buf *buf );
extern int _cdecl jl_dic_list_e( struct wnn_env *env, WNN_DIC_INFO **dicinfo );
extern int _cdecl jl_kanji_len( struct wnn_buf *buf, register int bun_no, register int bun_no2 );
extern int _cdecl jl_kill( struct wnn_buf *buf, register int bun_no, register int bun_no2 );
extern int _cdecl jl_ren_conv( register struct wnn_buf *buf, register w_char *yomi, int bun_no, int bun_no2, int use_maep );
extern int _cdecl jl_tan_conv( register struct wnn_buf *buf, w_char *yomi, register int bun_no, register int bun_no2, int use_maep, int ich_shop );
extern int _cdecl jl_yomi_len( struct wnn_buf *buf, register int bun_no, register int bun_no2 );
extern int _cdecl jl_set_jikouho( register struct wnn_buf *buf, register int offset );
extern int _cdecl jl_zenkouho( register struct wnn_buf *buf, int bun_no, int use_maep, int uniq_level );
extern int _cdecl jl_zenkouho_dai( register struct wnn_buf *buf, int bun_no, int bun_no2, int use_maep, int uniq_level );
extern int _cdecl jl_update_hindo( register struct wnn_buf *buf, int bun_no, int bun_no2 );
extern int _cdecl jl_word_add_e( struct wnn_env *env, int dic_no, w_char *yomi, w_char *kanji, w_char *comment, int hinsi, int init_hindo );
extern int _cdecl jl_dic_save_all_e( struct wnn_env *env );
extern int _cdecl wnn_get_area( struct wnn_buf *buf, register int bun_no, register int bun_no2, w_char *area, int kanjip );
// Internal utility functions
//
INT IM_CALLCNV StrTransform( UniChar *puszString, INT iMax, XformObject xform );
INT IM_CALLCNV MakeKatakana( void );
INT IM_CALLCNV MakeHalfKana( void );
BYTE IM_CALLCNV PreprocessKana( USHORT fsMode, PUSHORT pusIn, PUSHORT pusOut, PSZ pszOutput, USHORT cbOutput );
// --------------------------------------------------------------------------
// Common variables
extern IMCLIENTDATA global;
char WnnErrorBuf[ 128 ] = {0}; // Error messages from error handler
BOOL fInitRK = FALSE; // Has phonetic input method been initialized?
BOOL fInitCJK = FALSE; // Has clause conversion method been initiated?
USHORT usCharIdx; // Index of next input character to be converted
UconvObject uconvEUC = NULL; // Conversion object (EUC to UCS-2)
XformObject xfKatakana = NULL; // Transformation object for fullwidth katakana
// =========================================================================
// CALLBACK FUNCTIONS
// =========================================================================
/* ------------------------------------------------------------------------- *
* NextCharacter *
* *
* Callback function registered with romkan_init(): returns the next input *
* byte for conversion. The FreeWnn romkan_next() function (which is called *
* internally by romkan_getc()) uses this to retrieve each byte. *
* *
* Since we don't use romkan_getc(), this is presumably unused in practice. *
* ------------------------------------------------------------------------- */
letter _cdecl NextCharacter()
{
letter ltr;
if ( usCharIdx >= sizeof( global.szRomaji ))
ltr = EOLTTR;
else
ltr = global.szRomaji[ usCharIdx ] ?
(letter)( global.szRomaji[ usCharIdx++ ]) :
EOLTTR;
return ltr;
}
/* ------------------------------------------------------------------------- *
* CharacterByteCount *
* *
* Callback function registered with romkan_init(): returns whether an *
* input character value is a single- or double-byte character for the *
* active input codepage. Used by romkan_next() via romkan_getc(). *
* *
* Since we don't use romkan_getc(), this is presumably unused in practice. *
* ------------------------------------------------------------------------- */
int _cdecl CharacterByteCount( char *pChar )
{
return 1; // We only pass ASCII characters as input anyway, so...
}
/* ------------------------------------------------------------------------- *
* ErrorFunc *
* *
* Callback function registered with jl_open_lang(): used to output error *
* messages. *
* ------------------------------------------------------------------------- */
int _cdecl ErrorFunc( const char *pcsz )
{
if ( pcsz && *pcsz )
strncpy( WnnErrorBuf, pcsz, sizeof( WnnErrorBuf ) - 1 );
else
WnnErrorBuf[ 0 ] = 0;
return 1;
}
// =========================================================================
// FUNCTIONS RELATED TO PHONETIC/INPUT CONVERSION
// =========================================================================
/* ------------------------------------------------------------------------- *
* InitInputMethod *
* *
* Initialize the phonetic input conversion engine. In this implementation *
* (FreeWnn romkan), this involves loading the romkan table files. *
* *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV InitInputMethod( PSZ pszPath, USHORT usLang )
{
USHORT cpEUC;
CHAR szLang[ 6 ];
CHAR szModeHyo[ CCHMAXPATH ] = {0};
int rc;
CHAR *c;
PSZ pszFile = NULL;
USHORT cbFile;
switch ( usLang ) {
case MODE_CN: strcpy( szLang, "zh_CN"); break;
case MODE_TW: strcpy( szLang, "zh_TW"); break;
case MODE_KR: strcpy( szLang, "ko_KR"); break;
default: strcpy( szLang, "ja_JP"); break;
}
if ( pszPath == NULL ) {
/* If ROMKAN_TABLE is defined and includes a directory specifier, use
* it as the full path to the romkan config file. If it is defined but
* doesn't contain a directory specifier, treat it as a filename only,
* and append it to the directory path as determined below.
*/
pszPath = getenv("ROMKAN_TABLE");
if ( pszPath ) {
if ( strpbrk( pszPath, ":/\\") == NULL )
pszFile = strdup( pszPath );
else
strncpy( szModeHyo, pszPath, CCHMAXPATH - 1 );
}
if ( !szModeHyo[0] ) {
pszPath = getenv("WNNLIB");
if ( !pszFile )
pszFile = strdup("mode");
if ( pszPath )
sprintf( szModeHyo, "%.200s/%.5s/rk/%.49s", pszPath, szLang, pszFile );
else
sprintf( szModeHyo, "/@unixroot/usr/lib/wnn/%.5s/rk/%.49s", szLang, pszFile );
}
pszPath = szModeHyo;
if ( pszFile ) free( pszFile );
while (( c = strchr( pszPath, '\\')) != NULL ) *c = '/';
}
_PmpfF(("Initializing romkan engine using configuration %s", pszPath ));
// TODO make sure the config file actually exists
romkan_set_lang( szLang ); // This may not actually be needed (?)
// romkan_init() parameters:
// pszPath Filespec of the main romkan config table ('mode')
// 0x08 Value of the 'delete' character code
// *NextCharacter Pointer to character-read function
// *CharacterByteCount Pointer to character byte-count function
rc = romkan_init( pszPath, 0x08, 0, *NextCharacter, *CharacterByteCount );
if ( rc )
sprintf( global.szEngineError, "Failed to initialize romkan (Wnn) library (error %u)", rc );
fInitRK = ( rc == 0 )? TRUE: FALSE;
// Create a conversion object for EUC encoding (what Wnn returns its strings in)
if ( fInitRK && ( uconvEUC == NULL )) {
cpEUC = GetEucCodepage( usLang );
rc = CreateUconvObject( cpEUC, &uconvEUC );
if ( rc )
sprintf( global.szEngineError, "Failed to create conversion object for codepage %u (error %u). The OS/2 codepage file might not be installed.", cpEUC, rc );
}
if (( rc == NO_ERROR ) && ( usLang == MODE_JP )) {
// Create transformation objects for alternate Japanese kana modes
if ( UniCreateTransformObject( NULL, (UniChar *)L"katakana", &xfKatakana ) != NO_ERROR )
xfKatakana = NULL;
}
return rc;
}
/* ------------------------------------------------------------------------- *
* FinishInputMethod *
* *
* Close down the input method engine and free any associated resources *
* (romkan doesn't utilize any cleanup functions so there's not much to do). *
* ------------------------------------------------------------------------- */
void IM_CALLCNV FinishInputMethod( void )
{
if ( uconvEUC != NULL )
UniFreeUconvObject( uconvEUC );
if ( xfKatakana != NULL )
UniFreeTransformObject( xfKatakana );
}
/* ------------------------------------------------------------------------- *
* StrTransform *
* *
* Applies a ULS transformation to a UCS-2 string. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV StrTransform( UniChar *puszString, INT iMax, XformObject xform )
{
int iLen,
iOut,
rc;
UniChar *puszTemp;
if ( puszString == NULL ) return 0;
iLen = UniStrlen( puszString );
iOut = iMax;
puszTemp = (UniChar *) calloc( iMax, sizeof( UniChar ));
if ( !puszTemp ) return ERROR_NOT_ENOUGH_MEMORY;
rc = UniTransformStr( xform, puszString, &iLen, puszTemp, &iOut );
if ( rc == ULS_SUCCESS )
UniStrncpy( puszString, puszTemp, iMax - 1 );
free( puszTemp );
return rc;
}
/* ------------------------------------------------------------------------- *
* MakeKatakana *
* *
* Transforms an already-converted kana string into fullwidth katakana. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV MakeKatakana( void )
{
StrTransform( global.uszKana, sizeof( global.uszKana ), xfKatakana );
return 0;
}
/* ------------------------------------------------------------------------- *
* MakeHalfKana *
* *
* Transforms an already-converted kana string into halfwidth katakana. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV MakeHalfKana( void )
{
USHORT iMax;
UniChar *puszTemp;
iMax = sizeof( global.uszKana );
puszTemp = (UniChar *) calloc( iMax, sizeof( UniChar ));
if ( !puszTemp ) return ERROR_NOT_ENOUGH_MEMORY;
if ( ConvertHankaku( global.uszKana, *puszTemp, iMax ) > 0 )
UniStrcpy( global.uszKana, puszTemp );
free( puszTemp );
return 0;
}
/* ------------------------------------------------------------------------- *
* PreprocessKana *
* *
* This function checks for and converts certain sequences not handled *
* by romkan_henkan()'s normal hiragana logic. This is done before the call *
* to romkan_henkan(), and thus also prior to the output buffer's conversion *
* from EUC to UCS-2. Therefore, this function generates EUC output. *
* *
* PARAMETERS: *
* USHORT fsMode : The current input mode flags. *
* PUSHORT pusIn : Current position in the input buffer. *
* PUSHORT pusOut : Current position in the output buffer. *
* PSZ pszOutput: Pointer to the output buffer. *
* USHORT cbOutput : Total size of the output buffer, in bytes. *
* *
* RETURNS: BYTE *
* Result of the conversion attempt. This will be either KANA_COMPLETE *
* (meaning we found & converted a sequence) or KANA_PENDING (in which case *
* the calling function will proceed to use romkan_henkan() as usual). *
* ------------------------------------------------------------------------- */
BYTE IM_CALLCNV PreprocessKana( USHORT fsMode, PUSHORT pusIn, PUSHORT pusOut, PSZ pszOutput, USHORT cbOutput )
{
#define NUM_SPEC_KATAKANA 27
// Special katakana sequences (romaji input)
CHAR *aszSpcKataIn[] = {
"KWA", "GWA",
"SHE", "JE", "CHE",
"TSA", "TSE", "TSO",
"TI", "XTU", "TU",
"DI", "DYU", "DU",
"FA", "FI", "FE", "FO",
"WI", "WE", "WO", "YE",
"VA", "VI", "VU", "VE", "VO"
};
// Special katakana sequences (kana output); these are EUC-JP encoded
CHAR *aszSpcKataOut[] = {
"クァ", "グァ",
"シェ", "ジェ", "チェ",
"ツァ", "ツェ", "ツォ",
"ティ", "ッ", "トゥ",
"ディ", "デュ", "ドゥ",
"ファ", "フィ", "フェ", "フォ",
"ウィ", "ウェ", "ウォ", "イェ",
"ヴァ", "ヴィ", "ヴ", "ヴェ", "ヴォ"
};
USHORT index;
PSZ pszInput;
BYTE result = KANA_PENDING;
pszInput = (PSZ)(global.szRomaji) + *pusIn;
if ( IS_INPUT_MODE( fsMode, MODE_KATAKANA )) {
for ( index = 0; index < NUM_SPEC_KATAKANA; index++ ) {
if ( strcmpi( pszInput, aszSpcKataIn[ index ] ) == 0 ) {
strncat( pszOutput, aszSpcKataOut[ index ], cbOutput );
*pusIn += strlen( aszSpcKataIn[ index ] );
*pusOut += strlen( aszSpcKataOut[ index ] );
result = KANA_COMPLETE;
break;
}
}
}
// Convert tilde to wavy dash (any conversion mode)
if (( result != KANA_COMPLETE ) && ( *pszInput == 0x7E )) {
strncat( pszOutput, "~", cbOutput );
*pusIn += 1;
*pusOut += 2;
result = KANA_COMPLETE;
}
return result;
}
/* ------------------------------------------------------------------------- *
* ConvertPhonetic *
* *
* Convert the input ('romaji') buffer into phonetic characters ('kana') for *
* the current language. (We use the term 'kana', which is Japanese, but *
* this also applies to Korean Hangul.) *
* *
* Note that the converted result may consist of several bytes, possibly *
* even more than two in the case of composite characters. *
* *
* PARAMETERS: *
* USHORT fsMode: The current input mode flags. *
* *
* RETURNS: BYTE *
* Result of the conversion attempt. One of: *
* KANA_INVALID Invalid character sequence, not converted; caller can do *
* what it likes with it, but should clear the buffers. *
* KANA_PENDING Incomplete character sequence, not converted; caller *
* should keep the input buffer as-is, and continue. *
* KANA_COMPLETE Valid character sequence, successfully converted; caller *
* should process the converted text & clear the buffers. *
* KANA_CANDIDATE Valid & successfully converted, may be modified later; *
* caller should allow the user to confirm the current *
* converted value (in which case process it and clear the *
* buffers), or continue to add characters (in which case *
* the caller should keep the input buffer and continue). *
* ------------------------------------------------------------------------- */
BYTE IM_CALLCNV ConvertPhonetic( USHORT fsMode )
{
CHAR szOutput[ 8 ]; // should be big enough for any known sequence
USHORT i, j, // index variables
len; // input string length
BYTE result; // result to return
letter ltr, // current letter being examined
*converted, // pointers to converted letters
*c; // "
if ( !fInitRK ) return KANA_INVALID;
i = 0;
ltr = 0;
result = KANA_PENDING;
len = strlen( global.szRomaji );
memset( szOutput, 0, sizeof( szOutput ));
// Read (and try to convert) the input buffer until either we reach the
// end, or the current state becomes something other than KANA_PENDING.
// NOTE: We don't use romkan_getc() due to problems with its internal loops.
// Instead, we call romkan_henkan() and handle the iteration ourselves.
for( j = 0; (result == KANA_PENDING) && (j < len); j++ ) {
// Get the next input character
ltr = (letter)(global.szRomaji[ j ]);
if ( ltr && ( fsMode & MODE_JP )) {
// Check for kana that require special handling. If found, the
// buffer positions will be advanced by this function.
result = PreprocessKana( fsMode, &i, &j, szOutput, sizeof( szOutput ));
ltr = (letter)(global.szRomaji[ j ]);
}
if (( ltr == 0 ) || ( i >= sizeof( szOutput ) - 1 )) {
// End of input string
converted = NULL;
ltr = LTREOF;
}
#if 0 // not actually needed at the moment
else if ( IsDBCSLeadByte( (CHAR)(ltr & 0xFF), global.dbcs )) {
// This is already a DBCS character so don't try and convert it;
// just read both bytes and continue.
szOutput[ i++ ] = (CHAR)(ltr & 0xFF);
ltr = (letter)(global.szRomaji[ ++j ]);
if (( ltr == 0 ) || ( i >= sizeof( szOutput ) - 1 ))
ltr = LTREOF;
else
szOutput[ i++ ] = (CHAR)(ltr & 0xFF);
}
#endif
else if ( result == KANA_PENDING ) {
// Input character that needs converting. Note that romkan_henkan()
// converts ltr into (potentially) a series of output bytes, so
// we have to read all of them...
converted = romkan_henkan( ltr & 0xFF );
c = converted;
while (( *c != EOLTTR ) && i < sizeof( szOutput )) {
ltr = *c;
if ( is_HON( ltr )) {
szOutput[ i++ ] = (CHAR)(ltr & 0xFF);
result = KANA_COMPLETE;
}
c++;
if (( c - converted ) > 10 ) break; // simple sanity check
}
}
if ( ltr == NISEBP ) {
result = KANA_INVALID;
break;
}
else if ( ltr == EOLTTR || ltr == LTREOF )
break; // end of input, exit with the last status result
else if ( ltr & 0x80000000 )
result = KANA_PENDING;
else
break; // exit with the last status result
// TODO not sure how to identify KANA_CANDIDATE yet (only used for Korean)
}
// Convert szOutput to UCS-2
if ( i ) {
StrConvert( szOutput, (PCH)(global.uszKana), uconvEUC, NULL );
// Apply any special transformations (e.g. hiragana to katakana)
if ( IS_LANGUAGE( fsMode, MODE_JP )) {
if ( IS_INPUT_MODE( fsMode, MODE_KATAKANA ))
MakeKatakana();
else if ( IS_INPUT_MODE( fsMode, MODE_HALFWIDTH ))
MakeHalfKana();
}
}
romkan_clear();
return result;
}
// =========================================================================
// FUNCTIONS RELATED TO CJK/CLAUSE CONVERSION
// =========================================================================
/* ------------------------------------------------------------------------- *
* InitConversionMethod *
* *
* Initialize the CJK conversion engine. In this implementation (FreeWnn), *
* this involves connecting to the FreeWnn server and initializing the *
* associated environment. *
* *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV InitConversionMethod( PSZ pszPath, USHORT usLang, PVOID *ppSession )
{
struct wnn_env *wnnenv = NULL; // Wnn environment object
struct wnn_buf *bdata = NULL; // Wnn session buffer
PSZ pszEnv, // Return pointer for getenv()
pszServer, // Host address of jserver
pszUser; // User/environment name to use on the server
USHORT cpEUC; // The EUC codepage for the current language
CHAR szLang[ 6 ]; // The current language locale (e.g. "ja_JP")
CHAR szEnvRC[ CCHMAXPATH ]; // Path to the environment configuration file
CHAR fzk[ 1024 ] = {0}; // Name of the auxiliary dictionary
ULONG rc; // Called API return code
INT result = CONV_OK; // This function's return code
if ( uconvEUC == NULL ) {
cpEUC = GetEucCodepage( usLang );
rc = CreateUconvObject( cpEUC, &uconvEUC );
if ( rc ) {
sprintf( global.szEngineError, "Failed to create conversion object for codepage %u (error %u). The OS/2 codepage file might not be installed.", cpEUC, rc );
return CONV_CONNECT;
}
}
// Get the server and user names to use.
switch ( usLang ) {
default:
case MODE_JP:
pszEnv = getenv( WNN_SERVER_ENV_JA );
strcpy( szLang, "ja_JP");
break;
case MODE_KR:
pszEnv = getenv( WNN_SERVER_ENV_KR );
strcpy( szLang, "ko_KR");
break;
case MODE_CN:
pszEnv = getenv( WNN_SERVER_ENV_CN );
strcpy( szLang, "zh_CN");
break;
case MODE_TW:
pszEnv = getenv( WNN_SERVER_ENV_TW );
strcpy( szLang, "zh_TW");
break;
}
pszServer = strdup( pszEnv? pszEnv: "localhost");
pszEnv = getenv("USER");
pszUser = strdup( pszEnv? pszEnv: "root");
if ( pszPath == NULL ) {
CHAR szPathBuf[ CCHMAXPATH ];
CHAR *c;
pszPath = getenv("WNNLIB");
if ( pszPath )
strncpy( szPathBuf, pszPath, CCHMAXPATH - 16 );
else
strcpy( szPathBuf, "/@unixroot/usr/lib/wnn");
while (( c = strchr( szPathBuf, '\\')) != NULL ) *c = '/';
sprintf( szEnvRC, "%.240s/%.5s/wnnenvrc", szPathBuf, szLang );
}
_PmpfF(("Opening Wnn jlib engine (%s) on %s for user %s", szLang, pszServer, pszUser ));
// Connect to the server.
bdata = jl_open_lang( pszUser, pszServer, szLang, NULL, *ErrorFunc, *ErrorFunc, 0 );
if (( bdata == NULL ) || ( jl_isconnect( bdata ) == 0 )) {
if ( WnnErrorBuf[0] )
strncpy( global.szEngineError, WnnErrorBuf, sizeof( global.szEngineError ) - 1 );
else
strcpy( global.szEngineError, "Unknown error in jl_open_lang");
result = CONV_CONNECT;
goto done_connect;
}
/* We don't actually use the returned value, but asking for the current
* auxiliary dictionary is a good way to see if the environment exists.
*/
if ( jl_fuzokugo_get( bdata, fzk ) == -1 ) {
// Environment isn't active on server, so initialize it now.
_PmpfF(("Initializing environment %s", szEnvRC ));
/*
// Check to make sure the local configuration file exists
// Note - this doesn't work with /@unixroot in the string, since
// unlike the Wnn server, we are not using kLIBC
if ( stat( szEnvRC, &st ) == -1 ) {
sprintf( global.szEngineError,
"Configuration file not found: %.225s",
szEnvRC );
result = CONV_CONNECT;
jl_close( bdata );
goto done_connect;
}
*/
wnnenv = jl_env_get( bdata );
jl_set_env_wnnrc( wnnenv, szEnvRC, (int *) WNN_CREATE, NULL );
}
result = CONV_OK;
*ppSession = bdata;
fInitCJK = TRUE;
done_connect:
free( pszServer );
free( pszUser );
return result;
}
/* ------------------------------------------------------------------------- *
* FinishConversionMethod *
* *
* Close down the clause conversion IME engine and free any associated *
* resources. *
* ------------------------------------------------------------------------- */
void IM_CALLCNV FinishConversionMethod( PVOID pSession )
{
struct wnn_buf *bdata = pSession;
if ( bdata && jl_isconnect( bdata )) {
// Free anything left over in the conversion buffer.
if ( jl_bun_suu( bdata ))
jl_kill( bdata, 0, -1 );
if ( jl_isconnect( bdata )) {
#if 0
// Update all dictionaries and frequency files
jl_dic_save_all( bdata );
#endif
// Now close the connection (also frees the buffer)
jl_close( bdata );
}
}
}
/* ------------------------------------------------------------------------- *
* ConvertClause *
* *
* Convert a phonetic character string (clause) into CJK ideographic text *
* for the current language. The input is taken as an argument in UCS-2 *
* format; the output is (for now) saved in the Wnn buffer (pSession). If *
* the conversion was successful, the client can subsequently retrieve the *
* candidates for the full converted clause, and/or split the clause into *
* individual phrases and get the candidates for each phrase. *
* *
* PARAMETERS: *
* PVOID pSession: Pointer to Wnn data buffer. *
* UniChar *puszClause: Clause string to convert. *
* *
* RETURNS: *
* Result of the conversion attempt. One of: *
* CONV_CONNECT No connection to IME engine. *
* CONV_FAILED Conversion failed. *
* CONV_OK Conversion succeeded. *
* ------------------------------------------------------------------------- */
BYTE IM_CALLCNV ConvertClause( PVOID pSession, UniChar *puszClause )
{
CHAR szTemp[ MAX_KANA_BUFZ * 3 ] = {0}; // Temporary conversion buffer
INT iLen, // Buffer length
iResult; // Conversion result code from jlib
ULONG rc; // ULS return code
w_char *yomi; // Input string in fixed-width EUC format
struct wnn_buf *bdata = pSession; // Wnn session buffer
// if ( !fInitCJK ) return CONV_CONNECT;
// Double-check we are connected to the server
if ( !bdata || !jl_isconnect( bdata )) {
strcpy( global.szEngineError, "Lost connection to server.");
return CONV_CONNECT;
}
if ( !puszClause ) return CONV_FAILED;
// Convert the UCS-2 kana string into EUC
rc = StrConvert( (PCH) puszClause, szTemp, NULL, uconvEUC );
if ( rc != ULS_SUCCESS ) {
sprintf( global.szEngineError, "String conversion failed: 0x%X", rc );
return CONV_FAILED;
}
// Now convert that into the fixed-width format expected by Wnn jlib
iLen = strlen( szTemp ) + 1;
yomi = (w_char *) calloc( iLen, sizeof( w_char ));
wnn_Sstrcpy( yomi, szTemp );
// Tell Wnn to convert the full clause
iResult = jl_ren_conv( bdata, yomi, 0, -1, WNN_USE_MAE );
if ( iResult == -1 ) {
if ( WnnErrorBuf[0] )
strncpy( global.szEngineError, WnnErrorBuf, sizeof( global.szEngineError ) - 1 );
else
strcpy( global.szEngineError, "Unknown error in jl_ren_conv");
return CONV_FAILED;
}
free( yomi );
return CONV_OK;
}
/* ------------------------------------------------------------------------- *
* ConvertPhrase *
* *
* Convert a word-phrase from phonetic characters into CJK ideographic text *
* for the current language. The input is taken as an argument in UCS-2 *
* format; the output is (for now) saved in the Wnn buffer (pSession). If *
* the conversion was successful, the client can subsequently retrieve the *
* candidates for the converted phrase. *
* *
* PARAMETERS: *
* PVOID pSession : Pointer to Wnn data buffer. *
* UniChar *puszPhrase: Phrase string to convert. *
* *
* RETURNS: *
* Result of the conversion attempt. One of: *
* CONV_CONNECT No connection to IME engine. *
* CONV_FAILED Conversion failed. *
* CONV_OK Conversion succeeded. *
* ------------------------------------------------------------------------- */
BYTE IM_CALLCNV ConvertPhrase( PVOID pSession, UniChar *puszPhrase )
{
CHAR szTemp[ MAX_KANA_BUFZ * 3 ] = {0}; // Temporary conversion buffer
ULONG rc; // ULS return code
INT iLen, // Buffer length
iResult; // Conversion result code from jlib
w_char *yomi; // Input string in fixed-width EUC format
struct wnn_buf *bdata = pSession; // Wnn session buffer
// if ( !fInitCJK ) return CONV_CONNECT;
// Double-check we are connected to the server
if ( !bdata || !jl_isconnect( bdata )) {
strcpy( global.szEngineError, "Lost connection to server.");
return CONV_CONNECT;
}
if ( !puszPhrase ) return CONV_FAILED;
// Convert the UCS-2 kana string into EUC
rc = StrConvert( (PCH) puszPhrase, szTemp, NULL, uconvEUC );
if ( rc != ULS_SUCCESS ) {
sprintf( global.szEngineError, "String conversion failed: 0x%X", rc );
return CONV_FAILED;
}
// Now convert that into the fixed-width format expected by Wnn jlib
iLen = strlen( szTemp ) + 1;
yomi = (w_char *) calloc( iLen, sizeof( w_char ));
wnn_Sstrcpy( yomi, szTemp );
// Tell Wnn to convert the phrase
iResult = jl_tan_conv( bdata, yomi, 0, -1, WNN_USE_ZENGO, WNN_DAI );
if ( iResult == -1 ) {
if ( WnnErrorBuf[0] )
strncpy( global.szEngineError, WnnErrorBuf, sizeof( global.szEngineError ) - 1 );
else
strcpy( global.szEngineError, "Error converting text.");
return CONV_FAILED;
}
free( yomi );
return CONV_OK;
}
/* ------------------------------------------------------------------------- *
* GetConvertedString *
* *
* Get a converted clause or phrase string from the Wnn buffer, in a *
* displayable format. This can be used to obtain a phrase or phrase group *
* within the clause, or a conversion result (for a phrase or clause). *
* *
* ConvertClause (i.e. jl_ren_conv) must have been called on the phonetic *
* clause buffer first. This function can then be used to retrieve either: *
* *
* A. A word phrase within the clause. One of the things jl_ren_conv does *
* is split the input clause into grammatical word phrases which can then *
* be enumerated with GetPhraseCount (i.e. jl_bun_suu) and then be *
* retrieved with this function. This is useful if the client wants to *
* split the input string into sub-sections to allow the user to try *
* converting them individually. (For this purpose you generally want to *
* work on the phrase in uncoverted form, so set fReading to TRUE.) *
* *
* B. A conversion result for a phrase or clause. If called immediately *
* after ConvertClause, this will return the default conversion result *
* for the entire clause. If SetCandidate was called in between, then *
* the indicated candidate (i.e. alternate result) will be returned. *
* If ConvertPhrase was called after ConvertClause, then the conversion *
* result (or candidate) for the converted phrase will be returned. *
* *
* NOTES: *
* - Before any of this can work, jl_ren_conv must have been called on the *
* the clause buffer (i.e. using ConvertClause). *
* - Obtaining an individual phrase also requires jl_tan_conv to have been *
* called subsequently on that phrase (i.e. using ConvertPhrase). *
* - To obtain a conversion candidate, jl_zenkouho and jl_next/jl_previous *
* must then have been called as well (i.e. using PrepareCandidates and *
* SetCandidate). *
* *
* PARAMETERS: *
* PVOID pSession: Pointer to Wnn data buffer. *
* INT iPhrase : First phrase in the clause to retrieve (first = 0) *
* INT iLast : Last phrase to retrieve (-1 for rest of clause) *
* (If retrieving a candidate for a phrase, then -1 can be *
* used, since the candidate has only the one phrase.) *
* BOOL fReading: If TRUE, return unconverted reading instead of kanji. *
* UniChar **ppuszString: Pointer to UCS-2 string containing the result. *
* String will be allocated by this function and *
* should be free()d by the caller. *
* *
* RETURNS: *
* One of: *
* CONV_CONNECT No connection to IME engine. *
* CONV_FAILED Failed to retrieve string. *
* CONV_OK String retrieved successfully. *
* ------------------------------------------------------------------------- */
BYTE IM_CALLCNV GetConvertedString( PVOID pSession, INT iPhrase, INT iLast, BOOL fReading, UniChar **ppuszString )
{
INT iLen; // Buffer length
BYTE bResult = CONV_FAILED; // Return code from this function
PSZ pszEUC; // Converted kanji string in standard EUC format
w_char *kanji; // Converted string in fixed-width EUC format
struct wnn_buf *bdata = pSession; // Wnn session buffer
// Double-check we are connected to the server
if ( !bdata || !jl_isconnect( bdata )) {
strcpy( global.szEngineError, "Lost connection to server.");
return CONV_CONNECT;
}
// Get the requested string length
iLen = fReading? jl_yomi_len( bdata, iPhrase, iLast ) :
jl_kanji_len( bdata, iPhrase, iLast );
if ( !iLen ) {
if ( !global.szEngineError[0] )
// Set a generic error message if FreeWnn didn't provide one
sprintf( global.szEngineError, "Conversion string not available for phrase %d.", iPhrase );
return bResult;
}
// Allocate a buffer for the string
kanji = (w_char *) calloc( iLen+1, sizeof( w_char ));
if ( !kanji ) {
strcpy( global.szEngineError, "Error allocating memory.");
return bResult;
}
// Now retrieve the requested string or substring(s)
iLen = fReading? jl_get_yomi( bdata, iPhrase, iLast, kanji ) :
jl_get_kanji( bdata, iPhrase, iLast, kanji );
if ( iLen > 0 ) {
// Convert to standard EUC, allowing up to 3 bytes per input character
iLen *= 3;
pszEUC = (PSZ) calloc( iLen+1, sizeof( CHAR ));
if ( !pszEUC ) goto done;
wnn_sStrcpy( pszEUC, kanji );
// Now convert that to UCS-2 for return to the caller
*ppuszString = (UniChar *) calloc( iLen, sizeof( UniChar ));
if ( *ppuszString ) {
StrConvert( (PCH)pszEUC, (PCH)(*ppuszString), uconvEUC, NULL );
bResult = CONV_OK;
}
else strcpy( global.szEngineError, "Error allocating memory.");
free( pszEUC );
}
else if ( !global.szEngineError[0] )
// Set a generic error message if FreeWnn didn't provide one
strcpy( global.szEngineError, "Failed to retrieve conversion string.");
done:
free( kanji );
return bResult;
}
/* ------------------------------------------------------------------------- *
* GetPhraseCount *
* *
* Returns the number of word-phrases in the current clause buffer. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV GetPhraseCount( PVOID pSession )
{
struct wnn_buf *bdata = pSession; // Wnn session buffer
// Double-check we are connected to the server
if ( !bdata || !jl_isconnect( bdata )) {
strcpy( global.szEngineError, "Lost connection to server.");
return CONV_CONNECT;
}
return jl_bun_suu( bdata );
}
/* ------------------------------------------------------------------------- *
* PrepareCandidates *
* *
* Generate the possible conversion candidates for the current data. Once *
* this function has been called, the candidates can be enumerated using *
* GetPhraseCount, or selected using SetCandidate. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV PrepareCandidates( PVOID pSession )
{
struct wnn_buf *bdata = pSession; // Wnn session buffer
// Double-check we are connected to the server
if ( !bdata || !jl_isconnect( bdata )) {
strcpy( global.szEngineError, "Lost connection to server.");
return CONV_CONNECT;
}
if ( !jl_bun_suu( bdata )) {
if ( !global.szEngineError[0] )
strcpy( global.szEngineError, "Phrase not converted!");
return CONV_FAILED;
}
jl_zenkouho( bdata, 0, WNN_USE_MAE, WNN_UNIQ_KNJ );
return jl_zenkouho_suu( bdata );
}
/* ------------------------------------------------------------------------- *
* GetCandidateCount *
* *
* Get the number of available conversion candidates in the data buffer. *
* ------------------------------------------------------------------------- */
INT IM_CALLCNV GetCandidateCount( PVOID pSession )
{
struct wnn_buf *bdata = pSession; // Wnn session buffer