1 // Created on: 2001-04-26
2 // Created by: OCC Team
3 // Copyright (c) 2001-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <Message_MsgFile.hxx>
18 #include <NCollection_Buffer.hxx>
19 #include <NCollection_DataMap.hxx>
20 #include <OSD_Environment.hxx>
21 #include <TCollection_AsciiString.hxx>
22 #include <TCollection_ExtendedString.hxx>
23 #include <Standard_Mutex.hxx>
24 #include <OSD_OpenFile.hxx>
29 typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
31 static Message_DataMapOfExtendedString& msgsDataMap ()
33 static Message_DataMapOfExtendedString aDataMap;
37 // mutex used to prevent concurrent access to message registry
38 static Standard_Mutex theMutex;
42 MsgFile_WaitingKeyword,
43 MsgFile_WaitingMessage,
44 MsgFile_WaitingMoreMessage,
48 //=======================================================================
49 //function : Message_MsgFile
50 //purpose : Load file from given directories
51 // theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
52 //=======================================================================
54 Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
55 const Standard_CString theFileName)
57 if ( ! theDirName || ! theFileName ) return Standard_False;
59 Standard_Boolean ret = Standard_True;
60 TCollection_AsciiString aDirList (theDirName);
61 // Try to load from all consecutive directories in list
64 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
65 if (aFileName.IsEmpty()) break;
71 aFileName += theFileName;
72 if ( ! LoadFile (aFileName.ToCString()) )
78 //=======================================================================
79 //function : getString
80 //purpose : Takes a TCollection_ExtendedString from Ascii or Unicode
81 // Strings are left-trimmed; those beginning with '!' are omitted
82 //Called : from loadFile()
83 //=======================================================================
85 template <class _Char> static inline Standard_Boolean
86 getString (_Char *& thePtr,
87 TCollection_ExtendedString& theString,
88 Standard_Integer& theLeftSpaces)
90 _Char * anEndPtr = thePtr;
92 Standard_Integer aLeftSpaces;
96 // Skip whitespaces in the beginning of the string
101 _Char aChar = * aPtr;
102 if (aChar == ' ') aLeftSpaces++;
103 else if (aChar == '\t') aLeftSpaces += 8;
104 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
109 // Find the end of the string
110 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
111 if (anEndPtr[0] == '\n')
113 if (anEndPtr[-1] == '\r') anEndPtr--;
117 } while (aPtr[0] == '!');
120 if (aPtr == anEndPtr) return Standard_False;
124 theString = TCollection_ExtendedString (aPtr);
125 theLeftSpaces = aLeftSpaces;
126 return Standard_True;
129 //=======================================================================
130 //function : loadFile
131 //purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
132 //Called : from LoadFile()
133 //=======================================================================
135 template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
137 TCollection_AsciiString aKeyword;
138 TCollection_ExtendedString aMessage, aString;
139 LoadingState aState = MsgFile_WaitingKeyword;
140 _Char * sCurrentString = theBuffer;
141 Standard_Integer aLeftSpaces=0, aFirstLeftSpaces = 0;
143 // Take strings one-by-one; comments already screened
144 while (::getString (sCurrentString, aString, aLeftSpaces))
146 Standard_Boolean isKeyword = (aString.Value(1) == '.');
149 case MsgFile_WaitingMoreMessage:
151 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
152 // Pass from here to 'case MsgFile_WaitingKeyword'
155 // Add another line to the message already in the buffer 'aMessage'
157 aLeftSpaces -= aFirstLeftSpaces;
158 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
162 case MsgFile_WaitingMessage:
163 if (isKeyword == Standard_False)
166 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
167 aState = MsgFile_WaitingMoreMessage;
170 // Pass from here to 'case MsgFile_WaitingKeyword'
171 case MsgFile_WaitingKeyword:
174 // remove the first dot character and all subsequent spaces + right-trim
175 aKeyword = TCollection_AsciiString (aString.Split(1));
176 aKeyword.LeftAdjust();
177 aKeyword.RightAdjust();
178 aState = MsgFile_WaitingMessage;
185 // Process the last string still remaining in the buffer
186 if (aState == MsgFile_WaitingMoreMessage)
187 Message_MsgFile::AddMsg (aKeyword, aMessage);
188 return Standard_True;
191 //=======================================================================
192 //function : GetFileSize
194 //=======================================================================
196 static Standard_Integer GetFileSize (FILE *theFile)
198 if ( !theFile ) return -1;
200 // get real file size
201 long nRealFileSize = 0;
202 if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
203 nRealFileSize = ftell(theFile);
204 if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
206 return (Standard_Integer) nRealFileSize;
209 //=======================================================================
210 //function : LoadFile
211 //purpose : Load the list of messages from a file
212 //=======================================================================
214 Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
216 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
219 FILE *anMsgFile = OSD_OpenFile(theFileName,"rb");
221 return Standard_False;
223 const Standard_Integer aFileSize = GetFileSize (anMsgFile);
224 NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
225 if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2))
228 return Standard_False;
231 char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
232 const Standard_Integer nbRead =
233 static_cast<Standard_Integer>( fread(anMsgBuffer, 1, aFileSize, anMsgFile) );
236 if (nbRead != aFileSize)
237 return Standard_False;
239 anMsgBuffer[aFileSize] = 0;
240 anMsgBuffer[aFileSize + 1] = 0;
242 // Read the messages in the file and append them to the global DataMap
243 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
244 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
245 if ( isLittleEndian || isBigEndian )
247 Standard_ExtCharacter* aUnicodeBuffer =
248 reinterpret_cast<Standard_ExtCharacter*>(&anMsgBuffer[2]);
249 // Convert Unicode representation to order adopted on current platform
250 #if defined(__sparc) && defined(__sun)
251 if ( isLittleEndian )
256 // Reverse the bytes throughout the buffer
257 const Standard_ExtCharacter* const anEnd =
258 reinterpret_cast<const Standard_ExtCharacter* const>(&anMsgBuffer[aFileSize]);
260 for (Standard_ExtCharacter* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++)
262 unsigned short aWord = *aPtr;
263 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
266 return ::loadFile (aUnicodeBuffer);
269 return ::loadFile (anMsgBuffer);
272 //=======================================================================
273 //function : LoadFromEnv
275 //=======================================================================
276 void Message_MsgFile::LoadFromEnv
277 (const Standard_CString envname,
278 const Standard_CString filename,
279 const Standard_CString ext)
281 Standard_CString extname = ext;
282 TCollection_AsciiString extstr;
283 if (!extname || extname[0] == '\0') {
284 OSD_Environment extenv("CSF_LANGUAGE");
285 extstr = extenv.Value();
286 extname = extstr.ToCString();
288 if (!extname || extname[0] == '\0') extname = "us";
290 TCollection_AsciiString filestr(filename);
291 if (envname && envname[0] != '\0') {
292 OSD_Environment envenv(envname);
293 TCollection_AsciiString envstr = envenv.Value();
294 if (envstr.Length() > 0) {
295 if (envstr.Value(envstr.Length()) != '/') filestr.Insert (1,'/');
296 filestr.Insert (1,envstr.ToCString());
299 if (extname[0] != '.') filestr.AssignCat ('.');
300 filestr.AssignCat (extname);
302 Message_MsgFile::LoadFile (filestr.ToCString());
305 //=======================================================================
307 //purpose : Add one message to the global table. Fails if the same keyword
308 // already exists in the table
309 //=======================================================================
311 Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
312 const TCollection_ExtendedString& theMessage)
314 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
316 Standard_Mutex::Sentry aSentry (theMutex);
317 aDataMap.Bind (theKeyword, theMessage);
318 return Standard_True;
321 //=======================================================================
323 //purpose : retrieve the message previously defined for the given keyword
324 //=======================================================================
326 const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
328 TCollection_AsciiString aKey((char*)theKeyword);
332 //=======================================================================
335 //=======================================================================
337 Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
339 Standard_Mutex::Sentry aSentry (theMutex);
340 return ::msgsDataMap().IsBound (theKeyword);
343 //=======================================================================
345 //purpose : retrieve the message previously defined for the given keyword
346 //=======================================================================
348 const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
350 // find message in the map
351 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
352 Standard_Mutex::Sentry aSentry (theMutex);
354 // if message is not found, generate error message and add it to the map to minimize overhead
355 // on consequent calls with the same key
356 if (! aDataMap.IsBound(theKeyword))
358 // text of the error message can be itself defined in the map
359 static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword");
360 static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword ");
361 TCollection_AsciiString aErrorMessage = (aDataMap.IsBound(aPrefixCode) ? aDataMap(aPrefixCode) : aDefPrefix);
362 aErrorMessage += theKeyword;
363 aDataMap.Bind (theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
366 return aDataMap (theKeyword);