Loading files
// 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 <Message_MsgFile.hxx>
#include <NCollection_Buffer.hxx>
#include <NCollection_DataMap.hxx>
#include <OSD_Environment.hxx>
#include <TCollection_AsciiString.hxx>
#include <OSD_OpenFile.hxx>
#include <cstdlib>
#include <cstdio>
#include <mutex>
typedef NCollection_DataMap<TCollection_AsciiString, TCollection_ExtendedString>
Message_DataMapOfExtendedString;
static Message_DataMapOfExtendedString& msgsDataMap()
{
static Message_DataMapOfExtendedString aDataMap;
return aDataMap;
}
// mutex used to prevent concurrent access to message registry
static std::mutex& Message_MsgFile_Mutex()
{
static std::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"
//=======================================================================
bool Message_MsgFile::Load(const char* const theDirName, const char* const theFileName)
{
if (!theDirName || !theFileName)
{
return false;
}
bool ret = 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 = 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 <typename CharType>
struct TCollection_String;
template <>
struct TCollection_String<char>
{
typedef TCollection_AsciiString type;
};
template <>
struct TCollection_String<char16_t>
{
typedef TCollection_ExtendedString type;
};
template <class CharType>
static inline bool getString(CharType*& thePtr,
TCollection_ExtendedString& theString,
int& theLeftSpaces)
{
CharType* anEndPtr = thePtr;
CharType* aPtr;
int 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 false;
}
thePtr = anEndPtr;
if (*thePtr)
{
*thePtr++ = '\0';
}
theString = typename TCollection_String<CharType>::type(aPtr);
theLeftSpaces = aLeftSpaces;
return true;
}
//=======================================================================
// function : loadFile
// purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
// Called : from LoadFile()
//=======================================================================
template <class _Char>
static inline bool loadFile(_Char* theBuffer)
{
TCollection_AsciiString aKeyword;
TCollection_ExtendedString aMessage, aString;
LoadingState aState = MsgFile_WaitingKeyword;
_Char* sCurrentString = theBuffer;
int aLeftSpaces = 0, aFirstLeftSpaces = 0;
// Take strings one-by-one; comments already screened
while (::getString(sCurrentString, aString, aLeftSpaces))
{
bool 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;
}
[[fallthrough]];
case MsgFile_WaitingMessage:
if (!isKeyword)
{
aMessage = aString;
aFirstLeftSpaces = aLeftSpaces; // remember the starting position
aState = MsgFile_WaitingMoreMessage;
break;
}
// Pass from here to 'case MsgFile_WaitingKeyword'
[[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 true;
}
//=================================================================================================
static int 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 (int)nRealFileSize;
}
//=======================================================================
// function : LoadFile
// purpose : Load the list of messages from a file
//=======================================================================
bool Message_MsgFile::LoadFile(const char* const theFileName)
{
if (theFileName == nullptr || *theFileName == '\0')
{
return false;
}
// Open the file
FILE* anMsgFile = OSD_OpenFile(theFileName, "rb");
if (!anMsgFile)
{
return false;
}
const int aFileSize = GetFileSize(anMsgFile);
NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2))
{
fclose(anMsgFile);
return false;
}
char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
const int nbRead = static_cast<int>(fread(anMsgBuffer, 1, aFileSize, anMsgFile));
fclose(anMsgFile);
if (nbRead != aFileSize)
{
return false;
}
anMsgBuffer[aFileSize] = 0;
anMsgBuffer[aFileSize + 1] = 0;
// Read the messages in the file and append them to the global DataMap
bool isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
bool isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
if (isLittleEndian || isBigEndian)
{
char16_t* aUnicodeBuffer = reinterpret_cast<char16_t*>(&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 char16_t* const anEnd = reinterpret_cast<const char16_t*>(&anMsgBuffer[aFileSize]);
for (char16_t* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++)
{
unsigned short aWord = *aPtr;
*aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
}
}
return ::loadFile(aUnicodeBuffer);
}
else
{
return ::loadFile(anMsgBuffer);
}
}
//=================================================================================================
bool Message_MsgFile::LoadFromEnv(const char* const theEnvName,
const char* const theFileName,
const char* const theLangExt)
{
TCollection_AsciiString aLangExt(theLangExt != nullptr ? theLangExt : "");
if (aLangExt.IsEmpty())
{
OSD_Environment aLangEnv("CSF_LANGUAGE");
aLangExt = aLangEnv.Value();
if (aLangExt.IsEmpty())
{
aLangExt = "us";
}
}
TCollection_AsciiString aFilePath(theFileName);
if (theEnvName != nullptr && 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());
}
//=================================================================================================
bool Message_MsgFile::LoadFromString(const char* const theContent, const int theLength)
{
int aStringSize = theLength >= 0 ? theLength : (int)strlen(theContent);
NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
if (aStringSize <= 0 || !aBuffer.Allocate(aStringSize + 2))
{
return false;
}
memcpy(aBuffer.ChangeData(), theContent, aStringSize);
aBuffer.ChangeData()[aStringSize + 0] = '\0';
aBuffer.ChangeData()[aStringSize + 1] = '\0';
char* anMsgBuffer = reinterpret_cast<char*>(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
//=======================================================================
bool Message_MsgFile::AddMsg(const TCollection_AsciiString& theKeyword,
const TCollection_ExtendedString& theMessage)
{
Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
std::lock_guard<std::mutex> aLock(Message_MsgFile_Mutex());
aDataMap.Bind(theKeyword, theMessage);
return true;
}
//=======================================================================
// function : getMsg
// purpose : retrieve the message previously defined for the given keyword
//=======================================================================
const TCollection_ExtendedString& Message_MsgFile::Msg(const char* const theKeyword)
{
TCollection_AsciiString aKey(theKeyword);
return Msg(aKey);
}
//=================================================================================================
bool Message_MsgFile::HasMsg(const TCollection_AsciiString& theKeyword)
{
std::lock_guard<std::mutex> aLock(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();
std::lock_guard<std::mutex> aLock(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 == nullptr)
{
// 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 != nullptr ? *aPrefValPtr : aDefPrefix);
aErrorMessage += theKeyword;
aDataMap.Bind(theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
aValPtr = aDataMap.Seek(theKeyword);
}
return *aValPtr;
}
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.