123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- //
- //////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2015 Autodesk, Inc. All rights reserved.
- //
- // Use of this software is subject to the terms of the Autodesk license
- // agreement provided at the time of installation or download, or which
- // otherwise accompanies this software in either electronic or hard copy form.
- //
- //////////////////////////////////////////////////////////////////////////////
- //
- // Name: AcCrtFileWrappers.h
- //
- // Description: Wrapper classes for clib FILE functions
- //
- //////////////////////////////////////////////////////////////////////////////
- //
- #pragma once
- #include "stdio.h" // for fopen, FILE, fprintf, etc
- #include <malloc.h> // for _alloca
- #include "AdCharFmt.h"
- #include "casuppress.h"
- #pragma warning(push)
- #pragma warning(disable:4996)
- #ifdef ASSERT
- #define AcFILE_Assert ASSERT
- #elif defined assert
- #define AcFILE_Assert assert
- #elif defined _ASSERTE
- #define AcFILE_Assert _ASSERTE
- #else
- #define AcFILE_Assert(T)
- #endif
- class AcFILE {
- public:
- // fwrite() expands LF's for us, so we set the formatter's mode
- // to not do the LF expansion.
- AcFILE()
- : mpFILE(NULL),
- mChFmtr(AdCharFormatter::kAnsi,
- false, // useCIF
- false) // expandLF
- {}
- AcFILE(FILE *pFILE)
- : mpFILE(pFILE),
- mChFmtr(AdCharFormatter::kAnsi,
- false, // useCIF
- false) // expandLF
- {}
- ~AcFILE() {
- AcFILE_Assert(this->mpFILE == NULL);
- }
- FILE * fopen(const TCHAR *pName, const TCHAR *pMode);
- FILE * fsopen(const TCHAR *pName, const TCHAR *pMode, int shflag);
- void attach(FILE *pFILE);
- FILE * detach();
- int fclose();
- int fputs(const TCHAR *pStr);
- TCHAR * fgets(TCHAR *pBuf, int nChars);
- int fprintf(const TCHAR *pFmtStr, ...);
- int vfprintf(const TCHAR *pFmtStr, va_list va);
- int fscanf(const TCHAR *pFmtStr, ...);
- int fputc(TCHAR c);
- int ungetc(TCHAR c);
- int fgetc();
- bool isOpen() const { return this->mpFILE != NULL; }
- FILE * pFILE() const { return this->mpFILE; }
- unsigned getCharFormat() const { return this->mChFmtr.getFormat(); }
- unsigned setCharFormat(unsigned nFmt) {
- return this->mChFmtr.setFormat(nFmt); }
- bool getUseCIF() const { return this->mChFmtr.getUseCIF(); }
- bool setUseCIF(bool bUseCIF) {
- return this->mChFmtr.setUseCIF(bUseCIF); }
- bool getExpandLF() const { return this->mChFmtr.getExpandLF(); }
- bool setExpandLF(bool bExpandLF) {
- return this->mChFmtr.setExpandLF(bExpandLF); }
- bool readBOM();
- bool writeBOM();
- private:
- #ifdef UNICODE
- bool parseAnsiOrCIF(wchar_t &wch);
- bool parseUtf8(wchar_t &wch);
- bool parseUtf16(wchar_t &wch);
- int fputsWorker(const wchar_t* pSrc, int nOptions);
- #endif
- FILE *mpFILE;
- AdCharFormatter mChFmtr;
- };
- // instance methods
- inline FILE * AcFILE::fopen(const TCHAR *pName, const TCHAR *pMode)
- {
- AcFILE_Assert(this->mpFILE == NULL);
- this->mpFILE = ::_tfopen(pName, pMode);
- return this->mpFILE;
- }
- inline FILE * AcFILE::fsopen(const TCHAR *pName, const TCHAR *pMode, int shflag)
- {
- AcFILE_Assert(this->mpFILE == NULL);
- this->mpFILE = ::_tfsopen(pName, pMode, shflag);
- return this->mpFILE;
- }
- inline void AcFILE::attach(FILE *pFILE)
- {
- AcFILE_Assert(this->mpFILE == NULL);
- this->mpFILE = pFILE;
- }
- inline FILE * AcFILE::detach()
- {
- FILE *pRet = this->mpFILE;
- this->mpFILE = NULL;
- return pRet;
- }
- inline int AcFILE::fclose()
- {
- int nRet = 0;
- if (this->mpFILE != NULL) {
- nRet = ::fclose(this->mpFILE);
- this->mpFILE = NULL;
- }
- return nRet;
- }
- inline int AcFILE::fputs(const TCHAR *pStr)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- #ifndef UNICODE
- return ::fputs(pStr, this->mpFILE);
- #else
- return fputsWorker(pStr, 0);
- #endif
- }
- inline TCHAR * AcFILE::fgets(TCHAR *pBuf, int nChars)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- // Need room for null, so it doesn't make sense to pass
- // a count of 1 or less.
- AcFILE_Assert(nChars > 1);
- #ifndef UNICODE
- return ::fgets(pBuf, nChars, this->mpFILE);
- #else
- // We don't know how many widechars the ansi chars
- // will turn into, so we have to read them one
- // at a time. Read until we hit newline, eof
- // or nChars.
- TCHAR *pSavePtr = pBuf;
- for (;;) {
- if (nChars <= 1)
- break;
- const int ch = this->fgetc();
- if (ch == EOF) // -1
- break;
- AcFILE_Assert((ch & ~0xffff) == 0); // only low 16 bits set
- AcFILE_Assert((wchar_t)ch == ch);
- *pSavePtr = (wchar_t)ch;
- pSavePtr++;
- if (ch == '\n')
- break; // stop reading after newline
- nChars--;
- }
- *pSavePtr = 0;
- if (pSavePtr == pBuf)
- return NULL;
- return pBuf;
- #endif
- }
- inline int AcFILE::fprintf(const TCHAR *pFmtStr, ...)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- va_list va;
- va_start(va, pFmtStr);
- return this->vfprintf(pFmtStr, va);
- }
- inline int AcFILE::vfprintf(const TCHAR *pFmtStr, va_list va)
- {
- #ifndef UNICODE
- return ::vfprintf(this->mpFILE, pFmtStr, va);
- #else
- // Note: we have to format the entire string into a widechar
- // buffer first, then convert that to ansi.
- int nChar = ::_vscwprintf(pFmtStr, va);
- wchar_t* wBuf = new wchar_t[nChar + 1];
- ::wmemset(wBuf, 0, nChar + 1);
- ::vswprintf_s(wBuf,nChar + 1, pFmtStr, va);
- AcFILE_Assert(wBuf[nChar] == 0);
- int iRet = fputsWorker(wBuf, 1);
- delete [] wBuf;
- return iRet;
- #endif
- }
- #ifndef _ADESK_MAC_
- inline int AcFILE::fscanf(const TCHAR *pFmtStr, ...)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- // TODO: Remove this, it's meant to shut the compiler up.
- pFmtStr = NULL;
- // UNICODE: TODO: SPAGO: There is no existing client of fscanf
- // method. Implement this method when time allows/need arises.
- }
- #endif
- inline int AcFILE::fputc(TCHAR c)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- #ifndef UNICODE
- return ::fputc(c, this->mpFILE);
- #else
- char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
- const unsigned nBytes = this->mChFmtr.wcharToBytes(c, chBuf,
- sizeof(chBuf));
- AcFILE_Assert(nBytes >= 1);
- AcFILE_Assert(nBytes <= 8); // worst case is utf-32 cr-lf?
- const unsigned nNumWrote = (unsigned)fwrite(chBuf, 1,
- nBytes, this->mpFILE);
- if (nNumWrote == nBytes)
- return c; // return the char written
- else
- return -1; // error
- #endif
- }
- inline int AcFILE::ungetc(TCHAR c)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- #ifndef UNICODE
- return ::ungetc(c, this->mpFILE);
- #else
- char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
- const unsigned nBytes = this->mChFmtr.wcharToBytes(c, chBuf,
- sizeof(chBuf));
- AcFILE_Assert(nBytes >= 1);
- AcFILE_Assert(nBytes <= 8);
- // Note: for now we can only unget a single char. So if
- // the wide char got converted to CIF or double-byte,
- // we fail. Todo: fix this if it's really needed.
- if (nBytes == 1 && ::ungetc(chBuf[0], this->mpFILE) == chBuf[0])
- return c; // on success, return the char passed in
- else
- return -1;
- #endif
- }
- inline int AcFILE::fgetc()
- {
- AcFILE_Assert(this->mpFILE != NULL);
- #ifndef UNICODE
- return ::fgetc(this->mpFILE);
- #else
- wchar_t wchLocal = L'\0';
- switch(this->getCharFormat())
- {
- case AdCharFormatter::kAnsi:
- if (parseAnsiOrCIF(wchLocal))
- return wchLocal;
- return EOF;
- break;
- case AdCharFormatter::kUtf8:
- if (parseUtf8(wchLocal))
- return wchLocal;
- return EOF;
- break;
- case AdCharFormatter::kUtf16LE:
- case AdCharFormatter::kUtf16BE:
- if (parseUtf16(wchLocal))
- return wchLocal;
- return EOF;
- break;
- case AdCharFormatter::kUtf32LE:
- case AdCharFormatter::kUtf32BE:
- AcFILE_Assert(false); // Implement me!
- return EOF;
- break;
- }
- AcFILE_Assert(false); // Should never reach here!
- return EOF;
- #endif
- }
- inline bool AcFILE::readBOM()
- {
- AcFILE_Assert(this->mpFILE != NULL);
- const long lFilePos = ::ftell(this->mpFILE);
- AcFILE_Assert(lFilePos == 0L);
- if (lFilePos != 0L)
- return false; // Can't do this unless we're at start of file.
- unsigned short nVal;
- if (::fread(&nVal, 1, 2, this->mpFILE) == 2) {
- if (nVal == 0xfeff) {
- this->setCharFormat(AdCharFormatter::kUtf16LE);
- return true;
- }
- if (nVal == 0xfffe) {
- this->setCharFormat(AdCharFormatter::kUtf16BE);
- return true;
- }
- if (nVal == 0xbbef) {
- unsigned char nByte3;
- if (::fread(&nByte3, 1, 1, this->mpFILE) == 1) {
- if (nByte3 == 0xbf) {
- this->setCharFormat(AdCharFormatter::kUtf8);
- return true;
- }
- }
- }
- else if (nVal == 0 || nVal == 0xfeff) {
- unsigned nVal2;
- if (::fread(&nVal2, 1, 2, this->mpFILE) == 2) {
- if (nVal == 0 && nVal2 == 0xfffe) {
- this->setCharFormat(AdCharFormatter::kUtf32BE);
- return true;
- }
- else if (nVal == 0xfeff && nVal2 == 0) {
- this->setCharFormat(AdCharFormatter::kUtf32LE);
- return true;
- }
- }
- }
- }
- // If got here, then no BOM found, so reset
- // to file beginning. Leave format what it was.
- ::rewind(this->mpFILE);
- return false;
- }
- inline bool AcFILE::writeBOM()
- {
- AcFILE_Assert(this->mpFILE != NULL);
- if (this->mpFILE == NULL)
- return false; // There must be an associated file.
- const long lFilePos = ::ftell(this->mpFILE);
- AcFILE_Assert(lFilePos == 0L);
- if (lFilePos != 0L)
- return false; // Can't do this unless we're at start of file.
- unsigned nBom = 0;
- const int cbBomSize = AdCharFormatter::getBOM(nBom,
- this->getCharFormat());
- if (cbBomSize == 0) // AdcharFormatter::getBOM would
- return false; // have brought up an assertion.
- const int cbWritten = (int) ::fwrite(&nBom, // BOM to be written.
- 1, // Item size.
- cbBomSize, // Item count.
- this->mpFILE); // File pointer.
- AcFILE_Assert(cbWritten == cbBomSize);
- if (cbWritten != cbBomSize)
- {
- ::rewind(this->mpFILE);
- return false;
- }
- return true;
- }
- #ifdef UNICODE
- inline bool AcFILE::parseAnsiOrCIF(wchar_t &wch)
- {
- AcFILE_Assert(this->getCharFormat() == AdCharFormatter::kAnsi);
- wch = L'\0';
- // Read in a single character from file.
- unsigned char cFirstChar = 0;
- int nBytesRead = (int)::fread(&cFirstChar, 1, 1, this->mpFILE);
- if (nBytesRead <= 0)
- return false; // Error or EOF condition.
- if (cFirstChar > 0x7f) // Possible double-byte character.
- {
- char chBuf[2] = {0};
- wchar_t wchLocal = 0;
- chBuf[0] = ((char) cFirstChar);
- const BOOL bIsDBCS = ::IsDBCSLeadByteEx(CP_ACP, cFirstChar);
- if (bIsDBCS != FALSE) // Attempt to read in the second character.
- {
- unsigned char cSecondChar = 0;
- nBytesRead = (int)::fread(&cSecondChar, 1, 1, this->mpFILE);
- if (nBytesRead <= 0)
- return false; // Error or EOF condition.
- chBuf[1] = ((char) cSecondChar);
- }
- // TODO: Handle the cases of surrogate pairs.
- const int nRet = ::MultiByteToWideChar(
- CP_ACP, // Conversion code page.
- MB_ERR_INVALID_CHARS, // Do we want this?
- chBuf, // DBCS character.
- bIsDBCS ? 2 : 1, // DBCS byte count.
- &wchLocal, // Converted wide character.
- 1); // Single wide character.
- AcFILE_Assert(nRet == 1); // If fails, do we need lang. pack installed?
- wch = (nRet == 1) ? wchLocal : L'?';
- return true;
- }
- AcFILE_Assert(cFirstChar <= 0x7f);
- wch = ((wchar_t) cFirstChar);
- if (cFirstChar == '\r' && (this->mChFmtr.getExpandLF())) {
- unsigned char cNewLinePair = 0; // Any pairing '\n'?
- nBytesRead = (int)::fread(&cNewLinePair, 1, 1, this->mpFILE);
- if (nBytesRead <= 0) // '\r' at the end of file, odd...
- return true; // but we are taking it as it is anyway.
- if (cNewLinePair != '\n') {
- ::fseek(this->mpFILE, -1L, SEEK_CUR);
- return true; // Return '\r' untranslated.
- }
- wch = L'\n'; // Translate to new line character.
- return true;
- }
- // If CIF not desired, return any character.
- if (cFirstChar != '\\' || (this->getUseCIF()) == false)
- return true;
- // When we get here, it means that there is possible CIF string
- // ahead of us. we will read in more from the underlying file...
- char chBuffer[8] = {0};
- chBuffer[0] = '\\';
- // Attempt to read 6 more bytes from file. This function might
- // not be reading as many bytes as we want but that's not what
- // we care right now, leave it to AdCharFormatter::isCIFString.
- nBytesRead = (int) ::fread(&chBuffer[1], // Input buffer.
- 1, // Unit data size.
- 6, // Unit count.
- this->mpFILE); // FILE pointer.
- // If it's a CIF sequence, parse into wch.
- if (AdCharFormatter::isCIFString(chBuffer))
- {
- AdCharFormatter::parseCIF(chBuffer, wch);
- return true;
- }
- // It was not CIF, we read too much, undo that.
- if (nBytesRead > 0)
- ::fseek(this->mpFILE, -nBytesRead, SEEK_CUR);
- wch = L'\\';
- return true;
- }
- inline bool AcFILE::parseUtf8(wchar_t &wch)
- {
- AcFILE_Assert(this->getUseCIF() == false);
- // Read in a single character from file.
- unsigned char cCharacter = 0;
- int nBytesRead = (int) ::fread(&cCharacter, 1, 1, this->mpFILE);
- if (nBytesRead <= 0)
- return false; // Error or EOF condition.
- if (cCharacter <= 0x7f)
- {
- wch = cCharacter;
- if (cCharacter == '\r' && mChFmtr.getExpandLF()) {
- unsigned char cNewLine = 0;
- nBytesRead = (int) ::fread(&cNewLine, 1, 1, this->mpFILE);
- if (nBytesRead <= 0) // '\r' at the end of file...
- return true; // ... weird but we'll take it anyway.
- if (cNewLine != '\n') { // Not a new line character!
- ::fseek(this->mpFILE, -1L, SEEK_CUR);
- return true; // Return '\r' untranslated.
- }
- wch = L'\n'; // Translated to new line character.
- }
- return true; // Single byte UTF8 character.
- }
- int nByteCount;
- char chBuffer[4] = {0};
- chBuffer[0] = ((char) cCharacter);
- if ((chBuffer[0] & 0xe0) == 0xc0)
- nByteCount = 2; // 110xxxxx = 2-byte code
- else if((chBuffer[0] & 0xf0) == 0xe0)
- nByteCount = 3; // 1110xxxx = 3-byte code
- else if((chBuffer[0] & 0xf8) == 0xf0)
- nByteCount = 4; // 11110xxx = 4-byte code
- else
- {
- // Adding this assert for now, to help with
- // debugging. Maybe we'll have to relax it if
- // we find we commonly hit malformed data...
- AcFILE_Assert(false);
- return false; // Malformed UTF8 character?
- }
- // Attempt to read one or more bytes from file...
- nBytesRead = (int) ::fread(&chBuffer[1], // Input buffer.
- 1, // Unit data size.
- nByteCount - 1, // Unit count.
- this->mpFILE); // FILE pointer.
- AcFILE_Assert(nBytesRead == nByteCount - 1);
- if (nBytesRead < nByteCount - 1)
- return false; // Malformed UTF8 character.
- const int nCvted = MultiByteToWideChar(CP_UTF8, // Conversion code page.
- 0, // Flags must be 0 for UTF8.
- chBuffer, // Input UTF8 string.
- nByteCount, // Byte count in UTF8 string.
- &wch, // Destination wide character.
- 1); // Expect a single wide char.
- AcFILE_Assert(nCvted == 1);
- return nCvted == 1;
- }
- inline bool AcFILE::parseUtf16(wchar_t &wch)
- {
- wchar_t wchLocal = 0;
- int nBytesRead = (int) ::fread(&wchLocal, 1, 2, this->mpFILE);
- if (nBytesRead <= 0)
- return false; // Error or EOF condition.
- const unsigned nFormat = this->mChFmtr.getFormat();
- if (nFormat == AdCharFormatter::kUtf16BE)
- wchLocal = (wchLocal >> 8) | (wchLocal << 8);
- wch = wchLocal;
- if (wchLocal == L'\r' && mChFmtr.getExpandLF()) {
- wchar_t wchNewLine = L'\n';
- nBytesRead = (int) ::fread(&wchNewLine, 1, 2, this->mpFILE);
- if (nBytesRead <= 0) // '\r' at the end of file...
- return true; // ... weird but we'll take it.
- if (nFormat == AdCharFormatter::kUtf16BE)
- wchNewLine = (wchNewLine >> 8) | (wchNewLine << 8);
- if (wchNewLine != L'\n') { // Not a new line character.
- ::fseek(this->mpFILE, -2L, SEEK_CUR);
- return true; // Return '\r' untranslated.
- }
- wch = L'\n'; // Translated to new line character.
- }
- return true;
- }
- // This helper function writes data to the file depending on the value of
- // nOptions. In case of fputs zero is returned when successful and incase
- // of fprintf the return value is the number of bytes.
- //
- inline int AcFILE::fputsWorker(const wchar_t* pSrc, int nOptions)
- {
- AcFILE_Assert(this->mpFILE != NULL);
- AcFILE_Assert(pSrc != NULL);
- int nNumInput = 0;
- for (;;) {
- const wchar_t wch = *pSrc;
- if (wch == 0)
- break;
- char chBuf[8]; // CIF is 7 bytes, utf-32 cr-lf might be 8
- const unsigned nBytes = this->mChFmtr.wcharToBytes(wch, chBuf,
- sizeof(chBuf));
- AcFILE_Assert(nBytes >= 1);
- AcFILE_Assert(nBytes <= 8);
- const unsigned nNumWrote = (unsigned)fwrite(chBuf, 1,
- nBytes, this->mpFILE);
- if (nNumWrote != nBytes)
- return -1; // error (out of disk space?)
- nNumInput++;
- pSrc++;
- }
- if (nOptions == 0) // called from puts
- return 0; // 0 indicates success, apparently
- else if (nOptions == 1) // called from printf
- return nNumInput;
- else {
- AcFILE_Assert(false);
- }
- return -1;
- }
- #endif // UNICODE defined.
- #pragma warning(pop)
|