2
**********************************************************************
3
* Copyright (C) 2000-2001, International Business Machines
4
* Corporation and others. All Rights Reserved.
5
**********************************************************************
6
* file name: ucnv_lmb.cpp
8
* tab size: 4 (not used)
11
* created on: 2000feb09
12
* created by: Brendan Murray
13
* extensively hacked up by: Jim Snyder-Grant
15
* Modification History:
17
* Date Name Description
19
* 06/20/2000 helena OS/400 port changes; mostly typecast.
20
* 06/27/2000 Jim Snyder-Grant Deal with partial characters and small buffers.
21
* Add comments to document LMBCS format and implementation
22
* restructured order & breakdown of functions
23
* 06/28/2000 helena Major rewrite for the callback API changes.
26
#include "unicode/utypes.h"
28
#include "unicode/ucnv_err.h"
30
#include "unicode/ucnv.h"
36
(Lotus Multi-Byte Character Set)
38
LMBCS was invented in the late 1980's and is primarily used in Lotus Notes
39
databases and in Lotus 1-2-3 files. Programmers who work with the APIs
40
into these products will sometimes need to deal with strings in this format.
42
The code in this file provides an implementation for an ICU converter of
43
LMBCS to and from Unicode.
45
Since the LMBCS character set is only sparsely documented in existing
46
printed or online material, we have added extensive annotation to this
47
file to serve as a guide to understanding LMBCS.
49
LMBCS was originally designed with these four sometimes-competing design goals:
51
-Provide encodings for the characters in 12 existing national standards
52
(plus a few other characters)
53
-Minimal memory footprint
54
-Maximal speed of conversion into the existing national character sets
55
-No need to track a changing state as you interpret a string.
58
All of the national character sets LMBCS was trying to encode are 'ANSI'
59
based, in that the bytes from 0x20 - 0x7F are almost exactly the
60
same common Latin unaccented characters and symbols in all character sets.
62
So, in order to help meet the speed & memory design goals, the common ANSI
63
bytes from 0x20-0x7F are represented by the same single-byte values in LMBCS.
65
The general LMBCS code unit is from 1-3 bytes. We can describe the 3 bytes as
70
That is, a sometimes-optional 'group' byte, followed by 1 and sometimes 2
71
data bytes. The maximum size of a LMBCS chjaracter is 3 bytes:
73
#define ULMBCS_CHARSIZE_MAX 3
75
The single-byte values from 0x20 to 0x7F are examples of single D1 bytes.
76
We often have to figure out if byte values are below or above this, so we
77
use the ANSI nomenclature 'C0' and 'C1' to refer to the range of control
78
characters just above & below the common lower-ANSI range */
79
#define ULMBCS_C0END 0x1F
80
#define ULMBCS_C1START 0x80
82
Since LMBCS is always dealing in byte units. we create a local type here for
83
dealing with these units of LMBCS code units:
86
typedef uint8_t ulmbcs_byte_t;
89
Most of the values less than 0x20 are reserved in LMBCS to announce
90
which national character standard is being used for the 'D' bytes.
91
In the comments we show the common name and the IBM character-set ID
92
for these character-set announcers:
95
#define ULMBCS_GRP_L1 0x01 /* Latin-1 :ibm-850 */
96
#define ULMBCS_GRP_GR 0x02 /* Greek :ibm-851 */
97
#define ULMBCS_GRP_HE 0x03 /* Hebrew :ibm-1255 */
98
#define ULMBCS_GRP_AR 0x04 /* Arabic :ibm-1256 */
99
#define ULMBCS_GRP_RU 0x05 /* Cyrillic :ibm-1251 */
100
#define ULMBCS_GRP_L2 0x06 /* Latin-2 :ibm-852 */
101
#define ULMBCS_GRP_TR 0x08 /* Turkish :ibm-1254 */
102
#define ULMBCS_GRP_TH 0x0B /* Thai :ibm-874 */
103
#define ULMBCS_GRP_JA 0x10 /* Japanese :ibm-943 */
104
#define ULMBCS_GRP_KO 0x11 /* Korean :ibm-1261 */
105
#define ULMBCS_GRP_TW 0x12 /* Chinese SC :ibm-950 */
106
#define ULMBCS_GRP_CN 0x13 /* Chinese TC :ibm-1386 */
109
So, the beginning of understanding LMBCS is that IF the first byte of a LMBCS
110
character is one of those 12 values, you can interpret the remaining bytes of
111
that character as coming from one of those character sets. Since the lower
112
ANSI bytes already are represented in single bytes, using one of the character
113
set announcers is used to announce a character that starts with a byte of
116
The character sets are arranged so that the single byte sets all appear
117
before the multi-byte character sets. When we need to tell whether a
118
group byte is for a single byte char set or not we use this define: */
120
#define ULMBCS_DOUBLEOPTGROUP_START 0x10
123
However, to fully understand LMBCS, you must also understand a series of
124
exceptions & optimizations made in service of the design goals.
126
First, those of you who are character set mavens may have noticed that
127
the 'double-byte' character sets are actually multi-byte character sets
128
that can have 1 or two bytes, even in the upper-ascii range. To force
129
each group byte to introduce a fixed-width encoding (to make it faster to
130
count characters), we use a convention of doubling up on the group byte
131
to introduce any single-byte character > 0x80 in an otherwise double-byte
132
character set. So, for example, the LMBCS sequence x10 x10 xAE is the
133
same as '0xAE' in the Japanese code page 943.
135
Next, you will notice that the list of group bytes has some gaps.
136
These are used in various ways.
138
We reserve a few special single byte values for common control
139
characters. These are in the same place as their ANSI eqivalents for speed.
142
#define ULMBCS_HT 0x09 /* Fixed control char - Horizontal Tab */
143
#define ULMBCS_LF 0x0A /* Fixed control char - Line Feed */
144
#define ULMBCS_CR 0x0D /* Fixed control char - Carriage Return */
146
/* Then, 1-2-3 reserved a special single-byte character to put at the
147
beginning of internal 'system' range names: */
149
#define ULMBCS_123SYSTEMRANGE 0x19
151
/* Then we needed a place to put all the other ansi control characters
152
that must be moved to different values because LMBCS reserves those
153
values for other purposes. To represent the control characters, we start
154
with a first byte of 0xF & add the control chaarcter value as the
156
#define ULMBCS_GRP_CTRL 0x0F
158
/* For the C0 controls (less than 0x20), we add 0x20 to preserve the
159
useful doctrine that any byte less than 0x20 in a LMBCS char must be
160
the first byte of a character:*/
161
#define ULMBCS_CTRLOFFSET 0x20
164
Where to put the characters that aren't part of any of the 12 national
165
character sets? The first thing that was done, in the earlier years of
166
LMBCS, was to use up the spaces of the form
170
where 'G' was one of the single-byte character groups, and
171
D1 was less than 0x80. These sequences are gathered together
172
into a Lotus-invented doublebyte character set to represent a
173
lot of stray values. Internally, in this implementation, we track this
174
as group '0', as a place to tuck this exceptions list.*/
176
#define ULMBCS_GRP_EXCEPT 0x00
178
Finally, as the durability and usefulness of UNICODE became clear,
179
LOTUS added a new group 0x14 to hold Unicode values not otherwise
180
represented in LMBCS: */
181
#define ULMBCS_GRP_UNICODE 0x14
182
/* The two bytes appearing after a 0x14 are intrepreted as UFT-16 BE
183
(Big-Endian) characters. The exception comes when the UTF16
184
representation would have a zero as the second byte. In that case,
185
'F6' is used in its place, and the bytes are swapped. (This prevents
186
LMBCS from encoding any Unicode values of the form U+F6xx, but that's OK:
187
0xF6xx is in the middle of the Private Use Area.)*/
188
#define ULMBCS_UNICOMPATZERO 0xF6
190
/* It is also useful in our code to have a constant for the size of
191
a LMBCS char that holds a literal Unicode value */
192
#define ULMBCS_UNICODE_SIZE 3
195
To squish the LMBCS representations down even further, and to make
196
translations even faster,sometimes the optimization group byte can be dropped
197
from a LMBCS character. This is decided on a process-by-process basis. The
198
group byte that is dropped is called the 'optimization group'.
200
For Notes, the optimzation group is always 0x1.*/
201
#define ULMBCS_DEFAULTOPTGROUP 0x1
202
/* For 1-2-3 files, the optimzation group is stored in the header of the 1-2-3
205
In any case, when using ICU, you either pass in the
206
optimization group as part of the name of the converter (LMBCS-1, LMBCS-2,
207
etc.). Using plain 'LMBCS' as the name of the converter will give you
211
*** Implementation strategy ***
214
Because of the extensive use of other character sets, the LMBCS converter
215
keeps a mapping between optimization groups and IBM character sets, so that
216
ICU converters can be created and used as needed. */
218
static const char * const OptGroupByteToCPName[ULMBCS_CTRLOFFSET] = {
219
/* 0x0000 */ "lmb-excp", /* internal home for the LOTUS exceptions list */
220
/* 0x0001 */ "ibm-850",
221
/* 0x0002 */ "ibm-851",
222
/* 0x0003 */ "ibm-1255",
223
/* 0x0004 */ "ibm-1256",
224
/* 0x0005 */ "ibm-1251",
225
/* 0x0006 */ "ibm-852",
226
/* 0x0007 */ NULL, /* Unused */
227
/* 0x0008 */ "ibm-1254",
228
/* 0x0009 */ NULL, /* Control char HT */
229
/* 0x000A */ NULL, /* Control char LF */
230
/* 0x000B */ "ibm-874",
231
/* 0x000C */ NULL, /* Unused */
232
/* 0x000D */ NULL, /* Control char CR */
233
/* 0x000E */ NULL, /* Unused */
234
/* 0x000F */ NULL, /* Control chars: 0x0F20 + C0/C1 character: algorithmic */
235
/* 0x0010 */ "ibm-943",
236
/* 0x0011 */ "ibm-1363",
237
/* 0x0012 */ "ibm-950",
238
/* 0x0013 */ "ibm-1386"
240
/* The rest are null, including the 0x0014 Unicode compatibility region
241
and 0x0019, the 1-2-3 system range control char */
244
/* As you can see, even though any byte below 0x20 could be an optimization
245
byte, only those at 0x13 or below can map to an actual converter. To limit
246
some loops and searches, we define a value for that last group converter:*/
248
#define ULMBCS_GRP_LAST 0x13 /* last LMBCS group that has a converter */
251
/* That's approximately all the data that's needed for translating
255
However, to translate Unicode to LMBCS, we need some more support.
257
That's because there are often more than one possible mappings from a Unicode
258
code point back into LMBCS. The first thing we do is look up into a table
259
to figure out if there are more than one possible mappings. This table,
260
arranged by Unicode values (including ranges) either lists which group
261
to use, or says that it could go into one or more of the SBCS sets, or
262
into one or more of the DBCS sets. (If the character exists in both DBCS &
263
SBCS, the table will place it in the SBCS sets, to make the LMBCS code point
264
length as small as possible. Here's the two special markers we use to indicate
265
ambiguous mappings: */
267
#define ULMBCS_AMBIGUOUS_SBCS 0x80 /* could fit in more than one
268
LMBCS sbcs native encoding
269
(example: most accented latin) */
270
#define ULMBCS_AMBIGUOUS_MBCS 0x81 /* could fit in more than one
271
LMBCS mbcs native encoding
274
/* And here's a simple way to see if a group falls in an appropriate range */
275
#define ULMBCS_AMBIGUOUS_MATCH(agroup, xgroup) \
276
((((agroup) == ULMBCS_AMBIGUOUS_SBCS) && \
277
(xgroup) < ULMBCS_DOUBLEOPTGROUP_START) || \
278
(((agroup) == ULMBCS_AMBIGUOUS_MBCS) && \
279
(xgroup) >= ULMBCS_DOUBLEOPTGROUP_START))
282
/* The table & some code to use it: */
285
static const struct _UniLMBCSGrpMap
287
const UChar uniStartRange;
288
const UChar uniEndRange;
289
const ulmbcs_byte_t GrpType;
294
{0x0001, 0x001F, ULMBCS_GRP_CTRL},
295
{0x0080, 0x009F, ULMBCS_GRP_CTRL},
296
{0x00A0, 0x01CD, ULMBCS_AMBIGUOUS_SBCS},
297
{0x01CE, 0x01CE, ULMBCS_GRP_TW },
298
{0x01CF, 0x02B9, ULMBCS_AMBIGUOUS_SBCS},
299
{0x02BA, 0x02BA, ULMBCS_GRP_CN},
300
{0x02BC, 0x02C8, ULMBCS_AMBIGUOUS_SBCS},
301
{0x02C9, 0x02D0, ULMBCS_AMBIGUOUS_MBCS},
302
{0x02D8, 0x02DD, ULMBCS_AMBIGUOUS_SBCS},
303
{0x0384, 0x03CE, ULMBCS_AMBIGUOUS_SBCS},
304
{0x0400, 0x044E, ULMBCS_GRP_RU},
305
{0x044F, 0x044F, ULMBCS_AMBIGUOUS_MBCS},
306
{0x0450, 0x0491, ULMBCS_GRP_RU},
307
{0x05B0, 0x05F2, ULMBCS_GRP_HE},
308
{0x060C, 0x06AF, ULMBCS_GRP_AR},
309
{0x0E01, 0x0E5B, ULMBCS_GRP_TH},
310
{0x200C, 0x200F, ULMBCS_AMBIGUOUS_SBCS},
311
{0x2010, 0x2010, ULMBCS_AMBIGUOUS_MBCS},
312
{0x2013, 0x2015, ULMBCS_AMBIGUOUS_SBCS},
313
{0x2016, 0x2016, ULMBCS_AMBIGUOUS_MBCS},
314
{0x2017, 0x2024, ULMBCS_AMBIGUOUS_SBCS},
315
{0x2025, 0x2025, ULMBCS_AMBIGUOUS_MBCS},
316
{0x2026, 0x2026, ULMBCS_AMBIGUOUS_SBCS},
317
{0x2027, 0x2027, ULMBCS_GRP_CN},
318
{0x2030, 0x2033, ULMBCS_AMBIGUOUS_SBCS},
319
{0x2035, 0x2035, ULMBCS_AMBIGUOUS_MBCS},
320
{0x2039, 0x203A, ULMBCS_AMBIGUOUS_SBCS},
321
{0x203B, 0x203B, ULMBCS_AMBIGUOUS_MBCS},
322
{0x2074, 0x2074, ULMBCS_GRP_KO},
323
{0x207F, 0x207F, ULMBCS_GRP_EXCEPT},
324
{0x2081, 0x2084, ULMBCS_GRP_KO},
325
{0x20A4, 0x20AC, ULMBCS_AMBIGUOUS_SBCS},
326
{0x2103, 0x2109, ULMBCS_AMBIGUOUS_MBCS},
327
{0x2111, 0x2126, ULMBCS_AMBIGUOUS_SBCS},
328
{0x212B, 0x212B, ULMBCS_AMBIGUOUS_MBCS},
329
{0x2135, 0x2135, ULMBCS_AMBIGUOUS_SBCS},
330
{0x2153, 0x2154, ULMBCS_GRP_KO},
331
{0x215B, 0x215E, ULMBCS_GRP_EXCEPT},
332
{0x2160, 0x2179, ULMBCS_AMBIGUOUS_MBCS},
333
{0x2190, 0x2195, ULMBCS_GRP_EXCEPT},
334
{0x2196, 0x2199, ULMBCS_AMBIGUOUS_MBCS},
335
{0x21A8, 0x21A8, ULMBCS_GRP_EXCEPT},
336
{0x21B8, 0x21B9, ULMBCS_GRP_CN},
337
{0x21D0, 0x21D5, ULMBCS_GRP_EXCEPT},
338
{0x21E7, 0x21E7, ULMBCS_GRP_CN},
339
{0x2200, 0x220B, ULMBCS_GRP_EXCEPT},
340
{0x220F, 0x2215, ULMBCS_AMBIGUOUS_MBCS},
341
{0x2219, 0x2220, ULMBCS_GRP_EXCEPT},
342
{0x2223, 0x2228, ULMBCS_AMBIGUOUS_MBCS},
343
{0x2229, 0x222B, ULMBCS_GRP_EXCEPT},
344
{0x222C, 0x223D, ULMBCS_AMBIGUOUS_MBCS},
345
{0x2245, 0x2248, ULMBCS_GRP_EXCEPT},
346
{0x224C, 0x224C, ULMBCS_GRP_TW},
347
{0x2252, 0x2252, ULMBCS_AMBIGUOUS_MBCS},
348
{0x2260, 0x2265, ULMBCS_GRP_EXCEPT},
349
{0x2266, 0x226F, ULMBCS_AMBIGUOUS_MBCS},
350
{0x2282, 0x2297, ULMBCS_GRP_EXCEPT},
351
{0x2299, 0x22BF, ULMBCS_AMBIGUOUS_MBCS},
352
{0x22C0, 0x22C0, ULMBCS_GRP_EXCEPT},
353
{0x2310, 0x2310, ULMBCS_GRP_EXCEPT},
354
{0x2312, 0x2312, ULMBCS_AMBIGUOUS_MBCS},
355
{0x2318, 0x2321, ULMBCS_GRP_EXCEPT},
356
{0x2318, 0x2321, ULMBCS_GRP_CN},
357
{0x2460, 0x24E9, ULMBCS_AMBIGUOUS_MBCS},
358
{0x2500, 0x2500, ULMBCS_AMBIGUOUS_SBCS},
359
{0x2501, 0x2501, ULMBCS_AMBIGUOUS_MBCS},
360
{0x2502, 0x2502, ULMBCS_AMBIGUOUS_SBCS},
361
{0x2503, 0x2503, ULMBCS_AMBIGUOUS_MBCS},
362
{0x2504, 0x2505, ULMBCS_GRP_TW},
363
{0x2506, 0x2665, ULMBCS_AMBIGUOUS_MBCS},
364
{0x2666, 0x2666, ULMBCS_GRP_EXCEPT},
365
{0x2666, 0x2666, ULMBCS_GRP_EXCEPT},
366
{0x2667, 0x2E7F, ULMBCS_AMBIGUOUS_SBCS},
367
{0x2E80, 0xF861, ULMBCS_AMBIGUOUS_MBCS},
368
{0xF862, 0xF8FF, ULMBCS_GRP_EXCEPT},
369
{0xF900, 0xFA2D, ULMBCS_AMBIGUOUS_MBCS},
370
{0xFB00, 0xFEFF, ULMBCS_AMBIGUOUS_SBCS},
371
{0xFF01, 0xFFEE, ULMBCS_AMBIGUOUS_MBCS},
372
{0xFFFF, 0xFFFF, ULMBCS_GRP_UNICODE}
376
FindLMBCSUniRange(UChar uniChar)
378
const struct _UniLMBCSGrpMap * pTable = UniLMBCSGrpMap;
380
while (uniChar > pTable->uniEndRange)
385
if (uniChar >= pTable->uniStartRange)
387
return pTable->GrpType;
389
return ULMBCS_GRP_UNICODE;
393
We also ask the creator of a converter to send in a preferred locale
394
that we can use in resolving ambiguous mappings. They send the locale
395
in as a string, and we map it, if possible, to one of the
396
LMBCS groups. We use this table, and the associated code, to
399
/**************************************************
400
This table maps locale ID's to LMBCS opt groups.
401
The default return is group 0x01. Note that for
402
performance reasons, the table is sorted in
403
increasing alphabetic order, with the notable
404
exception of zhTW. This is to force the check
405
for Traditonal Chinese before dropping back to
408
Note too that the Latin-1 groups have been
409
commented out because it's the default, and
410
this shortens the table, allowing a serial
411
search to go quickly.
412
*************************************************/
414
static const struct _LocaleLMBCSGrpMap
416
const char *LocaleID;
417
const ulmbcs_byte_t OptGroup;
418
} LocaleLMBCSGrpMap[] =
420
{"ar", ULMBCS_GRP_AR},
421
{"be", ULMBCS_GRP_RU},
422
{"bg", ULMBCS_GRP_L2},
423
/* {"ca", ULMBCS_GRP_L1}, */
424
{"cs", ULMBCS_GRP_L2},
425
/* {"da", ULMBCS_GRP_L1}, */
426
/* {"de", ULMBCS_GRP_L1}, */
427
{"el", ULMBCS_GRP_GR},
428
/* {"en", ULMBCS_GRP_L1}, */
429
/* {"es", ULMBCS_GRP_L1}, */
430
/* {"et", ULMBCS_GRP_L1}, */
431
/* {"fi", ULMBCS_GRP_L1}, */
432
/* {"fr", ULMBCS_GRP_L1}, */
433
{"he", ULMBCS_GRP_HE},
434
{"hu", ULMBCS_GRP_L2},
435
/* {"is", ULMBCS_GRP_L1}, */
436
/* {"it", ULMBCS_GRP_L1}, */
437
{"iw", ULMBCS_GRP_HE},
438
{"ja", ULMBCS_GRP_JA},
439
{"ko", ULMBCS_GRP_KO},
440
/* {"lt", ULMBCS_GRP_L1}, */
441
/* {"lv", ULMBCS_GRP_L1}, */
442
{"mk", ULMBCS_GRP_RU},
443
/* {"nl", ULMBCS_GRP_L1}, */
444
/* {"no", ULMBCS_GRP_L1}, */
445
{"pl", ULMBCS_GRP_L2},
446
/* {"pt", ULMBCS_GRP_L1}, */
447
{"ro", ULMBCS_GRP_L2},
448
{"ru", ULMBCS_GRP_RU},
449
{"sh", ULMBCS_GRP_L2},
450
{"sk", ULMBCS_GRP_L2},
451
{"sl", ULMBCS_GRP_L2},
452
{"sq", ULMBCS_GRP_L2},
453
{"sr", ULMBCS_GRP_RU},
454
/* {"sv", ULMBCS_GRP_L1}, */
455
{"th", ULMBCS_GRP_TH},
456
{"tr", ULMBCS_GRP_TR},
457
{"uk", ULMBCS_GRP_RU},
458
/* {"vi", ULMBCS_GRP_L1}, */
459
{"zhTW", ULMBCS_GRP_TW},
460
{"zh", ULMBCS_GRP_CN},
461
{NULL, ULMBCS_GRP_L1}
466
FindLMBCSLocale(const char *LocaleID)
468
const struct _LocaleLMBCSGrpMap *pTable = LocaleLMBCSGrpMap;
470
if ((!LocaleID) || (!*LocaleID))
475
while (pTable->LocaleID)
477
if (*pTable->LocaleID == *LocaleID) /* Check only first char for speed */
479
/* First char matches - check whole name, for entry-length */
480
if (strncmp(pTable->LocaleID, LocaleID, strlen(pTable->LocaleID)) == 0)
481
return pTable->OptGroup;
484
if (*pTable->LocaleID > *LocaleID) /* Sorted alphabetically - exit */
488
return ULMBCS_GRP_L1;
493
Before we get to the main body of code, here's how we hook up to the rest
494
of ICU. ICU converters are required to define a structure that includes
495
some function pointers, and some common data, in the style of a C++
496
vtable. There is also room in there for converter-specific data. LMBCS
497
uses that converter-specific data to keep track of the 12 subconverters
498
we use, the optimization group, and the group (if any) that matches the
499
locale. We have one structure instantiated for each of the 12 possible
500
optimization groups. To avoid typos & to avoid boring the reader, we
501
put the declarations of these structures and functions into macros. To see
502
the definitions of these structures, see unicode\ucnv_bld.h
507
#define DECLARE_LMBCS_DATA(n) \
508
static const UConverterImpl _LMBCSImpl##n={\
514
_LMBCSToUnicodeWithOffsets,\
515
_LMBCSToUnicodeWithOffsets,\
521
static const UConverterStaticData _LMBCSStaticData##n={\
522
sizeof(UConverterStaticData),\
524
0, UCNV_IBM, UCNV_LMBCS_##n, 1, 1,\
525
{ 0x3f, 0, 0, 0 },1,FALSE,FALSE,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \
527
const UConverterSharedData _LMBCSData##n={\
528
sizeof(UConverterSharedData), ~((uint32_t) 0),\
529
NULL, NULL, &_LMBCSStaticData##n, FALSE, &_LMBCSImpl##n, \
533
/* The only function we needed to duplicate 12 times was the 'open'
534
function, which will do basically the same thing except set a different
535
optimization group. So, we put the common stuff into a worker function,
536
and set up another macro to stamp out the 12 open functions:*/
537
#define DEFINE_LMBCS_OPEN(n) \
539
_LMBCSOpen##n(UConverter* _this,const char* name,const char* locale,uint32_t options,UErrorCode* err) \
540
{ _LMBCSOpenWorker(_this, name,locale,options, err, n);}
544
/* Here's the open worker & the common close function */
546
_LMBCSOpenWorker(UConverter* _this,
551
ulmbcs_byte_t OptGroup
554
UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS*)uprv_malloc (sizeof (UConverterDataLMBCS));
555
if(extraInfo != NULL)
559
imax = sizeof(extraInfo->OptGrpConverter)/sizeof(extraInfo->OptGrpConverter[0]);
561
for (i=0; i < imax; i++)
563
extraInfo->OptGrpConverter[i] =
564
(OptGroupByteToCPName[i] != NULL) ?
565
ucnv_open(OptGroupByteToCPName[i], err) : NULL;
567
extraInfo->OptGroup = OptGroup;
568
extraInfo->localeConverterIndex = FindLMBCSLocale(locale);
572
*err = U_MEMORY_ALLOCATION_ERROR;
574
_this->extraInfo = extraInfo;
578
_LMBCSClose(UConverter * _this)
580
if (_this->extraInfo != NULL && !_this->isCopyLocal)
583
UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) _this->extraInfo;
585
for (Ix=0; Ix < ULMBCS_GRP_UNICODE; Ix++)
587
if (extraInfo->OptGrpConverter[Ix] != NULL)
588
ucnv_close (extraInfo->OptGrpConverter[Ix]);
590
uprv_free (_this->extraInfo);
595
Here's an all-crash stop for debugging, since ICU does not have asserts.
596
Turn this on by defining LMBCS_DEBUG, or by changing it to
600
#define MyAssert(b) {if (!(b)) {*(char *)0 = 1;}}
606
Here's the basic helper function that we use when converting from
607
Unicode to LMBCS, and we suspect that a Unicode character will fit into
608
one of the 12 groups. The return value is the number of bytes written
609
starting at pStartLMBCS (if any).
613
LMBCSConversionWorker (
614
UConverterDataLMBCS * extraInfo, /* subconverters, opt & locale groups */
615
ulmbcs_byte_t group, /* The group to try */
616
ulmbcs_byte_t * pStartLMBCS, /* where to put the results */
617
UChar * pUniChar, /* The input unicode character */
618
ulmbcs_byte_t * lastConverterIndex, /* output: track last successful group used */
619
UBool * groups_tried /* output: track any unsuccessful groups */
622
ulmbcs_byte_t * pLMBCS = pStartLMBCS;
623
UConverter * xcnv = extraInfo->OptGrpConverter[group];
627
ulmbcs_byte_t firstByte;
630
MyAssert(group<ULMBCS_GRP_UNICODE);
632
bytesConverted = _MBCSFromUChar32(xcnv->sharedData, *pUniChar, &value, FALSE);
634
/* get the first result byte */
635
switch(bytesConverted)
638
firstByte = (ulmbcs_byte_t)(value >> 24);
641
firstByte = (ulmbcs_byte_t)(value >> 16);
644
firstByte = (ulmbcs_byte_t)(value >> 8);
647
firstByte = (ulmbcs_byte_t)value;
650
/* most common failure mode is an unassigned character */
651
groups_tried[group] = TRUE;
655
*lastConverterIndex = group;
657
/* All initial byte values in lower ascii range should have been caught by now,
658
except with the exception group.
660
MyAssert((firstByte <= ULMBCS_C0END) || (firstByte >= ULMBCS_C1START) || (group == ULMBCS_GRP_EXCEPT));
662
/* use converted data: first write 0, 1 or two group bytes */
663
if (group != ULMBCS_GRP_EXCEPT && extraInfo->OptGroup != group)
666
if (bytesConverted == 1 && group >= ULMBCS_DOUBLEOPTGROUP_START)
672
/* don't emit control chars */
673
if ( bytesConverted == 1 && firstByte < 0x20 )
677
/* then move over the converted data */
678
switch(bytesConverted)
681
*pLMBCS++ = (ulmbcs_byte_t)(value >> 24);
683
*pLMBCS++ = (ulmbcs_byte_t)(value >> 16);
685
*pLMBCS++ = (ulmbcs_byte_t)(value >> 8);
687
*pLMBCS++ = (ulmbcs_byte_t)value;
689
/* will never occur */
693
return (pLMBCS - pStartLMBCS);
697
/* This is a much simpler version of above, when we
698
know we are writing LMBCS using the Unicode group
701
LMBCSConvertUni(ulmbcs_byte_t * pLMBCS, UChar uniChar)
703
/* encode into LMBCS Unicode range */
704
uint8_t LowCh = (uint8_t)(uniChar & 0x00FF);
705
uint8_t HighCh = (uint8_t)(uniChar >> 8);
707
*pLMBCS++ = ULMBCS_GRP_UNICODE;
711
*pLMBCS++ = ULMBCS_UNICOMPATZERO;
719
return ULMBCS_UNICODE_SIZE;
724
/* The main Unicode to LMBCS conversion function */
726
_LMBCSFromUnicode(UConverterFromUnicodeArgs* args,
729
ulmbcs_byte_t lastConverterIndex = 0;
731
ulmbcs_byte_t LMBCS[ULMBCS_CHARSIZE_MAX];
732
ulmbcs_byte_t * pLMBCS;
734
UBool groups_tried[ULMBCS_GRP_LAST+1];
735
UConverterDataLMBCS * extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo;
739
/* Basic strategy: attempt to fill in local LMBCS 1-char buffer.(LMBCS)
740
If that succeeds, see if it will all fit into the target & copy it over
743
We try conversions in the following order:
745
1. Single-byte ascii & special fixed control chars (&null)
746
2. Look up group in table & try that (could be
749
C) national encoding,
750
or ambiguous SBCS or MBCS group (on to step 4...)
752
3. If its ambiguous, try this order:
753
A) The optimization group
755
C) The last group that succeeded with this string.
756
D) every other group that's relevent (single or double)
757
E) If its single-byte ambiguous, try the exceptions group
759
4. And as a grand fallback: Unicode
762
while (args->source < args->sourceLimit && !U_FAILURE(*err))
764
if (args->target >= args->targetLimit)
766
*err = U_BUFFER_OVERFLOW_ERROR;
769
uniChar = *(args->source);
773
/* check cases in rough order of how common they are, for speed */
775
/* single byte matches: strategy 1 */
777
if (((uniChar > ULMBCS_C0END) && (uniChar < ULMBCS_C1START)) ||
778
uniChar == 0 || uniChar == ULMBCS_HT || uniChar == ULMBCS_CR ||
779
uniChar == ULMBCS_LF || uniChar == ULMBCS_123SYSTEMRANGE
782
*pLMBCS++ = (ulmbcs_byte_t ) uniChar;
789
/* Check by UNICODE range (Strategy 2) */
790
ulmbcs_byte_t group = FindLMBCSUniRange(uniChar);
792
if (group == ULMBCS_GRP_UNICODE) /* (Strategy 2A) */
794
pLMBCS += LMBCSConvertUni(pLMBCS,uniChar);
796
bytes_written = pLMBCS - LMBCS;
798
else if (group == ULMBCS_GRP_CTRL) /* (Strategy 2B) */
800
/* Handle control characters here */
801
if (uniChar <= ULMBCS_C0END)
803
*pLMBCS++ = ULMBCS_GRP_CTRL;
804
*pLMBCS++ = (ulmbcs_byte_t)(ULMBCS_CTRLOFFSET + uniChar);
806
else if (uniChar >= ULMBCS_C1START && uniChar <= ULMBCS_C1START + ULMBCS_CTRLOFFSET)
808
*pLMBCS++ = ULMBCS_GRP_CTRL;
809
*pLMBCS++ = (ulmbcs_byte_t ) (uniChar & 0x00FF);
811
bytes_written = pLMBCS - LMBCS;
813
else if (group < ULMBCS_GRP_UNICODE) /* (Strategy 2C) */
815
/* a specific converter has been identified - use it */
816
bytes_written = LMBCSConversionWorker (
817
extraInfo, group, pLMBCS, &uniChar,
818
&lastConverterIndex, groups_tried);
820
if (!bytes_written) /* the ambiguous group cases (Strategy 3) */
822
memset(groups_tried, 0, sizeof(groups_tried));
824
/* check for non-default optimization group (Strategy 3A )*/
825
if (extraInfo->OptGroup != 1
826
&& ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->OptGroup))
828
bytes_written = LMBCSConversionWorker (extraInfo,
829
extraInfo->OptGroup, pLMBCS, &uniChar,
830
&lastConverterIndex, groups_tried);
832
/* check for locale optimization group (Strategy 3B) */
834
&& (extraInfo->localeConverterIndex)
835
&& (ULMBCS_AMBIGUOUS_MATCH(group, extraInfo->localeConverterIndex)))
837
bytes_written = LMBCSConversionWorker (extraInfo,
838
extraInfo->localeConverterIndex, pLMBCS, &uniChar,
839
&lastConverterIndex, groups_tried);
841
/* check for last optimization group used for this string (Strategy 3C) */
843
&& (lastConverterIndex)
844
&& (ULMBCS_AMBIGUOUS_MATCH(group, lastConverterIndex)))
846
bytes_written = LMBCSConversionWorker (extraInfo,
847
lastConverterIndex, pLMBCS, &uniChar,
848
&lastConverterIndex, groups_tried);
853
/* just check every possible matching converter (Strategy 3D) */
854
ulmbcs_byte_t grp_start;
855
ulmbcs_byte_t grp_end;
856
ulmbcs_byte_t grp_ix;
857
grp_start = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS)
858
? ULMBCS_DOUBLEOPTGROUP_START
860
grp_end = (ulmbcs_byte_t)((group == ULMBCS_AMBIGUOUS_MBCS)
863
for (grp_ix = grp_start;
864
grp_ix <= grp_end && !bytes_written;
867
if (extraInfo->OptGrpConverter [grp_ix] && !groups_tried [grp_ix])
869
bytes_written = LMBCSConversionWorker (extraInfo,
870
grp_ix, pLMBCS, &uniChar,
871
&lastConverterIndex, groups_tried);
874
/* a final conversion fallback to the exceptions group if its likely
875
to be single byte (Strategy 3E) */
876
if (!bytes_written && grp_start == ULMBCS_GRP_L1)
878
bytes_written = LMBCSConversionWorker (extraInfo,
879
ULMBCS_GRP_EXCEPT, pLMBCS, &uniChar,
880
&lastConverterIndex, groups_tried);
883
/* all of our other strategies failed. Fallback to Unicode. (Strategy 4)*/
887
pLMBCS += LMBCSConvertUni(pLMBCS, uniChar);
888
bytes_written = pLMBCS - LMBCS;
893
/* we have a translation. increment source and write as much as posible to target */
896
while (args->target < args->targetLimit && bytes_written--)
898
*(args->target)++ = *pLMBCS++;
901
*(args->offsets)++ = sourceIndex;
905
if (bytes_written > 0)
907
/* write any bytes that didn't fit in target to the error buffer,
908
common code will move this to target if we get called back with
911
uint8_t * pErrorBuffer = args->converter->charErrorBuffer;
912
*err = U_BUFFER_OVERFLOW_ERROR;
913
args->converter->charErrorBufferLength = (int8_t)bytes_written;
914
while (bytes_written--)
916
*pErrorBuffer++ = *pLMBCS++;
923
/* Now, the Unicode from LMBCS section */
927
Special codes for the getNextUnicodeWorker -- usually as the result of
928
special error-callback behavior:
929
ULMBCS_SKIP To control skipping over LMBCS sequences
930
ULMBCS_MULTI To indicate that a single LMBCS char translates to
933
#define ULMBCS_SKIP U_ERROR_LIMIT
934
#define ULMBCS_MULTI ULMBCS_SKIP+1
936
/* A function to call when we are looking at the Unicode group byte in LMBCS */
938
GetUniFromLMBCSUni(char const ** ppLMBCSin) /* Called with LMBCS-style Unicode byte stream */
940
uint8_t HighCh = *(*ppLMBCSin)++; /* Big-endian Unicode in LMBCS compatibility group*/
941
uint8_t LowCh = *(*ppLMBCSin)++;
943
if (HighCh == ULMBCS_UNICOMPATZERO )
946
LowCh = 0; /* zero-byte in LSB special character */
948
return (UChar)((HighCh << 8) | LowCh);
953
/* CHECK_SOURCE_LIMIT: Helper macro to verify that there are at least'index'
954
bytes left in source up to sourceLimit.Errors appropriately if not
957
#define CHECK_SOURCE_LIMIT(index) \
958
if (args->source+index > args->sourceLimit){\
959
*err = U_TRUNCATED_CHAR_FOUND;\
960
args->source = saveSource;\
963
/* Return the Unicode representation for the current LMBCS character
965
This worker function is used by both ucnv_getNextUChar() and ucnv_ToUnicode().
966
The last parameter says whether the return value should be treated as UTF-16 or
967
UTF-32. The only difference is in surrogate handling
971
_LMBCSGetNextUCharWorker(UConverterToUnicodeArgs* args,
975
UChar32 uniChar = 0; /* an output UNICODE char */
976
ulmbcs_byte_t CurByte; /* A byte from the input stream */
977
const char * saveSource;
980
if (args->source >= args->sourceLimit)
982
*err = U_ILLEGAL_ARGUMENT_ERROR;
985
/* Grab first byte & save address for error recovery */
986
CurByte = *((ulmbcs_byte_t *) (saveSource = args->source++));
989
* at entry of each if clause:
990
* 1. 'CurByte' points at the first byte of a LMBCS character
991
* 2. '*source'points to the next byte of the source stream after 'CurByte'
993
* the job of each if clause is:
994
* 1. set '*source' to point at the beginning of next char (nop if LMBCS char is only 1 byte)
995
* 2. set 'uniChar' up with the right Unicode value, or set 'err' appropriately
998
/* First lets check the simple fixed values. */
1000
if(((CurByte > ULMBCS_C0END) && (CurByte < ULMBCS_C1START)) /* ascii range */
1002
|| CurByte == ULMBCS_HT || CurByte == ULMBCS_CR
1003
|| CurByte == ULMBCS_LF || CurByte == ULMBCS_123SYSTEMRANGE)
1009
UConverterDataLMBCS * extraInfo;
1010
ulmbcs_byte_t group;
1013
if (CurByte == ULMBCS_GRP_CTRL) /* Control character group - no opt group update */
1015
ulmbcs_byte_t C0C1byte;
1016
CHECK_SOURCE_LIMIT(1);
1017
C0C1byte = *(args->source)++;
1018
uniChar = (C0C1byte < ULMBCS_C1START) ? C0C1byte - ULMBCS_CTRLOFFSET : C0C1byte;
1021
if (CurByte == ULMBCS_GRP_UNICODE) /* Unicode compatibility group: BigEndian UTF16 */
1024
CHECK_SOURCE_LIMIT(2);
1026
uniChar = GetUniFromLMBCSUni(&(args->source));
1028
/* at this point we are usually done, but we need to make sure we are not in
1029
a situation where we can successfully put together a surrogate pair */
1031
if(returnUTF32 && UTF_IS_FIRST_SURROGATE(uniChar) && (args->source+3 <= args->sourceLimit)
1032
&& *(args->source)++ == ULMBCS_GRP_UNICODE
1033
&& UTF_IS_SECOND_SURROGATE(second = GetUniFromLMBCSUni(&(args->source))))
1035
uniChar = UTF16_GET_PAIR_VALUE(uniChar, second);
1038
else if (CurByte <= ULMBCS_CTRLOFFSET)
1040
group = CurByte; /* group byte is in the source */
1041
extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo;
1042
cnv = extraInfo->OptGrpConverter[group];
1045
/* this is not a valid group byte - no converter*/
1046
*err = U_INVALID_CHAR_FOUND;
1048
else if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */
1051
CHECK_SOURCE_LIMIT(2);
1053
/* check for LMBCS doubled-group-byte case */
1054
if (*args->source == group) {
1057
uniChar = _MBCSSimpleGetNextUChar(cnv->sharedData, &args->source, args->source + 1, FALSE);
1060
const char *newLimit = args->source + 2;
1061
uniChar = _MBCSSimpleGetNextUChar(cnv->sharedData, &args->source, newLimit, FALSE);
1062
args->source = newLimit; /* set the correct limit even in case of an error */
1065
else { /* single byte conversion */
1066
CHECK_SOURCE_LIMIT(1);
1067
CurByte = *(args->source)++;
1069
if (CurByte >= ULMBCS_C1START)
1071
uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv->sharedData, CurByte);
1075
/* The non-optimizable oddballs where there is an explicit byte
1076
* AND the second byte is not in the upper ascii range
1081
extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo;
1082
cnv = extraInfo->OptGrpConverter [ULMBCS_GRP_EXCEPT];
1084
/* Lookup value must include opt group */
1088
uniChar = _MBCSSimpleGetNextUChar(cnv->sharedData, &s, bytes + 2, FALSE);
1092
else if (CurByte >= ULMBCS_C1START) /* group byte is implicit */
1094
extraInfo = (UConverterDataLMBCS *) args->converter->extraInfo;
1095
group = extraInfo->OptGroup;
1096
cnv = extraInfo->OptGrpConverter[group];
1097
if (group >= ULMBCS_DOUBLEOPTGROUP_START) /* double byte conversion */
1099
if (!_MBCSIsLeadByte(cnv->sharedData, CurByte))
1101
CHECK_SOURCE_LIMIT(0);
1103
/* let the MBCS conversion consume CurByte again */
1105
uniChar = _MBCSSimpleGetNextUChar(cnv->sharedData, &args->source, args->source + 1, FALSE);
1109
CHECK_SOURCE_LIMIT(1);
1110
/* let the MBCS conversion consume CurByte again */
1112
/* since we know that we start at a lead byte, args->source _will_ be incremented by 2 */
1113
uniChar = _MBCSSimpleGetNextUChar(cnv->sharedData, &args->source, args->source + 2, FALSE);
1116
else /* single byte conversion */
1118
uniChar = _MBCS_SINGLE_SIMPLE_GET_NEXT_BMP(cnv->sharedData, CurByte);
1122
if (((uint32_t)uniChar - 0xfffe) <= 1) /* 0xfffe<=uniChar<=0xffff */
1124
UConverterToUnicodeArgs cbArgs = *args;
1125
UConverterCallbackReason reason;
1128
if (uniChar == 0xfffe)
1130
reason = UCNV_UNASSIGNED;
1131
*err = U_INVALID_CHAR_FOUND;
1135
reason = UCNV_ILLEGAL;
1136
*err = U_ILLEGAL_CHAR_FOUND;
1139
cbArgs.target = &UCh;
1140
cbArgs.targetLimit = &UCh + 1;
1141
cbArgs.converter->fromCharErrorBehaviour(cbArgs.converter->toUContext,
1144
args->source - saveSource,
1148
if (cbArgs.target != &UCh)
1150
uniChar = (UChar32) UCh;
1152
/* Did error functor skip */
1153
if (U_SUCCESS(*err) && cbArgs.target == &UCh)
1157
/* Did error functor try to write multiple UChars? */
1158
else if (*err == U_BUFFER_OVERFLOW_ERROR)
1160
*err = ULMBCS_MULTI;
1167
/* The exported function that gets one UTF32 character from a LMBCS stream
1170
_LMBCSGetNextUChar(UConverterToUnicodeArgs* args,
1175
nextUChar = _LMBCSGetNextUCharWorker(args, err, TRUE);
1176
} while (*err == ULMBCS_SKIP);
1178
if (*err == ULMBCS_MULTI)
1180
*err = U_ZERO_ERROR;
1185
/* The exported function that converts lmbcs to one or more
1186
UChars - currently UTF-16
1189
_LMBCSToUnicodeWithOffsets(UConverterToUnicodeArgs* args,
1192
UChar uniChar; /* one output UNICODE char */
1193
const char * saveSource = args->source; /* beginning of current code point */
1194
const char * pStartLMBCS = args->source; /* beginning of whole string */
1196
if (args->targetLimit == args->target) /* error check may belong in common code */
1198
*err = U_BUFFER_OVERFLOW_ERROR;
1202
/* Process from source to limit, or until error */
1203
while (!*err && args->sourceLimit > args->source && args->targetLimit > args->target)
1205
saveSource = args->source; /* beginning of current code point */
1207
if (args->converter->invalidCharLength) /* reassemble char from previous call */
1209
char LMBCS [ULMBCS_CHARSIZE_MAX];
1210
const char *pLMBCS = LMBCS, *saveSourceLimit;
1211
size_t size_old = args->converter->invalidCharLength;
1213
/* limit from source is either reminder of temp buffer, or user limit on source */
1214
size_t size_new_maybe_1 = sizeof(LMBCS) - size_old;
1215
size_t size_new_maybe_2 = args->sourceLimit - args->source;
1216
size_t size_new = (size_new_maybe_1 < size_new_maybe_2) ? size_new_maybe_1 : size_new_maybe_2;
1219
uprv_memcpy(LMBCS, args->converter->invalidCharBuffer, size_old);
1220
uprv_memcpy(LMBCS + size_old, args->source, size_new);
1221
saveSourceLimit = args->sourceLimit;
1222
args->source = pLMBCS;
1223
args->sourceLimit = pLMBCS+size_old+size_new;
1224
uniChar = (UChar) _LMBCSGetNextUCharWorker(args, err, FALSE);
1225
pLMBCS = args->source;
1226
args->source =saveSource;
1227
args->sourceLimit = saveSourceLimit;
1228
args->source += (pLMBCS - LMBCS - size_old);
1230
if (*err == U_TRUNCATED_CHAR_FOUND && !args->flush)
1232
/* evil special case: source buffers so small a char spans more than 2 buffers */
1233
int8_t savebytes = (int8_t)(size_old+size_new);
1234
args->converter->invalidCharLength = savebytes;
1235
uprv_memcpy(args->converter->invalidCharBuffer, LMBCS, savebytes);
1236
args->source = args->sourceLimit;
1237
*err = U_ZERO_ERROR;
1242
/* clear the partial-char marker */
1243
args->converter->invalidCharLength = 0;
1248
uniChar = (UChar) _LMBCSGetNextUCharWorker(args, err, FALSE);
1250
if (U_SUCCESS(*err))
1252
if (uniChar < 0xfffe)
1254
*(args->target)++ = uniChar;
1257
*(args->offsets)++ = saveSource - pStartLMBCS;
1260
else if (uniChar == 0xfffe)
1262
*err = U_INVALID_CHAR_FOUND;
1264
else /* if (uniChar == 0xffff) */
1266
*err = U_ILLEGAL_CHAR_FOUND;
1269
else if (*err == ULMBCS_MULTI)
1271
UChar * pUChar = args->converter->UCharErrorBuffer;
1272
int8_t BufferLength = args->converter->UCharErrorBufferLength;
1274
*err = U_ZERO_ERROR;
1276
{ /* error functor wants to write multiple UniChars */
1277
*(args->target)++ = uniChar;
1280
*(args->offsets)++ = saveSource - pStartLMBCS;
1282
uniChar = *pUChar++;
1284
while(BufferLength-- && args->targetLimit > args->target);
1286
if (++BufferLength > 0)
1287
{ /* fix up remaining UChars that can't fit in caller's buffer */
1288
uprv_memmove( args->converter->UCharErrorBuffer,
1289
args->converter->UCharErrorBuffer + args->converter->UCharErrorBufferLength - BufferLength,
1290
sizeof(UChar) * BufferLength);
1292
args->converter->UCharErrorBufferLength = BufferLength;
1294
else if (*err == ULMBCS_SKIP)
1296
*err = U_ZERO_ERROR; /* and just go around again..*/
1299
/* if target ran out before source, return U_BUFFER_OVERFLOW_ERROR */
1300
if (U_SUCCESS(*err) && args->sourceLimit > args->source && args->targetLimit <= args->target)
1302
*err = U_BUFFER_OVERFLOW_ERROR;
1305
/* If character incomplete, store away partial char if more to come */
1306
if (*err == U_TRUNCATED_CHAR_FOUND)
1308
args->source = args->sourceLimit;
1311
int8_t savebytes = (int8_t)(args->sourceLimit - saveSource);
1312
args->converter->invalidCharLength = (int8_t)savebytes;
1313
uprv_memcpy(args->converter->invalidCharBuffer, saveSource, savebytes);
1314
*err = U_ZERO_ERROR;
1319
/* And now, the macroized declarations of data & functions: */
1320
DEFINE_LMBCS_OPEN(1)
1321
DEFINE_LMBCS_OPEN(2)
1322
DEFINE_LMBCS_OPEN(3)
1323
DEFINE_LMBCS_OPEN(4)
1324
DEFINE_LMBCS_OPEN(5)
1325
DEFINE_LMBCS_OPEN(6)
1326
DEFINE_LMBCS_OPEN(8)
1327
DEFINE_LMBCS_OPEN(11)
1328
DEFINE_LMBCS_OPEN(16)
1329
DEFINE_LMBCS_OPEN(17)
1330
DEFINE_LMBCS_OPEN(18)
1331
DEFINE_LMBCS_OPEN(19)
1334
DECLARE_LMBCS_DATA(1)
1335
DECLARE_LMBCS_DATA(2)
1336
DECLARE_LMBCS_DATA(3)
1337
DECLARE_LMBCS_DATA(4)
1338
DECLARE_LMBCS_DATA(5)
1339
DECLARE_LMBCS_DATA(6)
1340
DECLARE_LMBCS_DATA(8)
1341
DECLARE_LMBCS_DATA(11)
1342
DECLARE_LMBCS_DATA(16)
1343
DECLARE_LMBCS_DATA(17)
1344
DECLARE_LMBCS_DATA(18)
1345
DECLARE_LMBCS_DATA(19)