// Created on: 2001-04-26 // Created by: OCC Team // Copyright (c) 2001-2014 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #include #include #include #include #include #include #include #include #include typedef NCollection_DataMap Message_DataMapOfExtendedString; static Message_DataMapOfExtendedString& msgsDataMap() { static Message_DataMapOfExtendedString aDataMap; return aDataMap; } // mutex used to prevent concurrent access to message registry static Standard_Mutex& Message_MsgFile_Mutex() { static Standard_Mutex theMutex; return theMutex; } typedef enum { MsgFile_WaitingKeyword, MsgFile_WaitingMessage, MsgFile_WaitingMoreMessage, MsgFile_Indefinite } LoadingState; //======================================================================= // function : Message_MsgFile // purpose : Load file from given directories // theDirName may be represented as list: "/dirA/dirB /dirA/dirC" //======================================================================= Standard_Boolean Message_MsgFile::Load(const Standard_CString theDirName, const Standard_CString theFileName) { if (!theDirName || !theFileName) return Standard_False; Standard_Boolean ret = Standard_True; TCollection_AsciiString aDirList(theDirName); // Try to load from all consecutive directories in list for (int i = 1;; i++) { TCollection_AsciiString aFileName = aDirList.Token(" \t\n", i); if (aFileName.IsEmpty()) break; #ifdef _WIN32 aFileName += '\\'; #else aFileName += '/'; #endif aFileName += theFileName; if (!LoadFile(aFileName.ToCString())) ret = Standard_False; } return ret; } //======================================================================= // function : getString // purpose : Takes a TCollection_ExtendedString from Ascii or Unicode // Strings are left-trimmed; those beginning with '!' are omitted // Called : from loadFile() //======================================================================= template struct TCollection_String; template <> struct TCollection_String { typedef TCollection_AsciiString type; }; template <> struct TCollection_String { typedef TCollection_ExtendedString type; }; template static inline Standard_Boolean getString(CharType*& thePtr, TCollection_ExtendedString& theString, Standard_Integer& theLeftSpaces) { CharType* anEndPtr = thePtr; CharType* aPtr; Standard_Integer aLeftSpaces; do { // Skip whitespaces in the beginning of the string aPtr = anEndPtr; aLeftSpaces = 0; for (;;) { CharType aChar = *aPtr; if (aChar == ' ') aLeftSpaces++; else if (aChar == '\t') aLeftSpaces += 8; else if (aChar == '\r' || *aPtr == '\n') aLeftSpaces = 0; else break; aPtr++; } // Find the end of the string for (anEndPtr = aPtr; *anEndPtr; anEndPtr++) if (anEndPtr[0] == '\n') { if (anEndPtr[-1] == '\r') anEndPtr--; break; } } while (aPtr[0] == '!'); // form the result if (aPtr == anEndPtr) return Standard_False; thePtr = anEndPtr; if (*thePtr) *thePtr++ = '\0'; theString = typename TCollection_String::type(aPtr); theLeftSpaces = aLeftSpaces; return Standard_True; } //======================================================================= // function : loadFile // purpose : Static function, fills the DataMap of Messages from Ascii or Unicode // Called : from LoadFile() //======================================================================= template static inline Standard_Boolean loadFile(_Char* theBuffer) { TCollection_AsciiString aKeyword; TCollection_ExtendedString aMessage, aString; LoadingState aState = MsgFile_WaitingKeyword; _Char* sCurrentString = theBuffer; Standard_Integer aLeftSpaces = 0, aFirstLeftSpaces = 0; // Take strings one-by-one; comments already screened while (::getString(sCurrentString, aString, aLeftSpaces)) { Standard_Boolean isKeyword = (aString.Value(1) == '.'); switch (aState) { case MsgFile_WaitingMoreMessage: if (isKeyword) Message_MsgFile::AddMsg(aKeyword, aMessage); // terminate the previous one // Pass from here to 'case MsgFile_WaitingKeyword' else { // Add another line to the message already in the buffer 'aMessage' aMessage += '\n'; aLeftSpaces -= aFirstLeftSpaces; if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString(aLeftSpaces, ' '); aMessage += aString; break; } Standard_FALLTHROUGH case MsgFile_WaitingMessage: if (isKeyword == Standard_False) { aMessage = aString; aFirstLeftSpaces = aLeftSpaces; // remember the starting position aState = MsgFile_WaitingMoreMessage; break; } // Pass from here to 'case MsgFile_WaitingKeyword' Standard_FALLTHROUGH case MsgFile_WaitingKeyword: if (isKeyword) { // remove the first dot character and all subsequent spaces + right-trim aKeyword = TCollection_AsciiString(aString.Split(1)); aKeyword.LeftAdjust(); aKeyword.RightAdjust(); aState = MsgFile_WaitingMessage; } break; default: break; } } // Process the last string still remaining in the buffer if (aState == MsgFile_WaitingMoreMessage) Message_MsgFile::AddMsg(aKeyword, aMessage); return Standard_True; } //================================================================================================= static Standard_Integer GetFileSize(FILE* theFile) { if (!theFile) return -1; // get real file size long nRealFileSize = 0; if (fseek(theFile, 0, SEEK_END) != 0) return -1; nRealFileSize = ftell(theFile); if (fseek(theFile, 0, SEEK_SET) != 0) return -1; return (Standard_Integer)nRealFileSize; } //======================================================================= // function : LoadFile // purpose : Load the list of messages from a file //======================================================================= Standard_Boolean Message_MsgFile::LoadFile(const Standard_CString theFileName) { if (theFileName == NULL || *theFileName == '\0') return Standard_False; // Open the file FILE* anMsgFile = OSD_OpenFile(theFileName, "rb"); if (!anMsgFile) return Standard_False; const Standard_Integer aFileSize = GetFileSize(anMsgFile); NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator()); if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2)) { fclose(anMsgFile); return Standard_False; } char* anMsgBuffer = reinterpret_cast(aBuffer.ChangeData()); const Standard_Integer nbRead = static_cast(fread(anMsgBuffer, 1, aFileSize, anMsgFile)); fclose(anMsgFile); if (nbRead != aFileSize) return Standard_False; anMsgBuffer[aFileSize] = 0; anMsgBuffer[aFileSize + 1] = 0; // Read the messages in the file and append them to the global DataMap Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe'); Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff'); if (isLittleEndian || isBigEndian) { Standard_ExtCharacter* aUnicodeBuffer = reinterpret_cast(&anMsgBuffer[2]); // Convert Unicode representation to order adopted on current platform #if defined(__sparc) && defined(__sun) if (isLittleEndian) #else if (isBigEndian) #endif { // Reverse the bytes throughout the buffer const Standard_ExtCharacter* const anEnd = reinterpret_cast(&anMsgBuffer[aFileSize]); for (Standard_ExtCharacter* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++) { unsigned short aWord = *aPtr; *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8; } } return ::loadFile(aUnicodeBuffer); } else return ::loadFile(anMsgBuffer); } //================================================================================================= Standard_Boolean Message_MsgFile::LoadFromEnv(const Standard_CString theEnvName, const Standard_CString theFileName, const Standard_CString theLangExt) { TCollection_AsciiString aLangExt(theLangExt != NULL ? theLangExt : ""); if (aLangExt.IsEmpty()) { OSD_Environment aLangEnv("CSF_LANGUAGE"); aLangExt = aLangEnv.Value(); if (aLangExt.IsEmpty()) { aLangExt = "us"; } } TCollection_AsciiString aFilePath(theFileName); if (theEnvName != NULL && theEnvName[0] != '\0') { OSD_Environment aNameEnv(theEnvName); TCollection_AsciiString aNameEnvStr = aNameEnv.Value(); if (!aNameEnvStr.IsEmpty()) { if (aNameEnvStr.Value(aNameEnvStr.Length()) != '/') { aFilePath.Insert(1, '/'); } aFilePath.Insert(1, aNameEnvStr); } } if (aLangExt.Value(1) != '.') { aFilePath.AssignCat('.'); } aFilePath.AssignCat(aLangExt); return Message_MsgFile::LoadFile(aFilePath.ToCString()); } //================================================================================================= Standard_Boolean Message_MsgFile::LoadFromString(const Standard_CString theContent, const Standard_Integer theLength) { Standard_Integer aStringSize = theLength >= 0 ? theLength : (Standard_Integer)strlen(theContent); NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator()); if (aStringSize <= 0 || !aBuffer.Allocate(aStringSize + 2)) { return Standard_False; } memcpy(aBuffer.ChangeData(), theContent, aStringSize); aBuffer.ChangeData()[aStringSize + 0] = '\0'; aBuffer.ChangeData()[aStringSize + 1] = '\0'; char* anMsgBuffer = reinterpret_cast(aBuffer.ChangeData()); return ::loadFile(anMsgBuffer); } //======================================================================= // function : AddMsg // purpose : Add one message to the global table. Fails if the same keyword // already exists in the table //======================================================================= Standard_Boolean Message_MsgFile::AddMsg(const TCollection_AsciiString& theKeyword, const TCollection_ExtendedString& theMessage) { Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap(); Standard_Mutex::Sentry aSentry(Message_MsgFile_Mutex()); aDataMap.Bind(theKeyword, theMessage); return Standard_True; } //======================================================================= // function : getMsg // purpose : retrieve the message previously defined for the given keyword //======================================================================= const TCollection_ExtendedString& Message_MsgFile::Msg(const Standard_CString theKeyword) { TCollection_AsciiString aKey(theKeyword); return Msg(aKey); } //================================================================================================= Standard_Boolean Message_MsgFile::HasMsg(const TCollection_AsciiString& theKeyword) { Standard_Mutex::Sentry aSentry(Message_MsgFile_Mutex()); return ::msgsDataMap().IsBound(theKeyword); } //======================================================================= // function : Msg // purpose : retrieve the message previously defined for the given keyword //======================================================================= const TCollection_ExtendedString& Message_MsgFile::Msg(const TCollection_AsciiString& theKeyword) { // find message in the map Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap(); Standard_Mutex::Sentry aSentry(Message_MsgFile_Mutex()); // if message is not found, generate error message and add it to the map to minimize overhead // on consequent calls with the same key const TCollection_ExtendedString* aValPtr = aDataMap.Seek(theKeyword); if (aValPtr == NULL) { // text of the error message can be itself defined in the map static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword"); static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword "); const TCollection_ExtendedString* aPrefValPtr = aDataMap.Seek(aPrefixCode); TCollection_AsciiString aErrorMessage = (aPrefValPtr != NULL ? *aPrefValPtr : aDefPrefix); aErrorMessage += theKeyword; aDataMap.Bind(theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock aValPtr = aDataMap.Seek(theKeyword); } return *aValPtr; }