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_DataMap.hxx>
19 #include <OSD_Environment.hxx>
20 #include <TCollection_AsciiString.hxx>
21 #include <TCollection_ExtendedString.hxx>
22 #include <Standard_Mutex.hxx>
27 typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
29 static Message_DataMapOfExtendedString& msgsDataMap ()
31 static Message_DataMapOfExtendedString aDataMap;
35 // mutex used to prevent concurrent access to message registry
36 static Standard_Mutex theMutex;
40 MsgFile_WaitingKeyword,
41 MsgFile_WaitingMessage,
42 MsgFile_WaitingMoreMessage,
46 //=======================================================================
47 //function : Message_MsgFile
48 //purpose : Load file from given directories
49 // theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
50 //=======================================================================
52 Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
53 const Standard_CString theFileName)
55 if ( ! theDirName || ! theFileName ) return Standard_False;
57 Standard_Boolean ret = Standard_True;
58 TCollection_AsciiString aDirList (theDirName);
59 // Try to load from all consecutive directories in list
62 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
63 if (aFileName.IsEmpty()) break;
69 aFileName += theFileName;
70 if ( ! LoadFile (aFileName.ToCString()) )
76 //=======================================================================
77 //function : getString
78 //purpose : Takes a TCollection_ExtendedString from Ascii or Unicode
79 // Strings are left-trimmed; those beginning with '!' are omitted
80 //Called : from loadFile()
81 //=======================================================================
83 template <class _Char> static inline Standard_Boolean
84 getString (_Char *& thePtr,
85 TCollection_ExtendedString& theString,
86 Standard_Integer& theLeftSpaces)
88 _Char * anEndPtr = thePtr;
90 Standard_Integer aLeftSpaces;
94 // Skip whitespaces in the beginning of the string
100 if (aChar == ' ') aLeftSpaces++;
101 else if (aChar == '\t') aLeftSpaces += 8;
102 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
107 // Find the end of the string
108 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
109 if (anEndPtr[0] == '\n')
111 if (anEndPtr[-1] == '\r') anEndPtr--;
115 } while (aPtr[0] == '!');
118 if (aPtr == anEndPtr) return Standard_False;
122 theString = TCollection_ExtendedString (aPtr);
123 theLeftSpaces = aLeftSpaces;
124 return Standard_True;
127 //=======================================================================
128 //function : loadFile
129 //purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
130 //Called : from LoadFile()
131 //=======================================================================
133 template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
135 TCollection_AsciiString aKeyword;
136 TCollection_ExtendedString aMessage, aString;
137 LoadingState aState = MsgFile_WaitingKeyword;
138 _Char * sCurrentString = theBuffer;
139 Standard_Integer aLeftSpaces=0, aFirstLeftSpaces = 0;
141 // Take strings one-by-one; comments already screened
142 while (::getString (sCurrentString, aString, aLeftSpaces))
144 Standard_Boolean isKeyword = (aString.Value(1) == '.');
147 case MsgFile_WaitingMoreMessage:
149 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
150 // Pass from here to 'case MsgFile_WaitingKeyword'
153 // Add another line to the message already in the buffer 'aMessage'
155 aLeftSpaces -= aFirstLeftSpaces;
156 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
160 case MsgFile_WaitingMessage:
161 if (isKeyword == Standard_False)
164 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
165 aState = MsgFile_WaitingMoreMessage;
168 // Pass from here to 'case MsgFile_WaitingKeyword'
169 case MsgFile_WaitingKeyword:
172 // remove the first dot character and all subsequent spaces + right-trim
173 aKeyword = TCollection_AsciiString (aString.Split(1));
174 aKeyword.LeftAdjust();
175 aKeyword.RightAdjust();
176 aState = MsgFile_WaitingMessage;
183 // Process the last string still remaining in the buffer
184 if (aState == MsgFile_WaitingMoreMessage)
185 Message_MsgFile::AddMsg (aKeyword, aMessage);
186 return Standard_True;
189 //=======================================================================
190 //function : GetFileSize
192 //=======================================================================
194 static Standard_Integer GetFileSize (FILE *theFile)
196 if ( !theFile ) return -1;
198 // get real file size
199 long nRealFileSize = 0;
200 if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
201 nRealFileSize = ftell(theFile);
202 if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
204 return (Standard_Integer) nRealFileSize;
207 //=======================================================================
208 //function : LoadFile
209 //purpose : Load the list of messages from a file
210 //=======================================================================
212 Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
214 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
218 // file name is treated as UTF-8 string
219 TCollection_ExtendedString aFileNameW(theFileName, Standard_True);
220 FILE *anMsgFile = _wfopen ((const wchar_t*)aFileNameW.ToExtString(), L"rb");
222 FILE *anMsgFile = fopen (theFileName, "rb");
224 if (!anMsgFile) return Standard_False;
226 // Read the file into memory
229 // self-destructing buffer
232 Buffer (Standard_Integer theSize) : myBuf(new char [theSize]) {}
233 ~Buffer () { delete [] myBuf; }
234 operator char* () const { return myBuf; }
235 char& operator [] (Standard_Integer theInd) { return myBuf[theInd]; }
237 Standard_Integer aFileSize = GetFileSize (anMsgFile);
241 return Standard_False;
243 Buffer anMsgBuffer (aFileSize + 2);
244 Standard_Integer nbRead =
245 (Standard_Integer) fread (anMsgBuffer, 1, aFileSize, anMsgFile);
247 if (nbRead != aFileSize)
248 return Standard_False;
249 anMsgBuffer[aFileSize] = 0;
250 anMsgBuffer[aFileSize+1] = 0;
252 // Read the messages in the file and append them to the global DataMap
253 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
254 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
255 if ( isLittleEndian || isBigEndian )
257 Standard_ExtCharacter * aUnicodeBuffer =
258 (Standard_ExtCharacter *) &anMsgBuffer[2];
259 // Convert Unicode representation to order adopted on current platform
260 #if defined(__sparc) && defined(__sun)
261 if ( isLittleEndian )
266 // Reverse the bytes throughout the buffer
267 for (Standard_ExtCharacter * aPtr = aUnicodeBuffer;
268 aPtr < (Standard_ExtCharacter *) &anMsgBuffer[aFileSize]; aPtr++)
270 unsigned short aWord = *aPtr;
271 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
274 return ::loadFile (aUnicodeBuffer);
277 return ::loadFile ((char*) anMsgBuffer);
280 //=======================================================================
281 //function : LoadFromEnv
283 //=======================================================================
284 void Message_MsgFile::LoadFromEnv
285 (const Standard_CString envname,
286 const Standard_CString filename,
287 const Standard_CString ext)
289 Standard_CString extname = ext;
290 TCollection_AsciiString extstr;
291 if (!extname || extname[0] == '\0') {
292 OSD_Environment extenv("CSF_LANGUAGE");
293 extstr = extenv.Value();
294 extname = extstr.ToCString();
296 if (!extname || extname[0] == '\0') extname = "us";
298 TCollection_AsciiString filestr(filename);
299 if (envname && envname[0] != '\0') {
300 OSD_Environment envenv(envname);
301 TCollection_AsciiString envstr = envenv.Value();
302 if (envstr.Length() > 0) {
303 if (envstr.Value(envstr.Length()) != '/') filestr.Insert (1,'/');
304 filestr.Insert (1,envstr.ToCString());
307 if (extname[0] != '.') filestr.AssignCat ('.');
308 filestr.AssignCat (extname);
310 Message_MsgFile::LoadFile (filestr.ToCString());
313 //=======================================================================
315 //purpose : Add one message to the global table. Fails if the same keyword
316 // already exists in the table
317 //=======================================================================
319 Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
320 const TCollection_ExtendedString& theMessage)
322 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
324 Standard_Mutex::Sentry aSentry (theMutex);
325 aDataMap.Bind (theKeyword, theMessage);
326 return Standard_True;
329 //=======================================================================
331 //purpose : retrieve the message previously defined for the given keyword
332 //=======================================================================
334 const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
336 TCollection_AsciiString aKey((char*)theKeyword);
340 //=======================================================================
343 //=======================================================================
345 Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
347 Standard_Mutex::Sentry aSentry (theMutex);
348 return ::msgsDataMap().IsBound (theKeyword);
351 //=======================================================================
353 //purpose : retrieve the message previously defined for the given keyword
354 //=======================================================================
356 const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
358 // find message in the map
359 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
360 Standard_Mutex::Sentry aSentry (theMutex);
362 // if message is not found, generate error message and add it to the map to minimize overhead
363 // on consequent calls with the same key
364 if (! aDataMap.IsBound(theKeyword))
366 // text of the error message can be itself defined in the map
367 static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword");
368 static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword ");
369 TCollection_AsciiString aErrorMessage = (aDataMap.IsBound(aPrefixCode) ? aDataMap(aPrefixCode) : aDefPrefix);
370 aErrorMessage += theKeyword;
371 aDataMap.Bind (theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
374 return aDataMap (theKeyword);