1 // Created on: 2001-04-26
2 // Created by: OCC Team
3 // Copyright (c) 2001-2012 OPEN CASCADE SAS
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
21 #include <Message_MsgFile.hxx>
23 #include <TCollection_AsciiString.hxx>
24 #include <TCollection_ExtendedString.hxx>
25 #include <NCollection_DefineBaseCollection.hxx>
26 #include <NCollection_DefineDataMap.hxx>
27 #include <OSD_Environment.hxx>
31 DEFINE_BASECOLLECTION(Message_CollectionOfExtendedString, TCollection_ExtendedString)
32 DEFINE_DATAMAP(Message_DataMapOfExtendedString,
33 Message_CollectionOfExtendedString,
34 TCollection_AsciiString,
35 TCollection_ExtendedString)
37 static Message_DataMapOfExtendedString& msgsDataMap ()
39 static Message_DataMapOfExtendedString aDataMap;
45 MsgFile_WaitingKeyword,
46 MsgFile_WaitingMessage,
47 MsgFile_WaitingMoreMessage,
51 //=======================================================================
52 //function : Message_MsgFile
53 //purpose : Load file from given directories
54 // theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
55 //=======================================================================
57 Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
58 const Standard_CString theFileName)
60 if ( ! theDirName || ! theFileName ) return Standard_False;
62 Standard_Boolean ret = Standard_True;
63 TCollection_AsciiString aDirList (theDirName);
64 // Try to load from all consecutive directories in list
67 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
68 if (aFileName.IsEmpty()) break;
74 aFileName += theFileName;
75 if ( ! LoadFile (aFileName.ToCString()) )
81 //=======================================================================
82 //function : getString
83 //purpose : Takes a TCollection_ExtendedString from Ascii or Unicode
84 // Strings are left-trimmed; those beginning with '!' are omitted
85 //Called : from loadFile()
86 //=======================================================================
88 template <class _Char> static inline Standard_Boolean
89 getString (_Char *& thePtr,
90 TCollection_ExtendedString& theString,
91 Standard_Integer& theLeftSpaces)
93 _Char * anEndPtr = thePtr;
95 Standard_Integer aLeftSpaces;
99 // Skip whitespaces in the beginning of the string
104 _Char aChar = * aPtr;
105 if (aChar == ' ') aLeftSpaces++;
106 else if (aChar == '\t') aLeftSpaces += 8;
107 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
112 // Find the end of the string
113 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
114 if (anEndPtr[0] == '\n')
116 if (anEndPtr[-1] == '\r') anEndPtr--;
120 } while (aPtr[0] == '!');
123 if (aPtr == anEndPtr) return Standard_False;
127 theString = TCollection_ExtendedString (aPtr);
128 theLeftSpaces = aLeftSpaces;
129 return Standard_True;
132 //=======================================================================
133 //function : loadFile
134 //purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
135 //Called : from LoadFile()
136 //=======================================================================
138 template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
140 TCollection_AsciiString aKeyword;
141 TCollection_ExtendedString aMessage, aString;
142 LoadingState aState = MsgFile_WaitingKeyword;
143 _Char * sCurrentString = theBuffer;
144 Standard_Integer aLeftSpaces=0, aFirstLeftSpaces = 0;
146 // Take strings one-by-one; comments already screened
147 while (::getString (sCurrentString, aString, aLeftSpaces))
149 Standard_Boolean isKeyword = (aString.Value(1) == '.');
152 case MsgFile_WaitingMoreMessage:
154 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
155 // Pass from here to 'case MsgFile_WaitingKeyword'
158 // Add another line to the message already in the buffer 'aMessage'
160 aLeftSpaces -= aFirstLeftSpaces;
161 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
165 case MsgFile_WaitingMessage:
166 if (isKeyword == Standard_False)
169 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
170 aState = MsgFile_WaitingMoreMessage;
173 // Pass from here to 'case MsgFile_WaitingKeyword'
174 case MsgFile_WaitingKeyword:
177 // remove the first dot character and all subsequent spaces + right-trim
178 aKeyword = TCollection_AsciiString (aString.Split(1));
179 aKeyword.LeftAdjust();
180 aKeyword.RightAdjust();
181 aState = MsgFile_WaitingMessage;
188 // Process the last string still remaining in the buffer
189 if (aState == MsgFile_WaitingMoreMessage)
190 Message_MsgFile::AddMsg (aKeyword, aMessage);
191 return Standard_True;
194 //=======================================================================
195 //function : GetFileSize
197 //=======================================================================
199 static Standard_Integer GetFileSize (FILE *theFile)
201 if ( !theFile ) return -1;
203 // get real file size
204 long nRealFileSize = 0;
205 if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
206 nRealFileSize = ftell(theFile);
207 if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
209 return (Standard_Integer) nRealFileSize;
212 //=======================================================================
213 //function : LoadFile
214 //purpose : Load the list of messages from a file
215 //=======================================================================
217 Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
219 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
222 FILE *anMsgFile = fopen (theFileName, "rb");
223 if (!anMsgFile) return Standard_False;
225 // Read the file into memory
228 // self-destructing buffer
231 Buffer (Standard_Integer theSize) : myBuf(new char [theSize]) {}
232 ~Buffer () { delete [] myBuf; }
233 operator char* () const { return myBuf; }
234 char& operator [] (Standard_Integer theInd) { return myBuf[theInd]; }
236 Standard_Integer aFileSize = GetFileSize (anMsgFile);
240 return Standard_False;
242 Buffer anMsgBuffer (aFileSize + 2);
243 Standard_Integer nbRead =
244 (Standard_Integer) fread (anMsgBuffer, 1, aFileSize, anMsgFile);
246 if (nbRead != aFileSize)
247 return Standard_False;
248 anMsgBuffer[aFileSize] = 0;
249 anMsgBuffer[aFileSize+1] = 0;
251 // Read the messages in the file and append them to the global DataMap
252 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
253 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
254 if ( isLittleEndian || isBigEndian )
256 Standard_ExtCharacter * aUnicodeBuffer =
257 (Standard_ExtCharacter *) &anMsgBuffer[2];
258 // Convert Unicode representation to order adopted on current platform
259 #if defined(__sparc) && defined(__sun)
260 if ( isLittleEndian )
265 // Reverse the bytes throughout the buffer
266 for (Standard_ExtCharacter * aPtr = aUnicodeBuffer;
267 aPtr < (Standard_ExtCharacter *) &anMsgBuffer[aFileSize]; aPtr++)
269 unsigned short aWord = *aPtr;
270 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
273 return ::loadFile (aUnicodeBuffer);
276 return ::loadFile ((char*) anMsgBuffer);
279 //=======================================================================
280 //function : LoadFromEnv
282 //=======================================================================
283 void Message_MsgFile::LoadFromEnv
284 (const Standard_CString envname,
285 const Standard_CString filename,
286 const Standard_CString ext)
288 Standard_CString extname = ext;
289 TCollection_AsciiString extstr;
290 if (!extname || extname[0] == '\0') {
291 OSD_Environment extenv("CSF_LANGUAGE");
292 extstr = extenv.Value();
293 extname = extstr.ToCString();
295 if (!extname || extname[0] == '\0') extname = "us";
297 TCollection_AsciiString filestr(filename);
298 if (envname && envname[0] != '\0') {
299 OSD_Environment envenv(envname);
300 TCollection_AsciiString envstr = envenv.Value();
301 if (envstr.Length() > 0) {
302 if (envstr.Value(envstr.Length()) != '/') filestr.Insert (1,'/');
303 filestr.Insert (1,envstr.ToCString());
306 if (extname[0] != '.') filestr.AssignCat ('.');
307 filestr.AssignCat (extname);
309 Message_MsgFile::LoadFile (filestr.ToCString());
312 //=======================================================================
314 //purpose : Add one message to the global table. Fails if the same keyword
315 // already exists in the table
316 //=======================================================================
318 Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
319 const TCollection_ExtendedString& theMessage)
321 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
322 // if (aDataMap.IsBound (theKeyword))
323 // return Standard_False;
324 aDataMap.Bind (theKeyword, theMessage);
325 return Standard_True;
328 //=======================================================================
330 //purpose : retrieve the message previously defined for the given keyword
331 //=======================================================================
333 const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
335 TCollection_AsciiString aKey((char*)theKeyword);
339 //=======================================================================
341 //purpose : retrieve the message previously defined for the given keyword
342 //=======================================================================
344 const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
346 // find message in the map
347 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
348 if (aDataMap.IsBound (theKeyword))
349 return aDataMap.Find (theKeyword);
351 // if not found, generate error message
352 // to minimize risk of data races when running concurrently, set the static variables
353 // only if they are empty; this gives a possibility to enforce calling this method
354 // upfront to initialize these variables and only read access them afterwards. However
355 // theKeyword is no longer appended. aDefPrefix remained unchanged to not break some
356 // logs which might expect the previous value
357 static const TCollection_ExtendedString aDefPrefix ("Unknown message invoked with the keyword");
358 static const TCollection_AsciiString aPrefixCode ("Message_Msg_BadKeyword");
359 static TCollection_ExtendedString aFailureMessage;
360 if (aFailureMessage.Length() == 0) {
361 if (aDataMap.IsBound (aPrefixCode))
362 aFailureMessage = aDataMap.Find (aPrefixCode);
364 aFailureMessage = aDefPrefix;
366 return aFailureMessage;