|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /* ================================================================ */
26: /*
27: File: ConvertUTF.c
28: Author: Mark E. Davis
29: Copyright (C) 1994 Taligent, Inc. All rights reserved.
30:
31: This code is copyrighted. Under the copyright laws, this code may not
32: be copied, in whole or part, without prior written consent of Taligent.
33:
34: Taligent grants the right to use or reprint this code as long as this
35: ENTIRE copyright notice is reproduced in the code or reproduction.
36: The code is provided AS-IS, AND TALIGENT DISCLAIMS ALL WARRANTIES,
37: EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO IMPLIED
38: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
39: NO EVENT WILL TALIGENT BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING,
40: WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS
41: INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY
42: LOSS) ARISING OUT OF THE USE OR INABILITY TO USE THIS CODE, EVEN
43: IF TALIGENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
44: BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF
45: LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE
46: LIMITATION MAY NOT APPLY TO YOU.
47:
48: RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the
49: government is subject to restrictions as set forth in subparagraph
50: (c)(l)(ii) of the Rights in Technical Data and Computer Software
51: clause at DFARS 252.227-7013 and FAR 52.227-19.
52:
53: This code may be protected by one or more U.S. and International
54: Patents.
55:
56: TRADEMARKS: Taligent and the Taligent Design Mark are registered
57: trademarks of Taligent, Inc.
58:
59: HISTORY:
60:
61: 22-Jan-1999 Don Brady Add decomposition to ConvertUTF8toUTF16.
62: 17-Nov-1998 Don Brady Add ":" to "/" conversions.
63: */
64: /* ================================================================ */
65:
66: #include "ConvertUTF.h"
67:
68: /* ================================================================ */
69:
70: const int halfShift = 10;
71: const UCS4 halfBase = 0x0010000UL;
72: const UCS4 halfMask = 0x3FFUL;
73: const UCS4 kSurrogateHighStart = 0xD800UL;
74: const UCS4 kSurrogateHighEnd = 0xDBFFUL;
75: const UCS4 kSurrogateLowStart = 0xDC00UL;
76: const UCS4 kSurrogateLowEnd = 0xDFFFUL;
77:
78: const UCS4 kReplacementCharacter = 0x0000FFFDUL;
79: const UCS4 kMaximumUCS2 = 0x0000FFFFUL;
80: const UCS4 kMaximumUTF16 = 0x0010FFFFUL;
81: const UCS4 kMaximumUCS4 = 0x7FFFFFFFUL;
82:
83: /* ================================================================ */
84:
85: UCS4 offsetsFromUTF8[6] = {0x00000000UL, 0x00003080UL, 0x000E2080UL,
86: 0x03C82080UL, 0xFA082080UL, 0x82082080UL};
87: char bytesFromUTF8[256] = {
88: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
89: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
90: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
91: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
92: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
93: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
94: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
95: 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5};
96:
97: UTF8 firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
98:
99: static UTF16 Decomposer(register UTF16 srcChar, UTF16 *cmbChar);
100:
101: /* ================================================================ */
102: /* This code is similar in effect to making successive calls on the
103: * mbtowc and wctomb routines in FSS-UTF. However, it is considerably
104: * different in code:
105: * it is adapted to be consistent with UTF16,
106: * the interface converts a whole buffer to avoid function-call overhead
107: * constants have been gathered.
108: * loops & conditionals have been removed as much as possible for
109: * efficiency, in favor of drop-through switch statements.
110: */
111:
112: /*
113: * Colons vs. Slash
114: *
115: * The VFS layer uses a "/" as a pathname separator but HFS disks
116: * use a ":". So when converting from UTF-8, ":" characters need
117: * to be changed to "/" so that colons don't end up on HFS disks.
118: * Likewise when converting into UTF-8, "/" characters need to be
119: * changed to ":" so that a "/" in a filename is not returned
120: * through the VFS layer.
121: *
122: * We do not need to worry about full-width slash or colons since
123: * their respective representations outside of Unicode are never
124: * the 7-bit versions (0x2f or 0x3a).
125: */
126:
127:
128: /* ================================================================ */
129: ConversionResult ConvertUTF16toUTF8 (
130: UTF16** sourceStart, const UTF16* sourceEnd,
131: UTF8** targetStart, const UTF8* targetEnd)
132: {
133: ConversionResult result = ok;
134: register UTF16* source = *sourceStart;
135: register UTF8* target = *targetStart;
136: while (source < sourceEnd) {
137: register UCS4 ch;
138: register unsigned short bytesToWrite = 0;
139: register const UCS4 byteMask = 0xBF;
140: register const UCS4 byteMark = 0x80;
141: register const UCS4 slash = '/';
142: ch = *source++;
143: if (ch == slash) {
144: ch = ':'; /* VFS doesn't like slash */
145: } else if (ch >= kSurrogateHighStart && ch <= kSurrogateHighEnd
146: && source < sourceEnd) {
147: register UCS4 ch2 = *source;
148: if (ch2 >= kSurrogateLowStart && ch2 <= kSurrogateLowEnd) {
149: ch = ((ch - kSurrogateHighStart) << halfShift)
150: + (ch2 - kSurrogateLowStart) + halfBase;
151: ++source;
152: };
153: };
154: if (ch < 0x80) { bytesToWrite = 1;
155: } else if (ch < 0x800) { bytesToWrite = 2;
156: } else if (ch < 0x10000) { bytesToWrite = 3;
157: } else if (ch < 0x200000) { bytesToWrite = 4;
158: } else if (ch < 0x4000000) { bytesToWrite = 5;
159: } else if (ch <= kMaximumUCS4){ bytesToWrite = 6;
160: } else { bytesToWrite = 2;
161: ch = kReplacementCharacter;
162: }; /* I wish there were a smart way to avoid this conditional */
163:
164: target += bytesToWrite;
165: if (target > targetEnd) {
166: target -= bytesToWrite; result = targetExhausted; break;
167: };
168: switch (bytesToWrite) { /* note: code falls through cases! */
169: case 6: *--target = (ch | byteMark) & byteMask; ch >>= 6;
170: case 5: *--target = (ch | byteMark) & byteMask; ch >>= 6;
171: case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
172: case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
173: case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
174: case 1: *--target = ch | firstByteMark[bytesToWrite];
175: };
176: target += bytesToWrite;
177: };
178: *sourceStart = source;
179: *targetStart = target;
180: return result;
181: };
182:
183: /* ================================================================ */
184:
185: ConversionResult ConvertUTF8toUTF16 (
186: UTF8** sourceStart, UTF8* sourceEnd,
187: UTF16** targetStart, const UTF16* targetEnd)
188: {
189: ConversionResult result = ok;
190: register UTF8* source = *sourceStart;
191: register UTF16* target = *targetStart;
192: while (source < sourceEnd) {
193: register UCS4 ch = 0;
194: register const UCS4 colon = ':';
195: register unsigned short extraBytesToWrite = bytesFromUTF8[*source];
196: if (source + extraBytesToWrite > sourceEnd) {
197: result = sourceExhausted; break;
198: };
199: switch(extraBytesToWrite) { /* note: code falls through cases! */
200: case 5: ch += *source++; ch <<= 6;
201: case 4: ch += *source++; ch <<= 6;
202: case 3: ch += *source++; ch <<= 6;
203: case 2: ch += *source++; ch <<= 6;
204: case 1: ch += *source++; ch <<= 6;
205: case 0: ch += *source++;
206: };
207: ch -= offsetsFromUTF8[extraBytesToWrite];
208:
209: if (target >= targetEnd) {
210: result = targetExhausted; break;
211: };
212: if (ch <= kMaximumUCS2) {
213: UTF16 combine;
214:
215: if (ch == colon) {
216: ch = '/'; /* HFS doesn't like colons */
217: }
218: *target++ = Decomposer(ch, &combine);
219:
220: if (combine) {
221: if (target >= targetEnd) {
222: result = targetExhausted; break;
223: };
224: *target++ = combine;
225: }
226: } else if (ch > kMaximumUTF16) {
227: *target++ = kReplacementCharacter;
228: } else {
229: if (target + 1 >= targetEnd) {
230: result = targetExhausted; break;
231: };
232: ch -= halfBase;
233: *target++ = (ch >> halfShift) + kSurrogateHighStart;
234: *target++ = (ch & halfMask) + kSurrogateLowStart;
235: };
236: };
237: *sourceStart = source;
238: *targetStart = target;
239: return result;
240: };
241:
242: /*
243: * Lookup tables for Unicode chars 0x00C0 thru 0x00FF
244: * primary_char yields first decomposed char. If this
245: * char is an alpha char then get the combining char
246: * from the combining_char table and add 0x0300 to it.
247: */
248:
249: static unsigned char primary_char[64] = {
250: 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0xC6, 0x43,
251:
252: 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
253:
254: 0xD0, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0xD7,
255:
256: 0xD8, 0x55, 0x55, 0x55, 0x55, 0x59, 0xDE, 0xDF,
257:
258: 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xE6, 0x63,
259:
260: 0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,
261:
262: 0xF0, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0xF7,
263:
264: 0xF8, 0x75, 0x75, 0x75, 0x75, 0x79, 0xFE, 0x79,
265: };
266:
267: static unsigned char combining_char[64] = {
268: 0x00, 0x01, 0x02, 0x03, 0x08, 0x0A, 0xFF, 0x27,
269:
270: 0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x02, 0x08,
271:
272: 0xFF, 0x03, 0x00, 0x01, 0x02, 0x03, 0x08, 0xFF,
273:
274: 0xFF, 0x00, 0x01, 0x02, 0x08, 0x01, 0xFF, 0xFF,
275:
276: 0x00, 0x01, 0x02, 0x03, 0x08, 0x0A, 0xFF, 0x27,
277:
278: 0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x02, 0x08,
279:
280: 0xFF, 0x03, 0x00, 0x01, 0x02, 0x03, 0x08, 0xFF,
281:
282: 0xFF, 0x00, 0x01, 0x02, 0x08, 0x01, 0xFF, 0x08
283: };
284:
285:
286: static const unsigned long __CJKDecompBitmap[] = { // 0x3000 ~ 0x30FF
287: 0x00000000, 0x00000000, 0x000AAAAA, 0xA540DB6C, // 0x3000
288: 0x00000802, 0x000AAAAA, 0xA540DB6C, 0x000009E2, // 0x3080
289: };
290:
291: #define IS_DECOMPOSABLE(table,unicodeVal) (table[(unicodeVal) / 32] & (1 << (31 - ((unicodeVal) % 32))))
292:
293:
294: /*
295: * Decomposer
296: *
297: * Composed Unicode characters are forbidden on
298: * HFS Plus volumes. Decomposer will convert a
299: * composed character into its correct decomposed
300: * sequence.
301: *
302: * Currently only MacRoman and MacJapanese chars
303: * are handled. Other composed characters are
304: * passed unchanged.
305: */
306: static UTF16
307: Decomposer(register UTF16 srcChar, UTF16 *cmbChar)
308: {
309: UTF16 dstChar;
310:
311: *cmbChar = 0;
312:
313: if ((srcChar <= 0x00FF) && (srcChar >= 0x00C0)) {
314: srcChar -= 0x00C0;
315:
316: dstChar = (UTF16) primary_char[srcChar];
317:
318: if (dstChar <= 'z') {
319: *cmbChar = (UTF16) 0x0300 + (UTF16) combining_char[srcChar];
320: }
321: } else if ((srcChar > 0x3000) && (srcChar < 0x3100) &&
322: IS_DECOMPOSABLE(__CJKDecompBitmap, srcChar - 0x3000)) {
323: switch(srcChar) {
324: case 0x3071: dstChar = 0x306F; *cmbChar = 0x309A; break; // HIRAGANA LETTER PA
325: case 0x3074: dstChar = 0x3072; *cmbChar = 0x309A; break; // HIRAGANA LETTER PI
326: case 0x3077: dstChar = 0x3075; *cmbChar = 0x309A; break; // HIRAGANA LETTER PU
327: case 0x307A: dstChar = 0x3078; *cmbChar = 0x309A; break; // HIRAGANA LETTER PE
328:
329: case 0x307D: dstChar = 0x307B; *cmbChar = 0x309A; break; // HIRAGANA LETTER PO
330: case 0x3094: dstChar = 0x3046; *cmbChar = 0x3099; break; // HIRAGANA LETTER VU
331: case 0x30D1: dstChar = 0x30CF; *cmbChar = 0x309A; break; // KATAKANA LETTER PA
332: case 0x30D4: dstChar = 0x30D2; *cmbChar = 0x309A; break; // KATAKANA LETTER PI
333:
334: case 0x30D7: dstChar = 0x30D5; *cmbChar = 0x309A; break; // KATAKANA LETTER PU
335: case 0x30DA: dstChar = 0x30D8; *cmbChar = 0x309A; break; // KATAKANA LETTER PE
336: case 0x30DD: dstChar = 0x30DB; *cmbChar = 0x309A; break; // KATAKANA LETTER PO
337: case 0x30F4: dstChar = 0x30A6; *cmbChar = 0x3099; break; // KATAKANA LETTER VU
338:
339: case 0x30F7: dstChar = 0x30EF; *cmbChar = 0x3099; break; // KATAKANA LETTER VA
340: case 0x30F8: dstChar = 0x30F0; *cmbChar = 0x3099; break; // KATAKANA LETTER VI
341: case 0x30F9: dstChar = 0x30F1; *cmbChar = 0x3099; break; // KATAKANA LETTER VE
342: case 0x30FA: dstChar = 0x30F2; *cmbChar = 0x3099; break; // KATAKANA LETTER VO
343:
344: default:
345: /* the rest (41 of them) have a simple conversion */
346: dstChar = srcChar - 1;
347: *cmbChar = 0x3099;
348: };
349: } else {
350: dstChar = srcChar;
351: }
352:
353: return dstChar;
354: }
355:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.