michael@0: /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsISO2022CNToUnicode.h" michael@0: #include "nsUCSupport.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); michael@0: michael@0: NS_IMETHODIMP nsISO2022CNToUnicode::GB2312_To_Unicode(unsigned char *aSrc, int32_t aSrcLength, char16_t * aDest, int32_t * aDestLength) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if(!mGB2312_Decoder) { michael@0: // creating a delegate converter (GB2312) michael@0: nsCOMPtr ccm = michael@0: do_GetService(kCharsetConverterManagerCID, &rv); michael@0: if(NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = ccm->GetUnicodeDecoderRaw("GB2312", getter_AddRefs(mGB2312_Decoder)); michael@0: if(NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if(!mGB2312_Decoder) // failed creating a delegate converter michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = mGB2312_Decoder->Convert((const char *)aSrc, &aSrcLength, aDest, aDestLength); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsISO2022CNToUnicode::EUCTW_To_Unicode(unsigned char *aSrc, int32_t aSrcLength, char16_t * aDest, int32_t * aDestLength) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if(!mEUCTW_Decoder) { michael@0: // creating a delegate converter (x-euc-tw) michael@0: nsCOMPtr ccm = michael@0: do_GetService(kCharsetConverterManagerCID, &rv); michael@0: if(NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = ccm->GetUnicodeDecoderRaw("x-euc-tw", getter_AddRefs(mEUCTW_Decoder)); michael@0: if(NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if(!mEUCTW_Decoder) // failed creating a delegate converter michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = mEUCTW_Decoder->Convert((const char *)aSrc, &aSrcLength, aDest, aDestLength); michael@0: return(rv); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsISO2022CNToUnicode::Convert(const char * aSrc, int32_t * aSrcLen, char16_t * aDest, int32_t * aDestLen) michael@0: { michael@0: const unsigned char * srcEnd = (unsigned char *)aSrc + *aSrcLen; michael@0: const unsigned char * src = (unsigned char *) aSrc; michael@0: char16_t* destEnd = aDest + *aDestLen; michael@0: char16_t* dest = aDest; michael@0: nsresult rv; michael@0: int32_t aLen; michael@0: michael@0: while ((src < srcEnd)) michael@0: { michael@0: switch (mState) michael@0: { michael@0: case eState_ASCII: michael@0: if(ESC == *src) { michael@0: mState = eState_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC: // ESC michael@0: if('$' == *src) { michael@0: mState = eState_ESC_24; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 2)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24: // ESC $ michael@0: if(')' == *src) { michael@0: mState = eState_ESC_24_29; michael@0: } else if('*' == *src) { michael@0: mState = eState_ESC_24_2A; michael@0: } else if('+' == *src) { michael@0: mState = eState_ESC_24_2B; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 3)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_29: // ESC $ ) michael@0: if('A' == *src) { michael@0: mState = eState_ESC_24_29_A; michael@0: } else if('G' == *src) { michael@0: mState = eState_ESC_24_29_G; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 4)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) ')'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_29_A: // ESC $ ) A michael@0: if(SO == *src) { michael@0: mState = eState_GB2312_1980; michael@0: mRunLength = 0; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 5)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) ')'; michael@0: *dest++ = (char16_t) 'A'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_GB2312_1980: // ESC $ ) A SO michael@0: if(SI == *src) { // Shift-In (SI) michael@0: mState = eState_ESC_24_29_A_SO_SI; michael@0: if (mRunLength == 0) { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = 0xFFFD; michael@0: } michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC; michael@0: } else { michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: mData = *src; michael@0: mState = eState_GB2312_1980_2ndbyte; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eState_GB2312_1980_2ndbyte: // ESC $ ) A SO michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: unsigned char gb[2]; michael@0: int32_t gbLen = 2; michael@0: michael@0: gb[0] = mData | 0x80; michael@0: gb[1] = *src | 0x80; michael@0: michael@0: aLen = destEnd - dest; michael@0: rv = GB2312_To_Unicode(gb, gbLen, dest, &aLen); michael@0: ++mRunLength; michael@0: if(rv == NS_OK_UDEC_MOREOUTPUT) { michael@0: goto error1; michael@0: } else if(NS_FAILED(rv)) { michael@0: goto error2; michael@0: } michael@0: michael@0: dest += aLen; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 2)) michael@0: goto error1; michael@0: *dest++ = (char16_t) mData; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: mState = eState_GB2312_1980; michael@0: break; michael@0: michael@0: case eState_ESC_24_29_A_SO_SI: // ESC $ ) A SO SI michael@0: if(SO == *src) { michael@0: mState = eState_GB2312_1980; michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_29_A_SO_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_29_G: // ESC $ ) G michael@0: if(SO == *src) { michael@0: mState = eState_CNS11643_1; michael@0: mRunLength = 0; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 5)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) ')'; michael@0: *dest++ = (char16_t) 'G'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_CNS11643_1: // ESC $ ) G SO michael@0: if(SI == *src) { // Shift-In (SI) michael@0: mState = eState_ESC_24_29_G_SO_SI; michael@0: if (mRunLength == 0) { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = 0xFFFD; michael@0: } michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC; michael@0: } else { michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: mData = *src; michael@0: mState = eState_CNS11643_1_2ndbyte; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eState_CNS11643_1_2ndbyte: // ESC $ ) G SO michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: unsigned char cns[4]; michael@0: int32_t cnsLen = 2; michael@0: michael@0: cns[0] = mData | 0x80; michael@0: cns[1] = *src | 0x80; michael@0: michael@0: aLen = destEnd - dest; michael@0: rv = EUCTW_To_Unicode(cns, cnsLen, dest, &aLen); michael@0: ++mRunLength; michael@0: if(rv == NS_OK_UDEC_MOREOUTPUT) { michael@0: goto error1; michael@0: } else if(NS_FAILED(rv)) { michael@0: goto error2; michael@0: } michael@0: michael@0: dest += aLen; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 2)) michael@0: goto error1; michael@0: *dest++ = (char16_t) mData; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: mState = eState_CNS11643_1; michael@0: break; michael@0: michael@0: case eState_ESC_24_29_G_SO_SI: // ESC $ ) G SO SI michael@0: if(SO == *src) { michael@0: mState = eState_CNS11643_1; michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_29_G_SO_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2A: // ESC $ * michael@0: if('H' == *src) { michael@0: mState = eState_ESC_24_2A_H; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 4)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '*'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2A_H: // ESC $ * H michael@0: if(ESC == *src) { michael@0: mState = eState_ESC_24_2A_H_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 5)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '*'; michael@0: *dest++ = (char16_t) 'H'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2A_H_ESC: // ESC $ * H ESC michael@0: if(SS2 == *src) { michael@0: mState = eState_CNS11643_2; michael@0: mRunLength = 0; michael@0: } else if('$' == *src) { michael@0: mState = eState_ESC_24; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 6)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '*'; michael@0: *dest++ = (char16_t) 'H'; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_CNS11643_2: // ESC $ * H ESC SS2 michael@0: if(SI == *src) { // Shift-In (SI) michael@0: mState = eState_ESC_24_2A_H_ESC_SS2_SI; michael@0: if (mRunLength == 0) { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = 0xFFFD; michael@0: } michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC_24_2A_H_ESC; michael@0: } else { michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: mData = *src; michael@0: mState = eState_CNS11643_2_2ndbyte; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eState_CNS11643_2_2ndbyte: // ESC $ * H ESC SS2 michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: unsigned char cns[4]; michael@0: int32_t cnsLen = 4; michael@0: michael@0: cns[0] = (unsigned char) MBYTE; michael@0: cns[1] = (unsigned char) (PMASK + 2); michael@0: cns[2] = mData | 0x80; michael@0: cns[3] = *src | 0x80; michael@0: michael@0: aLen = destEnd - dest; michael@0: rv = EUCTW_To_Unicode(cns, cnsLen, dest, &aLen); michael@0: ++mRunLength; michael@0: if(rv == NS_OK_UDEC_MOREOUTPUT) { michael@0: goto error1; michael@0: } else if(NS_FAILED(rv)) { michael@0: goto error2; michael@0: } michael@0: michael@0: dest += aLen; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 2)) michael@0: goto error1; michael@0: *dest++ = (char16_t) mData; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: mState = eState_CNS11643_2; michael@0: break; michael@0: michael@0: case eState_ESC_24_2A_H_ESC_SS2_SI: // ESC $ * H ESC SS2 SI michael@0: if(ESC == *src) { michael@0: mState = eState_ESC_24_2A_H_ESC_SS2_SI_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_2A_H_ESC_SS2_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2A_H_ESC_SS2_SI_ESC: // ESC $ * H ESC SS2 SI ESC michael@0: if(SS2 == *src) { michael@0: mState = eState_CNS11643_2; michael@0: mRunLength = 0; michael@0: } else if('$' == *src) { michael@0: mState = eState_ESC_24; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_2A_H_ESC_SS2_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2B: // ESC $ + michael@0: if('I' <= *src && *src <= 'M') { michael@0: mState = eState_ESC_24_2B_I; michael@0: mPlaneID = *src - 'I' + 3; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 4)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '+'; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2B_I: // ESC $ + I michael@0: if(ESC == *src) { michael@0: mState = eState_ESC_24_2B_I_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 5)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '+'; michael@0: *dest++ = (char16_t) 'I' + mPlaneID - 3; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2B_I_ESC: // ESC $ + I ESC michael@0: if(SS3 == *src) { michael@0: mState = eState_CNS11643_3; michael@0: mRunLength = 0; michael@0: } else if('$' == *src) { michael@0: mState = eState_ESC_24; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 6)) michael@0: goto error1; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (char16_t) '$'; michael@0: *dest++ = (char16_t) '+'; michael@0: *dest++ = (char16_t) 'I' + mPlaneID - 3; michael@0: *dest++ = (char16_t) ESC; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ASCII; michael@0: } michael@0: break; michael@0: michael@0: case eState_CNS11643_3: // ESC $ + I ESC SS3 michael@0: if(SI == *src) { // Shift-In (SI) michael@0: mState = eState_ESC_24_2B_I_ESC_SS3_SI; michael@0: if (mRunLength == 0) { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = 0xFFFD; michael@0: } michael@0: mRunLength = 0; michael@0: } else if(ESC == *src) { michael@0: mState = eState_ESC_24_2B_I_ESC; michael@0: } else { michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: mData = *src; michael@0: mState = eState_CNS11643_3_2ndbyte; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: } michael@0: michael@0: break; michael@0: michael@0: case eState_CNS11643_3_2ndbyte: // ESC $ + I ESC SS3 michael@0: if(0x20 < *src && *src < 0x7f) { michael@0: unsigned char cns[4]; michael@0: int32_t cnsLen = 4; michael@0: michael@0: cns[0] = (unsigned char) MBYTE; michael@0: cns[1] = (unsigned char) (PMASK + mPlaneID); michael@0: cns[2] = mData | 0x80; michael@0: cns[3] = *src | 0x80; michael@0: michael@0: aLen = destEnd - dest; michael@0: rv = EUCTW_To_Unicode(cns, cnsLen, dest, &aLen); michael@0: ++mRunLength; michael@0: if(rv == NS_OK_UDEC_MOREOUTPUT) { michael@0: goto error1; michael@0: } else if(NS_FAILED(rv)) { michael@0: goto error2; michael@0: } michael@0: michael@0: dest += aLen; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 2)) michael@0: goto error1; michael@0: *dest++ = (char16_t) mData; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: } michael@0: mState = eState_CNS11643_3; michael@0: break; michael@0: michael@0: case eState_ESC_24_2B_I_ESC_SS3_SI: // ESC $ + I ESC SS3 SI michael@0: if(ESC == *src) { michael@0: mState = eState_ESC_24_2B_I_ESC_SS3_SI_ESC; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_2B_I_ESC_SS3_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ESC_24_2B_I_ESC_SS3_SI_ESC: // ESC $ + I ESC SS3 SI ESC michael@0: if(SS3 == *src) { michael@0: mState = eState_CNS11643_3; michael@0: mRunLength = 0; michael@0: } else if('$' == *src) { michael@0: mState = eState_ESC_24; michael@0: } else { michael@0: if (CHECK_OVERRUN(dest, destEnd, 1)) michael@0: goto error1; michael@0: *dest++ = (0x80 & *src) ? 0xFFFD : (char16_t) *src; michael@0: michael@0: mState = eState_ESC_24_2B_I_ESC_SS3_SI; michael@0: } michael@0: break; michael@0: michael@0: case eState_ERROR: michael@0: NS_NOTREACHED("unhandled case"); michael@0: goto error2; michael@0: michael@0: } // switch michael@0: src++; michael@0: } michael@0: michael@0: *aDestLen = dest- aDest; michael@0: return NS_OK; michael@0: michael@0: error1: michael@0: *aDestLen = dest-aDest; michael@0: *aSrcLen = src - (const unsigned char*)aSrc; michael@0: return NS_OK_UDEC_MOREOUTPUT; michael@0: michael@0: error2: michael@0: *aSrcLen = src - (const unsigned char*)aSrc; michael@0: *aDestLen = dest-aDest; michael@0: mState = eState_ASCII; michael@0: return NS_ERROR_UNEXPECTED; michael@0: }