#include "CStreamConverter.h" #include "CUTextFileStream.h" #include "CycloneConstants.h" //#include #include "CyclonePreferences.h" #include "COutputBuffer.h" #include "Alert.h" #include "CConverter.h" #include "CThrownResult.h" #include "AFileRef.h" extern CyclonePrefs *gPrefs; //extern UAnimateColorCursor *gCursorAnimator; CStreamConverter::CStreamConverter(TextEncoding inputEncoding, TextEncoding outputEncoding, UInt16 inLineBreak, FSRef &inputRef, FSRef &outputRef, CBuffer *inputBuffer, COutputBuffer *outputBuffer) : mEncodingConverter(NULL), mInputEncoding(inputEncoding), mOutputEncoding(outputEncoding), mLineBreak(inLineBreak), mInputBuffer(inputBuffer), mOutputBuffer(outputBuffer), mOutputRef(outputRef) { mInputTextStream = new CUTextFileStream(inputRef, fsRdPerm); mOutputTextStream = new CUTextFileStream(outputRef, fsRdWrPerm); mEncodingConverter = NULL; CThrownOSErr err = ::TECCreateConverter( &mEncodingConverter, inputEncoding, outputEncoding); } CStreamConverter::~CStreamConverter() { delete mInputTextStream; delete mOutputTextStream; if(mEncodingConverter != NULL) ::TECDisposeConverter(mEncodingConverter);//if this returns error we do not care - we cannot do anything anyway } void CStreamConverter::Convert() { if(mEncodingConverter == NULL) return; OSStatus status = noErr; //UAnimateColorCursor animator( 129, 8, 10, 0 );//first id, total no, interval, init delay // gCursorAnimator->Resume(); ByteCount totalOutputLen = 0; if( OpenFiles() ) { char *src = NULL; SInt32 srcLen = 0; do { //reset source specifications: try { src = mInputBuffer->GetBufferPtr();//all allocations are triggered here if nothing was allocated before } catch(...) { status = memFullErr; break; } srcLen = mInputBuffer->GetSize(); StringPtr dst = (StringPtr)( mOutputBuffer->GetBufferPtr() ); ByteCount maxOutput = mOutputBuffer->ConvGetSize(); status = mInputTextStream->ReadData(srcLen, src); if( (status != noErr) && (status != eofErr) ) break; if(srcLen == 0)//break if no more data available { totalOutputLen += Flush(); status = noErr;//clear the "eofErr" since it is not an error break; } do { ByteCount tSourceRead, tOutputLen; status = ::TECConvertText( mEncodingConverter, (ConstStringPtr)src, (ByteCount)srcLen, &tSourceRead, dst, maxOutput, &tOutputLen ); if(status == kTECBufferBelowMinimumSizeErr)//something wrong, our buffer _IS_ bigger break; if(status == kTECPartialCharErr)//there is a problem with streamed conversion that input buffer may break a multi-byte char { try { srcLen--;//try again with shorter input SInt64 currPosition = mInputTextStream->Position(); currPosition--; mInputTextStream->SetPosition(currPosition);//and tell stream to re-read the byte status = noErr;//clear the error - we will try again } catch(...) { status = fileBoundsErr;//can be anything - just to force break } } else if((status == noErr) || (status == kTECUsedFallbacksStatus) || (status == kTECNeedFlushStatus) || (status == kTECOutputBufferFullStatus) )//status codes which are not errors { SInt32 destSize = 0; ByteCount newLen = tOutputLen; if(mLineBreak == kLineBreakAutoChange) { CConverter::AutoCorrectBreaks(dst, newLen, mOutputBuffer->GetSize(), mOutputEncoding); } else { CConverter::ChangeBreaks(dst, newLen, mOutputBuffer->GetSize(), mOutputEncoding, mLineBreak); } //this may throw, so catch it to safely close everything try { destSize = (SInt32)newLen; mOutputTextStream->Write( destSize, mOutputBuffer->GetBufferPtr() ); } catch(...) { status = fileBoundsErr;//can be anything - just to force break } //ok - now we have the output buffer available again if( status == kTECOutputBufferFullStatus )//not everything from source buffer was converted - re-run with the remaining chars { src = (char*)( (UInt32)src + tSourceRead); srcLen -= tSourceRead; } totalOutputLen += destSize; } } while( status == kTECOutputBufferFullStatus);//all other errors are cleared/handled if( (status != noErr) && (status != kTECUsedFallbacksStatus) ) break; // gCursorAnimator->Tick(); } while(true); } if( mOutputTextStream->IsTwoByteUnicode() ) totalOutputLen += sizeof(UniChar); SInt64 totalSize = (SInt64)totalOutputLen; mOutputTextStream->SetSize(totalSize); //close files now: delete mInputTextStream; mInputTextStream = NULL; delete mOutputTextStream; mOutputTextStream = NULL; // gCursorAnimator->Suspend(); if( (status != noErr) && (status != kTECUsedFallbacksStatus) ) { if( ((gPrefs->flags & kKeepPartialFiles) == 0) || (totalOutputLen == 0) )//if "keep" bit is clear - we want to delete it { ::FSDeleteObject(&mOutputRef);//delete it - ignore error } else { //keep the unfinished conversion file - just change type to 'unfinished' FInfo fndrInfo; try { AFileRef outputFile(mOutputRef); outputFile.GetFinderInfo(fndrInfo); fndrInfo.fdType = kPartialFileType; outputFile.SetFinderInfo(fndrInfo); } catch(...) {//this is not critial by any means } } CThrownOSStatus err = status;//throw it up } } ByteCount CStreamConverter::Flush() { if(mEncodingConverter == nil) return 0L; OSStatus status = noErr; StringPtr dest = (StringPtr)( mOutputBuffer->GetBufferPtr() ); ByteCount maxOutput = mOutputBuffer->ConvGetSize(); ByteCount totalOutputLen = 0; do { ByteCount tOutputLen; status = ::TECFlushText( mEncodingConverter, (TextPtr) dest, maxOutput, &tOutputLen); if(status == kTECBufferBelowMinimumSizeErr)//something wrong break; if( tOutputLen > 0 ) { SInt32 destSize = 0; ByteCount newLen = tOutputLen; if(mLineBreak == kLineBreakAutoChange) { CConverter::AutoCorrectBreaks(dest, newLen, mOutputBuffer->GetSize(), mOutputEncoding); } else { CConverter::ChangeBreaks(dest, newLen, mOutputBuffer->GetSize(), mOutputEncoding, mLineBreak); } //this may throw, so catch it to safely close everything try { destSize = (SInt32)newLen; mOutputTextStream->Write( destSize, mOutputBuffer->GetBufferPtr() ); } catch(...) { status = fileBoundsErr;//can be anything - just to force break } totalOutputLen += destSize; } } while( status == kTECOutputBufferFullStatus ); if( (status == noErr) || (status == kTECUsedFallbacksStatus) ) return totalOutputLen; return 0L; } Boolean CStreamConverter::OpenFiles() { Boolean isUnicode = kTextIsNOTUnicode; TextEncodingBase theBase = ::GetTextEncodingBase(mInputEncoding); if(theBase >= kStandardUnicode && theBase < kStandardISO) { TextEncodingFormat uFormat = ::GetTextEncodingFormat(mInputEncoding); if(uFormat == kUnicode16BitFormat) isUnicode = kTextIsUnicode; } try { mInputTextStream->OpenDataForkForReading(isUnicode); } catch(...) { StopAlertResIDName(12, openErr ); return false; } isUnicode = kTextIsNOTUnicode; theBase = ::GetTextEncodingBase(mOutputEncoding); if(theBase >= kStandardUnicode && theBase < kStandardISO) { TextEncodingFormat uFormat = ::GetTextEncodingFormat(mOutputEncoding); if(uFormat == kUnicode16BitFormat) isUnicode = kTextIsUnicode; } /* file must already be created becuse we use FSRef OSType theFileType = isUnicode ? kUniTextFileType: kTextFileType; OSType theCreator = kAppSignature; if( gPrefs->flags & kUseCustomSignature ) theCreator = gPrefs->theSignature; try { mOutputTextStream->CreateNewDataFile( theCreator, theFileType ); } catch(const LException& inException) { FSSpec theSpec; mOutputTextStream->GetSpecifier(theSpec); StopAlertResIDStr(13, theSpec.name, inException.GetErrorCode() );//Could not create the destination file. return false; } */ try { mOutputTextStream->OpenDataForkForWriting(isUnicode); } catch(...) { //FSSpec theSpec; //mOutputTextStream->GetSpecifier(theSpec); // StopAlertResIDStr(13, theSpec.name, inException.GetErrorCode() );//Could not create the destination file. StopAlertResID(13, openErr); return false; } return true; }