|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: File: UnicodeWrappers.c
24:
25: Contains: Wrapper routines for Unicode conversion and comparison.
26:
27: Version: HFS Plus 1.0
28:
29: Written by: Mark Day
30:
31: Copyright: � 1996-1999 by Apple Computer, Inc., all rights reserved.
32:
33: File Ownership:
34:
35: DRI: Mark Day
36:
37: Other Contact: Don Brady
38:
39: Technology: xxx put technology here xxx
40:
41: Writers:
42:
43: (DSH) Deric Horn
44: (msd) Mark Day
45: (djb) Don Brady
46:
47: Change History (most recent first):
48: <MOSXS> 6/10/99 djb Add support for Euro Sign (0x20AC) to MacRoman/Unicode conversions.
49: <MOSXS> 2/09/99 djb Fix UnicodeToMacRoman to handle a terminating decomposed char.
50: <MOSXS> 1/22/99 djb Add more TARGET_OS_MAC conditionals to remove orphaned code.
51: <MOSXS> 7/6/98 djb Handle hi-bit Mac Roman characters in basic latin conversions (radar #2247519).
52: <MOSXS> 6/11/98 PPD Added a few special-case ASCII/Unicode mappings to cover installer's needs.
53:
54: <CS41> 1/28/98 msd Bug 2207446: When mangling a name, check to see if the Unicode
55: Converter is installed before we call it.
56: <CS40> 1/21/98 msd Bug 2206836: If a name contains a colon, change it to question
57: mark and mangle the name.
58: <CS39> 12/11/97 msd For Metrowerks and test tools, call the Get_xxx routines to get
59: the Unicode table addresses.
60: <CS38> 12/10/97 djb Radar #2005461, don't use fallback chars when converting to
61: Unicode, instead let the client (Catalog) retry with MacRoman.
62: <CS37> 12/2/97 DSH Conditionalize out some unicode related routines for DFA
63: <CS36> 11/26/97 djb Radar #2005461,2005688 don't swallow kTECPartialCharErr errors!
64: <CS35> 11/17/97 djb Name mangling was broken with decomposed Unicode.
65: <CS34> 11/16/97 djb Radar #2001928 - use kUnicodeCanonicalDecompVariant variant.
66: <CS33> 11/11/97 DSH Use Get_gLowerCaseTable for DiskFirstAid builds to avoid loading
67: in a branch to the table.
68: <CS32> 11/7/97 msd Replace FastSimpleCompareStrings with FastUnicodeCompare (which
69: handles ignorable Unicode characters). Remove the wrapper
70: routine, CompareUnicodeNames, and have its callers call
71: FastUnicodeCompare directly.
72: <CS31> 10/17/97 djb Change kUnicodeUseHFSPlusMapping to kUnicodeUseLatestMapping.
73: <CS30> 10/17/97 msd Fix some type casts for char pointers.
74: <CS29> 10/13/97 djb Add new SPIs for Finder View font (radar #1679073).
75: <CS28> 10/1/97 djb Preserve current heap zone in InitializeEncodingContext routine
76: (radar #1682686).
77: <CS27> 9/17/97 djb Handle kTECPartialCharErr errors in ConvertHFSNameToUnicode.
78: <CS26> 9/16/97 msd In MockConvertFromPStringToUnicode, use pragma unused instead of
79: commenting out unused parameter (so SC will compile it).
80: <CS25> 9/15/97 djb Fix MockConverters to do either 7-bit ascii or else mangle the
81: name (radar #1672388). Use 'p2u#' resource for bootstrapping
82: Unicode. Make sure InitializeEncodingContext uses System heap.
83: <CS24> 9/10/97 msd Make InitializeEncodingContext public.
84: <CS23> 9/7/97 djb Handle '�' char in BasicLatinUnicode converter.
85: <CS22> 9/4/97 djb Add logging to BasicLatinUnicodeToPascal.
86: <CS21> 8/26/97 djb Make FastSimpleCompareStrings faster. Add
87: BasicLatinUnicodeToPascal to make 7-bit ascii conversions
88: faster.
89: <CS20> 8/14/97 djb Add FastRelString here (to be next to the data tables).
90: <CS19> 7/21/97 djb LogEndTime now takes an error code.
91: <CS18> 7/18/97 msd Include LowMemPriv.h, Gestalt.h, TextUtils.h.
92: <CS17> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
93: collision
94: <CS16> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
95: <CS15> 7/8/97 DSH InitializeUnicode changed its API
96: <CS14> 7/1/97 DSH SC, DFA complier, requires parameters in functions. #pragma'd
97: them out to eliminate C warnings.
98: <CS13> 6/30/97 msd Remove unused parameter warnings in FallbackProc by commenting
99: out unused parameter names.
100: <CS12> 6/26/97 DSH FallbackProc declare variables before useage for SC,
101: MockConverters no longer static for DFA.
102: <CS11> 6/25/97 msd In function InitStaticUnicodeConverter, the variable fsVars was
103: being used before being initialized.
104: <CS10> 6/24/97 DSH Runtime checks to call through CFM or static linked routines.
105: <CS9> 6/20/97 msd Re-introduce fix from <CS7>. Fix another missing cast. Remove a
106: spurious semicolon.
107: <CS8> 6/18/97 djb Add more ConversionContexts routines. Improved file mangling.
108: <CS7> 6/16/97 msd Add a missing cast in GetFileIDString.
109: <CS6> 6/13/97 djb Added support for long filenames. Switched to
110: ConvertUnicodeToHFSName, ConvertHFSNameToUnicode, and
111: CompareUnicodeNames.
112: <CS5> 6/4/97 djb Use system script instead of macRoman.
113: <CS4> 5/19/97 djb Add call to LockMappingTable so tables won't move!
114: <CS3> 5/9/97 djb Include HFSInstrumentation.h
115: <CS2> 5/7/97 djb Add summary traces. Add FastSimpleCompareStrings routine.
116: <CS1> 4/24/97 djb first checked in
117: <HFS5> 3/27/97 djb Add calls to real Unicode conversion routines.
118: <HFS4> 2/6/97 msd Add conditional code to use real Unicode comparison routines
119: (default to off).
120: <HFS3> 1/6/97 djb Fix HFSUnicodeCompare - the final comparison of length1 and
121: length2 was backwards.
122: <HFS2> 12/12/96 msd Use precompiled headers.
123: <HFS1> 12/12/96 msd first checked in
124:
125: */
126:
127: #include "../../hfs_macos_defs.h"
128: #include "UCStringCompareData.h"
129:
130: #include "../headers/FileMgrInternal.h"
131: #include "../headers/HFSUnicodeWrappers.h"
132:
133: #include "ConvertUTF.h"
134:
135: enum {
136: kMinFileExtensionChars = 1, // does not include dot
137: kMaxFileExtensionChars = 5 // does not include dot
138: };
139:
140: #define kASCIIPiSymbol 0xB9
141: #define kASCIIMicroSign 0xB5
142: #define kASCIIGreekDelta 0xC6
143:
144:
145: #define Is7BitASCII(c) ( (c) >= 0x20 && (c) <= 0x7F )
146:
147: #define IsSpecialASCIIChar(c) ( (c) == (UInt8) kASCIIMicroSign || (c) == (UInt8) kASCIIPiSymbol || (c) == (UInt8) kASCIIGreekDelta )
148:
149: // Note: '�' has two Unicode representations 0x00B5 (micro sign) and 0x03BC (greek)
150: // '�' has two Unicode representations 0x2206 (increment) and 0x0394 (greek)
151: #define IsSpecialUnicodeChar(c) ( (c) == 0x00B5 || (c) == 0x03BC || (c) == 0x03C0 || (c) == 0x2206 || (c) == 0x0394 )
152:
153: #define IsHexDigit(c) ( ((c) >= (UInt8) '0' && (c) <= (UInt8) '9') || ((c) >= (UInt8) 'A' && (c) <= (UInt8) 'F') )
154:
155:
156: static void GetFilenameExtension( ItemCount length, ConstUniCharArrayPtr unicodeStr, Str15 extStr );
157:
158: static void GetFileIDString( HFSCatalogNodeID fileID, Str15 fileIDStr );
159:
160: static void AppendPascalString( ConstStr15Param src, Str31 dst );
161:
162: static UInt32 HexStringToInteger( UInt32 length, const UInt8 *hexStr );
163:
164:
165:
166: //
167: // Get filename extension (if any) as a pascal string
168: //
169: #if TARGET_API_MAC_OS8
170: static void
171: GetFilenameExtension( ItemCount length, ConstUniCharArrayPtr unicodeStr, Str15 extStr )
172: {
173: UInt32 i;
174: UniChar c;
175: UInt16 extChars; // number of extension characters (excluding the dot)
176: UInt16 maxExtChars;
177: Boolean foundExtension;
178:
179:
180: extStr[0] = (UInt8) 0; // assume there's no extension
181:
182: if ( length < 3 )
183: return; // sorry, "x.y" is smallest possible extension
184:
185: if ( length < (kMaxFileExtensionChars + 2) )
186: maxExtChars = length - 2; // we need at least one prefix character and dot
187: else
188: maxExtChars = kMaxFileExtensionChars;
189:
190: i = length;
191: extChars = 0;
192: foundExtension = false;
193:
194: while ( extChars <= maxExtChars )
195: {
196: c = unicodeStr[--i];
197:
198: if ( c == (UniChar) '.' ) // look for leading dot
199: {
200: if ( extChars > 0 ) // cannot end with a dot
201: foundExtension = true;
202: break;
203: }
204:
205: if ( Is7BitASCII(c) || IsSpecialUnicodeChar(c) )
206: ++extChars;
207: else
208: break;
209: }
210:
211: // if we found one then copy it
212: if ( foundExtension )
213: {
214: UInt8 *extStrPtr = extStr;
215: const UniChar *unicodeStrPtr = &unicodeStr[i]; // point to dot char
216:
217: *(extStrPtr++) = extChars + 1; // set length to extension chars plus dot
218:
219: for ( i = 0; i <= extChars; ++i )
220: {
221: c = *(unicodeStrPtr++);
222:
223: // map any special characters
224: switch (c)
225: {
226: case 0x00B5: // micro sign
227: case 0x03BC: // greek mu
228: c = (UniChar) '�';
229: break;
230:
231: case 0x03C0: // greek pi
232: c = (UniChar) '�';
233: break;
234:
235: case 0x2206: // increment sign
236: case 0x0394: // greek capital delta
237: c = (UniChar) '�';
238: break;
239: }
240:
241: *(extStrPtr++) = (UInt8) c; // copy/convert to ascii
242: }
243: }
244:
245: } // end GetFilenameExtension
246: #endif /* TARGET_API_MAC_OS8 */
247:
248:
249: //
250: // Count filename extension characters (if any)
251: //
252: static UInt32
253: CountFilenameExtensionChars( const unsigned char * filename, UInt32 length )
254: {
255: UInt32 i;
256: UniChar c;
257: UInt32 extChars; // number of extension characters (excluding the dot)
258: UInt16 maxExtChars;
259: Boolean foundExtension;
260:
261:
262: if (length == kUndefinedStrLen)
263: length = strlen(filename);
264:
265: if ( length < 3 )
266: return 0; // sorry, "x.y" is smallest possible extension
267:
268: if ( length < (kMaxFileExtensionChars + 2) )
269: maxExtChars = length - 2; // we need at least on prefix character and dot
270: else
271: maxExtChars = kMaxFileExtensionChars;
272:
273: extChars = 0; // assume there's no extension
274: i = length - 1; // index to last ascii character
275: foundExtension = false;
276:
277: while ( extChars <= maxExtChars )
278: {
279: c = filename[i--];
280:
281: if ( c == (UInt8) '.' ) // look for leading dot
282: {
283: if ( extChars > 0 ) // cannot end with a dot
284: return (extChars);
285:
286: break;
287: }
288:
289: if ( Is7BitASCII(c) || IsSpecialASCIIChar(c) )
290: ++extChars;
291: else
292: break;
293: }
294:
295: return 0;
296:
297: } // end CountFilenameExtensionChars
298:
299:
300: //
301: // Convert file ID into a hexidecimal string with no leading zeros
302: //
303: #if TARGET_API_MAC_OS8
304: static void
305: GetFileIDString( HFSCatalogNodeID fileID, Str15 fileIDStr )
306: {
307: SInt32 i, b;
308: static UInt8 *translate = (UInt8 *) "0123456789ABCDEF";
309: UInt8 c;
310:
311: fileIDStr[1] = '#';
312:
313: for ( i = 1, b = 28; b >= 0; b -= 4 )
314: {
315: c = *(translate + ((fileID >> b) & 0x0000000F));
316:
317: // if its not a leading zero add it to our string
318: if ( (c != (UInt8) '0') || (i > 1) || (b == 0) )
319: fileIDStr[++i] = c;
320: }
321:
322: fileIDStr[0] = (UInt8) i;
323:
324: } // end GetFileIDString
325: #endif /* TARGET_API_MAC_OS8 */
326:
327:
328: //
329: // Append a suffix to a pascal string
330: //
331: #if TARGET_API_MAC_OS8
332: static void
333: AppendPascalString( ConstStr15Param src, Str31 dst )
334: {
335: UInt32 i, j;
336: UInt32 srcLen;
337:
338: srcLen = StrLength(src);
339:
340: if ( (srcLen + StrLength(dst)) > 31 ) // safety net
341: return;
342:
343: i = dst[0] + 1; // get end of dst
344:
345: for (j = 1; j <= srcLen; ++j)
346: dst[i++] = src[j];
347:
348: dst[0] += srcLen;
349:
350: } // end AppendPascalString
351: #endif /* TARGET_API_MAC_OS8 */
352:
353:
354: HFSCatalogNodeID
355: GetEmbeddedFileID(const unsigned char * filename, UInt32 length, UInt32 *prefixLength)
356: {
357: short extChars;
358: short i;
359: UInt8 c; // current character in filename
360:
361: *prefixLength = 0;
362:
363: if ( filename == NULL )
364: return 0;
365:
366: if (length == kUndefinedStrLen)
367: length = strlen(filename);
368:
369: if ( length < 4 )
370: return 0; // too small to have a file ID
371:
372: if ( length >= 6 ) // big enough for a file ID (#10) and an extension (.x) ?
373: extChars = CountFilenameExtensionChars(filename, length);
374: else
375: extChars = 0;
376:
377: if ( extChars > 0 )
378: length -= (extChars + 1); // skip dot plus extension characters
379:
380: // scan for file id digits...
381: for ( i = length - 1; i >= 0; --i)
382: {
383: c = filename[i];
384:
385: if ( c == '#' ) // look for file ID marker
386: {
387: if ( (length - i) < 3 )
388: break; // too small to be a file ID
389:
390: *prefixLength = i;
391: return HexStringToInteger(length - i - 1, &filename[i+1]);
392: }
393:
394: if ( !IsHexDigit(c) )
395: break; // file ID string must have hex digits
396: }
397:
398: return 0;
399:
400: } // end GetEmbeddedFileID
401:
402:
403: //_______________________________________________________________________
404:
405: static UInt32
406: HexStringToInteger (UInt32 length, const UInt8 *hexStr)
407: {
408: UInt32 value; // decimal value represented by the string
409: short i;
410: UInt8 c; // next character in buffer
411: const UInt8 *p; // pointer to character string
412:
413: value = 0;
414: p = hexStr;
415:
416: for ( i = 0; i < length; ++i )
417: {
418: c = *p++;
419:
420: if (c >= '0' && c <= '9')
421: {
422: value = value << 4;
423: value += (UInt32) c - (UInt32) '0';
424: }
425: else if (c >= 'A' && c <= 'F')
426: {
427: value = value << 4;
428: value += 10 + ((unsigned int) c - (unsigned int) 'A');
429: }
430: else
431: {
432: return 0; // oops, how did this character get in here?
433: }
434: }
435:
436: return value;
437:
438: } // end HexStringToInteger
439:
440:
441: //_______________________________________________________________________
442: //
443: // Routine: FastRelString
444: //
445: // Output: returns -1 if str1 < str2
446: // returns 1 if str1 > str2
447: // return 0 if equal
448: //
449: //_______________________________________________________________________
450:
451: extern unsigned short gCompareTable[];
452:
453: SInt32 FastRelString( ConstStr255Param str1, ConstStr255Param str2 )
454: {
455: UInt16* compareTable;
456: SInt32 bestGuess;
457: UInt8 length, length2;
458: UInt8 delta;
459:
460: delta = 0;
461: length = *(str1++);
462: length2 = *(str2++);
463:
464: if (length == length2)
465: bestGuess = 0;
466: else if (length < length2)
467: {
468: bestGuess = -1;
469: delta = length2 - length;
470: }
471: else
472: {
473: bestGuess = 1;
474: length = length2;
475: }
476:
477: compareTable = (UInt16*) gCompareTable;
478:
479: while (length--)
480: {
481: UInt8 aChar, bChar;
482:
483: aChar = *(str1++);
484: bChar = *(str2++);
485:
486: if (aChar != bChar) // If they don't match exacly, do case conversion
487: {
488: UInt16 aSortWord, bSortWord;
489:
490: aSortWord = compareTable[aChar];
491: if (bChar == 0 && delta == 1) {
492: bChar = *(str2++); /* skip over embedded null */
493: bestGuess = 0;
494: }
495: bSortWord = compareTable[bChar];
496:
497: if (aSortWord > bSortWord)
498: return 1;
499:
500: if (aSortWord < bSortWord)
501: return -1;
502: }
503:
504: // If characters match exactly, then go on to next character immediately without
505: // doing any extra work.
506: }
507:
508: // if you got to here, then return bestGuess
509: return bestGuess;
510: }
511:
512:
513:
514: //
515: // FastUnicodeCompare - Compare two Unicode strings; produce a relative ordering
516: //
517: // IF RESULT
518: // --------------------------
519: // str1 < str2 => -1
520: // str1 = str2 => 0
521: // str1 > str2 => +1
522: //
523: // The lower case table starts with 256 entries (one for each of the upper bytes
524: // of the original Unicode char). If that entry is zero, then all characters with
525: // that upper byte are already case folded. If the entry is non-zero, then it is
526: // the _index_ (not byte offset) of the start of the sub-table for the characters
527: // with that upper byte. All ignorable characters are folded to the value zero.
528: //
529: // In pseudocode:
530: //
531: // Let c = source Unicode character
532: // Let table[] = lower case table
533: //
534: // lower = table[highbyte(c)]
535: // if (lower == 0)
536: // lower = c
537: // else
538: // lower = table[lower+lowbyte(c)]
539: //
540: // if (lower == 0)
541: // ignore this character
542: //
543: // To handle ignorable characters, we now need a loop to find the next valid character.
544: // Also, we can't pre-compute the number of characters to compare; the string length might
545: // be larger than the number of non-ignorable characters. Further, we must be able to handle
546: // ignorable characters at any point in the string, including as the first or last characters.
547: // We use a zero value as a sentinel to detect both end-of-string and ignorable characters.
548: // Since the File Manager doesn't prevent the NUL character (value zero) as part of a filename,
549: // the case mapping table is assumed to map u+0000 to some non-zero value (like 0xFFFF, which is
550: // an invalid Unicode character).
551: //
552: // Pseudocode:
553: //
554: // while (1) {
555: // c1 = GetNextValidChar(str1) // returns zero if at end of string
556: // c2 = GetNextValidChar(str2)
557: //
558: // if (c1 != c2) break // found a difference
559: //
560: // if (c1 == 0) // reached end of string on both strings at once?
561: // return 0; // yes, so strings are equal
562: // }
563: //
564: // // When we get here, c1 != c2. So, we just need to determine which one is less.
565: // if (c1 < c2)
566: // return -1;
567: // else
568: // return 1;
569: //
570:
571: extern UInt16 gLowerCaseTable[];
572: extern UInt16 gLatinCaseFold[];
573:
574: SInt32 FastUnicodeCompare ( register ConstUniCharArrayPtr str1, register ItemCount length1,
575: register ConstUniCharArrayPtr str2, register ItemCount length2)
576: {
577: register UInt16 c1,c2;
578: register UInt16 temp;
579: register UInt16* lowerCaseTable;
580:
581: lowerCaseTable = (UInt16*) gLowerCaseTable;
582:
583: while (1) {
584: /* Set default values for c1, c2 in case there are no more valid chars */
585: c1 = 0;
586: c2 = 0;
587:
588: /* Find next non-ignorable char from str1, or zero if no more */
589: while (length1 && c1 == 0) {
590: c1 = *(str1++);
591: --length1;
592: /* check for basic latin first */
593: if (c1 < 0x0100) {
594: c1 = gLatinCaseFold[c1];
595: break;
596: }
597: /* case fold if neccessary */
598: if ((temp = lowerCaseTable[c1>>8]) != 0)
599: c1 = lowerCaseTable[temp + (c1 & 0x00FF)];
600: }
601:
602:
603: /* Find next non-ignorable char from str2, or zero if no more */
604: while (length2 && c2 == 0) {
605: c2 = *(str2++);
606: --length2;
607: /* check for basic latin first */
608: if (c2 < 0x0100) {
609: if ((c2 = gLatinCaseFold[c2]) != 0)
610: break;
611: else
612: continue; /* ignore this character */
613: }
614: /* case fold if neccessary */
615: if ((temp = lowerCaseTable[c2>>8]) != 0)
616: c2 = lowerCaseTable[temp + (c2 & 0x00FF)];
617: }
618:
619: if (c1 != c2) // found a difference, so stop looping
620: break;
621:
622: if (c1 == 0) // did we reach the end of both strings at the same time?
623: return 0; // yes, so strings are equal
624: }
625:
626: if (c1 < c2)
627: return -1;
628: else
629: return 1;
630: }
631:
632:
633: OSErr
634: ConvertUTF8ToUnicode(ByteCount srcLen, const unsigned char* srcStr, ByteCount maxDstLen,
635: ByteCount *actualDstLen, UniCharArrayPtr dstStr)
636: {
637: ConversionResult result;
638: UTF8* sourceStart;
639: UTF8* sourceEnd;
640: UTF16* targetStart;
641: UTF16* targetEnd;
642:
643: sourceStart = (UTF8*) srcStr;
644: sourceEnd = sourceStart + srcLen;
645: targetStart = (UTF16*) dstStr;
646: targetEnd = targetStart + maxDstLen/2;
647:
648: result = ConvertUTF8toUTF16 (&sourceStart, sourceEnd, &targetStart, targetEnd);
649:
650: *actualDstLen = (targetStart - dstStr) * sizeof(UniChar);
651:
652: if (result == targetExhausted)
653: return kTECOutputBufferFullStatus;
654: else if (result == sourceExhausted)
655: return kTextMalformedInputErr;
656:
657: return noErr;
658: }
659:
660:
661: OSErr
662: ConvertUnicodeToUTF8(ByteCount srcLen, ConstUniCharArrayPtr srcStr, ByteCount maxDstLen,
663: ByteCount *actualDstLen, unsigned char* dstStr)
664: {
665: ConversionResult result;
666: UTF16* sourceStart;
667: UTF16* sourceEnd;
668: UTF8* targetStart;
669: UTF8* targetEnd;
670: ByteCount outputLength;
671:
672: sourceStart = (UTF16*) srcStr;
673: sourceEnd = (UTF16*) ((char*) srcStr + srcLen);
674: targetStart = (UTF8*) dstStr;
675: targetEnd = targetStart + maxDstLen;
676:
677: result = ConvertUTF16toUTF8 (&sourceStart, sourceEnd, &targetStart, targetEnd);
678:
679: *actualDstLen = outputLength = targetStart - dstStr;
680:
681: if (result == targetExhausted)
682: return kTECOutputBufferFullStatus;
683: else if (result == sourceExhausted)
684: return kTECPartialCharErr;
685:
686: if (outputLength >= maxDstLen)
687: return kTECOutputBufferFullStatus;
688:
689: dstStr[outputLength] = 0; /* also add null termination */
690:
691: return noErr;
692: }
693:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.