// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * * Copyright (C) 2003-2013, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* * file name: ucm.c * encoding: UTF-8 * tab size: 8 (not used) * indentation:4 * * created on: 2003jun20 * created by: Markus W. Scherer * * This file reads a .ucm file, stores its mappings and sorts them. * It implements handling of Unicode conversion mappings from .ucm files * for makeconv, canonucm, rptp2ucm, etc. * * Unicode code point sequences with a length of more than 1, * as well as byte sequences with more than 4 bytes or more than one complete * character sequence are handled to support m:n mappings. */ #include "unicode/utypes.h" #include "unicode/ustring.h" #include "cstring.h" #include "cmemory.h" #include "filestrm.h" #include "uarrsort.h" #include "ucnvmbcs.h" #include "ucnv_bld.h" #include "ucnv_ext.h" #include "uparse.h" #include "ucm.h" #include <stdio.h> #if !UCONFIG_NO_CONVERSION /* -------------------------------------------------------------------------- */ static void printMapping(UCMapping *m, UChar32 *codePoints, uint8_t *bytes, FILE *f) { int32_t j; for(j=0; j<m->uLen; ++j) { fprintf(f, "<U%04lX>", (long)codePoints[j]); } fputc(' ', f); for(j=0; j<m->bLen; ++j) { fprintf(f, "\\x%02X", bytes[j]); } if(m->f>=0) { fprintf(f, " |%u\n", m->f); } else { fputs("\n", f); } } U_CAPI void U_EXPORT2 ucm_printMapping(UCMTable *table, UCMapping *m, FILE *f) { printMapping(m, UCM_GET_CODE_POINTS(table, m), UCM_GET_BYTES(table, m), f); } U_CAPI void U_EXPORT2 ucm_printTable(UCMTable *table, FILE *f, UBool byUnicode) { UCMapping *m; int32_t i, length; m=table->mappings; length=table->mappingsLength; if(byUnicode) { for(i=0; i<length; ++m, ++i) { ucm_printMapping(table, m, f); } } else { const int32_t *map=table->reverseMap; for(i=0; i<length; ++i) { ucm_printMapping(table, m+map[i], f); } } } /* mapping comparisons ------------------------------------------------------ */ static int32_t compareUnicode(UCMTable *lTable, const UCMapping *l, UCMTable *rTable, const UCMapping *r) { const UChar32 *lu, *ru; int32_t result, i, length; if(l->uLen==1 && r->uLen==1) { /* compare two single code points */ return l->u-r->u; } /* get pointers to the code point sequences */ lu=UCM_GET_CODE_POINTS(lTable, l); ru=UCM_GET_CODE_POINTS(rTable, r); /* get the minimum length */ if(l->uLen<=r->uLen) { length=l->uLen; } else { length=r->uLen; } /* compare the code points */ for(i=0; i<length; ++i) { result=lu[i]-ru[i]; if(result!=0) { return result; } } /* compare the lengths */ return l->uLen-r->uLen; } static int32_t compareBytes(UCMTable *lTable, const UCMapping *l, UCMTable *rTable, const UCMapping *r, UBool lexical) { const uint8_t *lb, *rb; int32_t result, i, length; /* * A lexical comparison is used for sorting in the builder, to allow * an efficient search for a byte sequence that could be a prefix * of a previously entered byte sequence. * * Comparing by lengths first is for compatibility with old .ucm tools * like canonucm and rptp2ucm. */ if(lexical) { /* get the minimum length and continue */ if(l->bLen<=r->bLen) { length=l->bLen; } else { length=r->bLen; } } else { /* compare lengths first */ result=l->bLen-r->bLen; if(result!=0) { return result; } else { length=l->bLen; } } /* get pointers to the byte sequences */ lb=UCM_GET_BYTES(lTable, l); rb=UCM_GET_BYTES(rTable, r); /* compare the bytes */ for(i=0; i<length; ++i) { result=lb[i]-rb[i]; if(result!=0) { return result; } } /* compare the lengths */ return l->bLen-r->bLen; } /* compare UCMappings for sorting */ static int32_t compareMappings(UCMTable *lTable, const UCMapping *l, UCMTable *rTable, const UCMapping *r, UBool uFirst) { int32_t result; /* choose which side to compare first */ if(uFirst) { /* Unicode then bytes */ result=compareUnicode(lTable, l, rTable, r); if(result==0) { result=compareBytes(lTable, l, rTable, r, FALSE); /* not lexically, like canonucm */ } } else { /* bytes then Unicode */ result=compareBytes(lTable, l, rTable, r, TRUE); /* lexically, for builder */ if(result==0) { result=compareUnicode(lTable, l, rTable, r); } } if(result!=0) { return result; } /* compare the flags */ return l->f-r->f; } U_CDECL_BEGIN /* sorting by Unicode first sorts mappings directly */ static int32_t U_CALLCONV compareMappingsUnicodeFirst(const void *context, const void *left, const void *right) { return compareMappings( (UCMTable *)context, (const UCMapping *)left, (UCMTable *)context, (const UCMapping *)right, TRUE); } /* sorting by bytes first sorts the reverseMap; use indirection to mappings */ static int32_t U_CALLCONV compareMappingsBytesFirst(const void *context, const void *left, const void *right) { UCMTable *table=(UCMTable *)context; int32_t l=*(const int32_t *)left, r=*(const int32_t *)right; return compareMappings( table, table->mappings+l, table, table->mappings+r, FALSE); } U_CDECL_END U_CAPI void U_EXPORT2 ucm_sortTable(UCMTable *t) { UErrorCode errorCode; int32_t i; if(t->isSorted) { return; } errorCode=U_ZERO_ERROR; /* 1. sort by Unicode first */ uprv_sortArray(t->mappings, t->mappingsLength, sizeof(UCMapping), compareMappingsUnicodeFirst, t, FALSE, &errorCode); /* build the reverseMap */ if(t->reverseMap==NULL) { /* * allocate mappingsCapacity instead of mappingsLength so that * if mappings are added, the reverseMap need not be * reallocated each time * (see ucm_moveMappings() and ucm_addMapping()) */ t->reverseMap=(int32_t *)uprv_malloc(t->mappingsCapacity*sizeof(int32_t)); if(t->reverseMap==NULL) { fprintf(stderr, "ucm error: unable to allocate reverseMap\n"); exit(U_MEMORY_ALLOCATION_ERROR); } } for(i=0; i<t->mappingsLength; ++i) { t->reverseMap[i]=i; } /* 2. sort reverseMap by mappings bytes first */ uprv_sortArray(t->reverseMap, t->mappingsLength, sizeof(int32_t), compareMappingsBytesFirst, t, FALSE, &errorCode); if(U_FAILURE(errorCode)) { fprintf(stderr, "ucm error: sortTable()/uprv_sortArray() fails - %s\n", u_errorName(errorCode)); exit(errorCode); } t->isSorted=TRUE; } /* * remove mappings with their move flag set from the base table * and move some of them (with UCM_MOVE_TO_EXT) to the extension table */ U_CAPI void U_EXPORT2 ucm_moveMappings(UCMTable *base, UCMTable *ext) { UCMapping *mb, *mbLimit; int8_t flag; mb=base->mappings; mbLimit=mb+base->mappingsLength; while(mb<mbLimit) { flag=mb->moveFlag; if(flag!=0) { /* reset the move flag */ mb->moveFlag=0; if(ext!=NULL && (flag&UCM_MOVE_TO_EXT)) { /* add the mapping to the extension table */ ucm_addMapping(ext, mb, UCM_GET_CODE_POINTS(base, mb), UCM_GET_BYTES(base, mb)); } /* remove this mapping: move the last base mapping down and overwrite the current one */ if(mb<(mbLimit-1)) { uprv_memcpy(mb, mbLimit-1, sizeof(UCMapping)); } --mbLimit; --base->mappingsLength; base->isSorted=FALSE; } else { ++mb; } } } enum { NEEDS_MOVE=1, HAS_ERRORS=2 }; static uint8_t checkBaseExtUnicode(UCMStates *baseStates, UCMTable *base, UCMTable *ext, UBool moveToExt, UBool intersectBase) { (void)baseStates; UCMapping *mb, *me, *mbLimit, *meLimit; int32_t cmp; uint8_t result; mb=base->mappings; mbLimit=mb+base->mappingsLength; me=ext->mappings; meLimit=me+ext->mappingsLength; result=0; for(;;) { /* skip irrelevant mappings on both sides */ for(;;) { if(mb==mbLimit) { return result; } if((0<=mb->f && mb->f<=2) || mb->f==4) { break; } ++mb; } for(;;) { if(me==meLimit) { return result; } if((0<=me->f && me->f<=2) || me->f==4) { break; } ++me; } /* compare the base and extension mappings */ cmp=compareUnicode(base, mb, ext, me); if(cmp<0) { if(intersectBase && (intersectBase!=2 || mb->bLen>1)) { /* * mapping in base but not in ext, move it * * if ext is DBCS, move DBCS mappings here * and check SBCS ones for Unicode prefix below */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; /* does mb map from an input sequence that is a prefix of me's? */ } else if( mb->uLen<me->uLen && 0==uprv_memcmp(UCM_GET_CODE_POINTS(base, mb), UCM_GET_CODE_POINTS(ext, me), 4*mb->uLen) ) { if(moveToExt) { /* mark this mapping to be moved to the extension table */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; } else { fprintf(stderr, "ucm error: the base table contains a mapping whose input sequence\n" " is a prefix of the input sequence of an extension mapping\n"); ucm_printMapping(base, mb, stderr); ucm_printMapping(ext, me, stderr); result|=HAS_ERRORS; } } ++mb; } else if(cmp==0) { /* * same output: remove the extension mapping, * otherwise treat as an error */ if( mb->f==me->f && mb->bLen==me->bLen && 0==uprv_memcmp(UCM_GET_BYTES(base, mb), UCM_GET_BYTES(ext, me), mb->bLen) ) { me->moveFlag|=UCM_REMOVE_MAPPING; result|=NEEDS_MOVE; } else if(intersectBase) { /* mapping in base but not in ext, move it */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; } else { fprintf(stderr, "ucm error: the base table contains a mapping whose input sequence\n" " is the same as the input sequence of an extension mapping\n" " but it maps differently\n"); ucm_printMapping(base, mb, stderr); ucm_printMapping(ext, me, stderr); result|=HAS_ERRORS; } ++mb; } else /* cmp>0 */ { ++me; } } } static uint8_t checkBaseExtBytes(UCMStates *baseStates, UCMTable *base, UCMTable *ext, UBool moveToExt, UBool intersectBase) { UCMapping *mb, *me; int32_t *baseMap, *extMap; int32_t b, e, bLimit, eLimit, cmp; uint8_t result; UBool isSISO; baseMap=base->reverseMap; extMap=ext->reverseMap; b=e=0; bLimit=base->mappingsLength; eLimit=ext->mappingsLength; result=0; isSISO=(UBool)(baseStates->outputType==MBCS_OUTPUT_2_SISO); for(;;) { /* skip irrelevant mappings on both sides */ for(;; ++b) { if(b==bLimit) { return result; } mb=base->mappings+baseMap[b]; if(intersectBase==2 && mb->bLen==1) { /* * comparing a base against a DBCS extension: * leave SBCS base mappings alone */ continue; } if(mb->f==0 || mb->f==3) { break; } } for(;;) { if(e==eLimit) { return result; } me=ext->mappings+extMap[e]; if(me->f==0 || me->f==3) { break; } ++e; } /* compare the base and extension mappings */ cmp=compareBytes(base, mb, ext, me, TRUE); if(cmp<0) { if(intersectBase) { /* mapping in base but not in ext, move it */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; /* * does mb map from an input sequence that is a prefix of me's? * for SI/SO tables, a single byte is never a prefix because it * occurs in a separate single-byte state */ } else if( mb->bLen<me->bLen && (!isSISO || mb->bLen>1) && 0==uprv_memcmp(UCM_GET_BYTES(base, mb), UCM_GET_BYTES(ext, me), mb->bLen) ) { if(moveToExt) { /* mark this mapping to be moved to the extension table */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; } else { fprintf(stderr, "ucm error: the base table contains a mapping whose input sequence\n" " is a prefix of the input sequence of an extension mapping\n"); ucm_printMapping(base, mb, stderr); ucm_printMapping(ext, me, stderr); result|=HAS_ERRORS; } } ++b; } else if(cmp==0) { /* * same output: remove the extension mapping, * otherwise treat as an error */ if( mb->f==me->f && mb->uLen==me->uLen && 0==uprv_memcmp(UCM_GET_CODE_POINTS(base, mb), UCM_GET_CODE_POINTS(ext, me), 4*mb->uLen) ) { me->moveFlag|=UCM_REMOVE_MAPPING; result|=NEEDS_MOVE; } else if(intersectBase) { /* mapping in base but not in ext, move it */ mb->moveFlag|=UCM_MOVE_TO_EXT; result|=NEEDS_MOVE; } else { fprintf(stderr, "ucm error: the base table contains a mapping whose input sequence\n" " is the same as the input sequence of an extension mapping\n" " but it maps differently\n"); ucm_printMapping(base, mb, stderr); ucm_printMapping(ext, me, stderr); result|=HAS_ERRORS; } ++b; } else /* cmp>0 */ { ++e; } } } U_CAPI UBool U_EXPORT2 ucm_checkValidity(UCMTable *table, UCMStates *baseStates) { UCMapping *m, *mLimit; int32_t count; UBool isOK; m=table->mappings; mLimit=m+table->mappingsLength; isOK=TRUE; while(m<mLimit) { count=ucm_countChars(baseStates, UCM_GET_BYTES(table, m), m->bLen); if(count<1) { ucm_printMapping(table, m, stderr); isOK=FALSE; } ++m; } return isOK; } U_CAPI UBool U_EXPORT2 ucm_checkBaseExt(UCMStates *baseStates, UCMTable *base, UCMTable *ext, UCMTable *moveTarget, UBool intersectBase) { uint8_t result; /* if we have an extension table, we must always use precision flags */ if(base->flagsType&UCM_FLAGS_IMPLICIT) { fprintf(stderr, "ucm error: the base table contains mappings without precision flags\n"); return FALSE; } if(ext->flagsType&UCM_FLAGS_IMPLICIT) { fprintf(stderr, "ucm error: extension table contains mappings without precision flags\n"); return FALSE; } /* checking requires both tables to be sorted */ ucm_sortTable(base); ucm_sortTable(ext); /* check */ result= checkBaseExtUnicode(baseStates, base, ext, (UBool)(moveTarget!=NULL), intersectBase)| checkBaseExtBytes(baseStates, base, ext, (UBool)(moveTarget!=NULL), intersectBase); if(result&HAS_ERRORS) { return FALSE; } if(result&NEEDS_MOVE) { ucm_moveMappings(ext, NULL); ucm_moveMappings(base, moveTarget); ucm_sortTable(base); ucm_sortTable(ext); if(moveTarget!=NULL) { ucm_sortTable(moveTarget); } } return TRUE; } /* merge tables for rptp2ucm ------------------------------------------------ */ U_CAPI void U_EXPORT2 ucm_mergeTables(UCMTable *fromUTable, UCMTable *toUTable, const uint8_t *subchar, int32_t subcharLength, uint8_t subchar1) { UCMapping *fromUMapping, *toUMapping; int32_t fromUIndex, toUIndex, fromUTop, toUTop, cmp; ucm_sortTable(fromUTable); ucm_sortTable(toUTable); fromUMapping=fromUTable->mappings; toUMapping=toUTable->mappings; fromUTop=fromUTable->mappingsLength; toUTop=toUTable->mappingsLength; fromUIndex=toUIndex=0; while(fromUIndex<fromUTop && toUIndex<toUTop) { cmp=compareMappings(fromUTable, fromUMapping, toUTable, toUMapping, TRUE); if(cmp==0) { /* equal: roundtrip, nothing to do (flags are initially 0) */ ++fromUMapping; ++toUMapping; ++fromUIndex; ++toUIndex; } else if(cmp<0) { /* * the fromU mapping does not have a toU counterpart: * fallback Unicode->codepage */ if( (fromUMapping->bLen==subcharLength && 0==uprv_memcmp(UCM_GET_BYTES(fromUTable, fromUMapping), subchar, subcharLength)) || (subchar1!=0 && fromUMapping->bLen==1 && fromUMapping->b.bytes[0]==subchar1) ) { fromUMapping->f=2; /* SUB mapping */ } else { fromUMapping->f=1; /* normal fallback */ } ++fromUMapping; ++fromUIndex; } else { /* * the toU mapping does not have a fromU counterpart: * (reverse) fallback codepage->Unicode, copy it to the fromU table */ /* ignore reverse fallbacks to Unicode SUB */ if(!(toUMapping->uLen==1 && (toUMapping->u==0xfffd || toUMapping->u==0x1a))) { toUMapping->f=3; /* reverse fallback */ ucm_addMapping(fromUTable, toUMapping, UCM_GET_CODE_POINTS(toUTable, toUMapping), UCM_GET_BYTES(toUTable, toUMapping)); /* the table may have been reallocated */ fromUMapping=fromUTable->mappings+fromUIndex; } ++toUMapping; ++toUIndex; } } /* either one or both tables are exhausted */ while(fromUIndex<fromUTop) { /* leftover fromU mappings are fallbacks */ if( (fromUMapping->bLen==subcharLength && 0==uprv_memcmp(UCM_GET_BYTES(fromUTable, fromUMapping), subchar, subcharLength)) || (subchar1!=0 && fromUMapping->bLen==1 && fromUMapping->b.bytes[0]==subchar1) ) { fromUMapping->f=2; /* SUB mapping */ } else { fromUMapping->f=1; /* normal fallback */ } ++fromUMapping; ++fromUIndex; } while(toUIndex<toUTop) { /* leftover toU mappings are reverse fallbacks */ /* ignore reverse fallbacks to Unicode SUB */ if(!(toUMapping->uLen==1 && (toUMapping->u==0xfffd || toUMapping->u==0x1a))) { toUMapping->f=3; /* reverse fallback */ ucm_addMapping(fromUTable, toUMapping, UCM_GET_CODE_POINTS(toUTable, toUMapping), UCM_GET_BYTES(toUTable, toUMapping)); } ++toUMapping; ++toUIndex; } fromUTable->isSorted=FALSE; } /* separate extension mappings out of base table for rptp2ucm --------------- */ U_CAPI UBool U_EXPORT2 ucm_separateMappings(UCMFile *ucm, UBool isSISO) { UCMTable *table; UCMapping *m, *mLimit; int32_t type; UBool needsMove, isOK; table=ucm->base; m=table->mappings; mLimit=m+table->mappingsLength; needsMove=FALSE; isOK=TRUE; for(; m<mLimit; ++m) { if(isSISO && m->bLen==1 && (m->b.bytes[0]==0xe || m->b.bytes[0]==0xf)) { fprintf(stderr, "warning: removing illegal mapping from an SI/SO-stateful table\n"); ucm_printMapping(table, m, stderr); m->moveFlag|=UCM_REMOVE_MAPPING; needsMove=TRUE; continue; } type=ucm_mappingType( &ucm->states, m, UCM_GET_CODE_POINTS(table, m), UCM_GET_BYTES(table, m)); if(type<0) { /* illegal byte sequence */ printMapping(m, UCM_GET_CODE_POINTS(table, m), UCM_GET_BYTES(table, m), stderr); isOK=FALSE; } else if(type>0) { m->moveFlag|=UCM_MOVE_TO_EXT; needsMove=TRUE; } } if(!isOK) { return FALSE; } if(needsMove) { ucm_moveMappings(ucm->base, ucm->ext); return ucm_checkBaseExt(&ucm->states, ucm->base, ucm->ext, ucm->ext, FALSE); } else { ucm_sortTable(ucm->base); return TRUE; } } /* ucm parser --------------------------------------------------------------- */ U_CAPI int8_t U_EXPORT2 ucm_parseBytes(uint8_t bytes[UCNV_EXT_MAX_BYTES], const char *line, const char **ps) { const char *s=*ps; char *end; uint8_t byte; int8_t bLen; bLen=0; for(;;) { /* skip an optional plus sign */ if(bLen>0 && *s=='+') { ++s; } if(*s!='\\') { break; } if( s[1]!='x' || (byte=(uint8_t)uprv_strtoul(s+2, &end, 16), end)!=s+4 ) { fprintf(stderr, "ucm error: byte must be formatted as \\xXX (2 hex digits) - \"%s\"\n", line); return -1; } if(bLen==UCNV_EXT_MAX_BYTES) { fprintf(stderr, "ucm error: too many bytes on \"%s\"\n", line); return -1; } bytes[bLen++]=byte; s=end; } *ps=s; return bLen; } /* parse a mapping line; must not be empty */ U_CAPI UBool U_EXPORT2 ucm_parseMappingLine(UCMapping *m, UChar32 codePoints[UCNV_EXT_MAX_UCHARS], uint8_t bytes[UCNV_EXT_MAX_BYTES], const char *line) { const char *s; char *end; UChar32 cp; int32_t u16Length; int8_t uLen, bLen, f; s=line; uLen=bLen=0; /* parse code points */ for(;;) { /* skip an optional plus sign */ if(uLen>0 && *s=='+') { ++s; } if(*s!='<') { break; } if( s[1]!='U' || (cp=(UChar32)uprv_strtoul(s+2, &end, 16), end)==s+2 || *end!='>' ) { fprintf(stderr, "ucm error: Unicode code point must be formatted as <UXXXX> (1..6 hex digits) - \"%s\"\n", line); return FALSE; } if((uint32_t)cp>0x10ffff || U_IS_SURROGATE(cp)) { fprintf(stderr, "ucm error: Unicode code point must be 0..d7ff or e000..10ffff - \"%s\"\n", line); return FALSE; } if(uLen==UCNV_EXT_MAX_UCHARS) { fprintf(stderr, "ucm error: too many code points on \"%s\"\n", line); return FALSE; } codePoints[uLen++]=cp; s=end+1; } if(uLen==0) { fprintf(stderr, "ucm error: no Unicode code points on \"%s\"\n", line); return FALSE; } else if(uLen==1) { m->u=codePoints[0]; } else { UErrorCode errorCode=U_ZERO_ERROR; u_strFromUTF32(NULL, 0, &u16Length, codePoints, uLen, &errorCode); if( (U_FAILURE(errorCode) && errorCode!=U_BUFFER_OVERFLOW_ERROR) || u16Length>UCNV_EXT_MAX_UCHARS ) { fprintf(stderr, "ucm error: too many UChars on \"%s\"\n", line); return FALSE; } } s=u_skipWhitespace(s); /* parse bytes */ bLen=ucm_parseBytes(bytes, line, &s); if(bLen<0) { return FALSE; } else if(bLen==0) { fprintf(stderr, "ucm error: no bytes on \"%s\"\n", line); return FALSE; } else if(bLen<=4) { uprv_memcpy(m->b.bytes, bytes, bLen); } /* skip everything until the fallback indicator, even the start of a comment */ for(;;) { if(*s==0) { f=-1; /* no fallback indicator */ break; } else if(*s=='|') { f=(int8_t)(s[1]-'0'); if((uint8_t)f>4) { fprintf(stderr, "ucm error: fallback indicator must be |0..|4 - \"%s\"\n", line); return FALSE; } break; } ++s; } m->uLen=uLen; m->bLen=bLen; m->f=f; return TRUE; } /* general APIs ------------------------------------------------------------- */ U_CAPI UCMTable * U_EXPORT2 ucm_openTable() { UCMTable *table=(UCMTable *)uprv_malloc(sizeof(UCMTable)); if(table==NULL) { fprintf(stderr, "ucm error: unable to allocate a UCMTable\n"); exit(U_MEMORY_ALLOCATION_ERROR); } memset(table, 0, sizeof(UCMTable)); return table; } U_CAPI void U_EXPORT2 ucm_closeTable(UCMTable *table) { if(table!=NULL) { uprv_free(table->mappings); uprv_free(table->codePoints); uprv_free(table->bytes); uprv_free(table->reverseMap); uprv_free(table); } } U_CAPI void U_EXPORT2 ucm_resetTable(UCMTable *table) { if(table!=NULL) { table->mappingsLength=0; table->flagsType=0; table->unicodeMask=0; table->bytesLength=table->codePointsLength=0; table->isSorted=FALSE; } } U_CAPI void U_EXPORT2 ucm_addMapping(UCMTable *table, UCMapping *m, UChar32 codePoints[UCNV_EXT_MAX_UCHARS], uint8_t bytes[UCNV_EXT_MAX_BYTES]) { UCMapping *tm; UChar32 c; int32_t idx; if(table->mappingsLength>=table->mappingsCapacity) { /* make the mappings array larger */ if(table->mappingsCapacity==0) { table->mappingsCapacity=1000; } else { table->mappingsCapacity*=10; } table->mappings=(UCMapping *)uprv_realloc(table->mappings, table->mappingsCapacity*sizeof(UCMapping)); if(table->mappings==NULL) { fprintf(stderr, "ucm error: unable to allocate %d UCMappings\n", (int)table->mappingsCapacity); exit(U_MEMORY_ALLOCATION_ERROR); } if(table->reverseMap!=NULL) { /* the reverseMap must be reallocated in a new sort */ uprv_free(table->reverseMap); table->reverseMap=NULL; } } if(m->uLen>1 && table->codePointsCapacity==0) { table->codePointsCapacity=10000; table->codePoints=(UChar32 *)uprv_malloc(table->codePointsCapacity*4); if(table->codePoints==NULL) { fprintf(stderr, "ucm error: unable to allocate %d UChar32s\n", (int)table->codePointsCapacity); exit(U_MEMORY_ALLOCATION_ERROR); } } if(m->bLen>4 && table->bytesCapacity==0) { table->bytesCapacity=10000; table->bytes=(uint8_t *)uprv_malloc(table->bytesCapacity); if(table->bytes==NULL) { fprintf(stderr, "ucm error: unable to allocate %d bytes\n", (int)table->bytesCapacity); exit(U_MEMORY_ALLOCATION_ERROR); } } if(m->uLen>1) { idx=table->codePointsLength; table->codePointsLength+=m->uLen; if(table->codePointsLength>table->codePointsCapacity) { fprintf(stderr, "ucm error: too many code points in multiple-code point mappings\n"); exit(U_MEMORY_ALLOCATION_ERROR); } uprv_memcpy(table->codePoints+idx, codePoints, (size_t)m->uLen*4); m->u=idx; } if(m->bLen>4) { idx=table->bytesLength; table->bytesLength+=m->bLen; if(table->bytesLength>table->bytesCapacity) { fprintf(stderr, "ucm error: too many bytes in mappings with >4 charset bytes\n"); exit(U_MEMORY_ALLOCATION_ERROR); } uprv_memcpy(table->bytes+idx, bytes, m->bLen); m->b.idx=idx; } /* set unicodeMask */ for(idx=0; idx<m->uLen; ++idx) { c=codePoints[idx]; if(c>=0x10000) { table->unicodeMask|=UCNV_HAS_SUPPLEMENTARY; /* there are supplementary code points */ } else if(U_IS_SURROGATE(c)) { table->unicodeMask|=UCNV_HAS_SURROGATES; /* there are surrogate code points */ } } /* set flagsType */ if(m->f<0) { table->flagsType|=UCM_FLAGS_IMPLICIT; } else { table->flagsType|=UCM_FLAGS_EXPLICIT; } tm=table->mappings+table->mappingsLength++; uprv_memcpy(tm, m, sizeof(UCMapping)); table->isSorted=FALSE; } U_CAPI UCMFile * U_EXPORT2 ucm_open() { UCMFile *ucm=(UCMFile *)uprv_malloc(sizeof(UCMFile)); if(ucm==NULL) { fprintf(stderr, "ucm error: unable to allocate a UCMFile\n"); exit(U_MEMORY_ALLOCATION_ERROR); } memset(ucm, 0, sizeof(UCMFile)); ucm->base=ucm_openTable(); ucm->ext=ucm_openTable(); ucm->states.stateFlags[0]=MBCS_STATE_FLAG_DIRECT; ucm->states.conversionType=UCNV_UNSUPPORTED_CONVERTER; ucm->states.outputType=-1; ucm->states.minCharLength=ucm->states.maxCharLength=1; return ucm; } U_CAPI void U_EXPORT2 ucm_close(UCMFile *ucm) { if(ucm!=NULL) { ucm_closeTable(ucm->base); ucm_closeTable(ucm->ext); uprv_free(ucm); } } U_CAPI int32_t U_EXPORT2 ucm_mappingType(UCMStates *baseStates, UCMapping *m, UChar32 codePoints[UCNV_EXT_MAX_UCHARS], uint8_t bytes[UCNV_EXT_MAX_BYTES]) { (void)codePoints; /* check validity of the bytes and count the characters in them */ int32_t count=ucm_countChars(baseStates, bytes, m->bLen); if(count<1) { /* illegal byte sequence */ return -1; } /* * Suitable for an ICU conversion base table means: * - a 1:1 mapping (1 Unicode code point : 1 byte sequence) * - precision flag 0..3 * - SBCS: any 1:1 mapping * (the table stores additional bits to distinguish mapping types) * - MBCS: not a |2 SUB mapping for <subchar1> * - MBCS: not a |1 fallback to 0x00 * - MBCS: not a multi-byte mapping with leading 0x00 bytes * * Further restrictions for fromUnicode tables * are enforced in makeconv (MBCSOkForBaseFromUnicode()). * * All of the MBCS fromUnicode specific tests could be removed from here, * but the ones above are for unusual mappings, and removing the tests * from here would change canonucm output which seems gratuitous. * (Markus Scherer 2006-nov-28) * * Exception: All implicit mappings (f<0) that need to be moved * because of fromUnicode restrictions _must_ be moved here because * makeconv uses a hack for moving mappings only for the fromUnicode table * that only works with non-negative values of f. */ if( m->uLen==1 && count==1 && m->f<=3 && (baseStates->maxCharLength==1 || !((m->f==2 && m->bLen==1) || (m->f==1 && bytes[0]==0) || (m->f<=1 && m->bLen>1 && bytes[0]==0))) ) { return 0; /* suitable for a base table */ } else { return 1; /* needs to go into an extension table */ } } U_CAPI UBool U_EXPORT2 ucm_addMappingAuto(UCMFile *ucm, UBool forBase, UCMStates *baseStates, UCMapping *m, UChar32 codePoints[UCNV_EXT_MAX_UCHARS], uint8_t bytes[UCNV_EXT_MAX_BYTES]) { int32_t type; if(m->f==2 && m->uLen>1) { fprintf(stderr, "ucm error: illegal <subchar1> |2 mapping from multiple code points\n"); printMapping(m, codePoints, bytes, stderr); return FALSE; } if(baseStates!=NULL) { /* check validity of the bytes and count the characters in them */ type=ucm_mappingType(baseStates, m, codePoints, bytes); if(type<0) { /* illegal byte sequence */ printMapping(m, codePoints, bytes, stderr); return FALSE; } } else { /* not used - adding a mapping for an extension-only table before its base table is read */ type=1; } /* * Add the mapping to the base table if this is requested and suitable. * Otherwise, add it to the extension table. */ if(forBase && type==0) { ucm_addMapping(ucm->base, m, codePoints, bytes); } else { ucm_addMapping(ucm->ext, m, codePoints, bytes); } return TRUE; } U_CAPI UBool U_EXPORT2 ucm_addMappingFromLine(UCMFile *ucm, const char *line, UBool forBase, UCMStates *baseStates) { UCMapping m={ 0, {0}, 0, 0, 0, 0 }; UChar32 codePoints[UCNV_EXT_MAX_UCHARS]; uint8_t bytes[UCNV_EXT_MAX_BYTES]; const char *s; /* ignore empty and comment lines */ if(line[0]=='#' || *(s=u_skipWhitespace(line))==0 || *s=='\n' || *s=='\r') { return TRUE; } return ucm_parseMappingLine(&m, codePoints, bytes, line) && ucm_addMappingAuto(ucm, forBase, baseStates, &m, codePoints, bytes); } U_CAPI void U_EXPORT2 ucm_readTable(UCMFile *ucm, FileStream* convFile, UBool forBase, UCMStates *baseStates, UErrorCode *pErrorCode) { char line[500]; char *end; UBool isOK; if(U_FAILURE(*pErrorCode)) { return; } isOK=TRUE; for(;;) { /* read the next line */ if(!T_FileStream_readLine(convFile, line, sizeof(line))) { fprintf(stderr, "incomplete charmap section\n"); isOK=FALSE; break; } /* remove CR LF */ end=uprv_strchr(line, 0); while(line<end && (*(end-1)=='\r' || *(end-1)=='\n')) { --end; } *end=0; /* ignore empty and comment lines */ if(line[0]==0 || line[0]=='#') { continue; } /* stop at the end of the mapping table */ if(0==uprv_strcmp(line, "END CHARMAP")) { break; } isOK&=ucm_addMappingFromLine(ucm, line, forBase, baseStates); } if(!isOK) { *pErrorCode=U_INVALID_TABLE_FORMAT; } } #endif