0023947: Eliminate trivial compiler warnings in MSVC++ with warning level 4
[occt.git] / src / Message / Message_MsgFile.cxx
CommitLineData
b311480e 1// Created on: 2001-04-26
2// Created by: OCC Team
3// Copyright (c) 2001-2012 OPEN CASCADE SAS
4//
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.
9//
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.
12//
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.
19
7fd59977 20
21#include <Message_MsgFile.hxx>
22
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>
28#include <stdlib.h>
29#include <stdio.h>
30
31DEFINE_BASECOLLECTION(Message_CollectionOfExtendedString, TCollection_ExtendedString)
32DEFINE_DATAMAP(Message_DataMapOfExtendedString,
33 Message_CollectionOfExtendedString,
34 TCollection_AsciiString,
35 TCollection_ExtendedString)
36
37static Message_DataMapOfExtendedString& msgsDataMap ()
38{
39 static Message_DataMapOfExtendedString aDataMap;
40 return aDataMap;
41}
42
43typedef enum
44{
45 MsgFile_WaitingKeyword,
46 MsgFile_WaitingMessage,
47 MsgFile_WaitingMoreMessage,
48 MsgFile_Indefinite
49} LoadingState;
50
51//=======================================================================
52//function : Message_MsgFile
53//purpose : Load file from given directories
54// theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
55//=======================================================================
56
57Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
58 const Standard_CString theFileName)
59{
60 if ( ! theDirName || ! theFileName ) return Standard_False;
61
62 Standard_Boolean ret = Standard_True;
63 TCollection_AsciiString aDirList (theDirName);
64 // Try to load from all consecutive directories in list
65 for (int i = 1;; i++)
66 {
67 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
68 if (aFileName.IsEmpty()) break;
69#ifdef WNT
70 aFileName += '\\';
71#else
72 aFileName += '/';
73#endif
74 aFileName += theFileName;
75 if ( ! LoadFile (aFileName.ToCString()) )
76 ret = Standard_False;
77 }
78 return ret;
79}
80
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//=======================================================================
87
88template <class _Char> static inline Standard_Boolean
89getString (_Char *& thePtr,
90 TCollection_ExtendedString& theString,
91 Standard_Integer& theLeftSpaces)
92{
93 _Char * anEndPtr = thePtr;
94 _Char * aPtr;
95 Standard_Integer aLeftSpaces;
96
97 do
98 {
99 // Skip whitespaces in the beginning of the string
100 aPtr = anEndPtr;
101 aLeftSpaces = 0;
302f96fb 102 for (;;)
7fd59977 103 {
104 _Char aChar = * aPtr;
105 if (aChar == ' ') aLeftSpaces++;
106 else if (aChar == '\t') aLeftSpaces += 8;
107 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
108 else break;
109 aPtr++;
110 }
111
112 // Find the end of the string
113 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
114 if (anEndPtr[0] == '\n')
115 {
116 if (anEndPtr[-1] == '\r') anEndPtr--;
117 break;
118 }
119
120 } while (aPtr[0] == '!');
121
122 // form the result
123 if (aPtr == anEndPtr) return Standard_False;
124 thePtr = anEndPtr;
125 if (*thePtr)
126 *thePtr++ = '\0';
127 theString = TCollection_ExtendedString (aPtr);
128 theLeftSpaces = aLeftSpaces;
129 return Standard_True;
130}
131
132//=======================================================================
133//function : loadFile
134//purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
135//Called : from LoadFile()
136//=======================================================================
137
138template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
139{
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;
145
146 // Take strings one-by-one; comments already screened
147 while (::getString (sCurrentString, aString, aLeftSpaces))
148 {
149 Standard_Boolean isKeyword = (aString.Value(1) == '.');
150 switch (aState)
151 {
152 case MsgFile_WaitingMoreMessage:
153 if (isKeyword)
154 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
155 // Pass from here to 'case MsgFile_WaitingKeyword'
156 else
157 {
158 // Add another line to the message already in the buffer 'aMessage'
159 aMessage += '\n';
160 aLeftSpaces -= aFirstLeftSpaces;
161 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
162 aMessage += aString;
163 break;
164 }
165 case MsgFile_WaitingMessage:
166 if (isKeyword == Standard_False)
167 {
168 aMessage = aString;
169 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
170 aState = MsgFile_WaitingMoreMessage;
171 break;
172 }
173 // Pass from here to 'case MsgFile_WaitingKeyword'
174 case MsgFile_WaitingKeyword:
175 if (isKeyword)
176 {
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;
182 }
183 break;
184 default:
185 break;
186 }
187 }
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;
192}
193
194//=======================================================================
195//function : GetFileSize
196//purpose :
197//=======================================================================
198
199static Standard_Integer GetFileSize (FILE *theFile)
200{
201 if ( !theFile ) return -1;
202
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;
208
209 return (Standard_Integer) nRealFileSize;
210}
211
212//=======================================================================
213//function : LoadFile
214//purpose : Load the list of messages from a file
215//=======================================================================
216
217Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
218{
219 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
220
221 // Open the file
222 FILE *anMsgFile = fopen (theFileName, "rb");
223 if (!anMsgFile) return Standard_False;
224
225 // Read the file into memory
226 class Buffer
227 {
228 // self-destructing buffer
229 char *myBuf;
230 public:
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]; }
235 };
236 Standard_Integer aFileSize = GetFileSize (anMsgFile);
237 if (aFileSize <= 0)
238 {
239 fclose (anMsgFile);
240 return Standard_False;
241 }
242 Buffer anMsgBuffer (aFileSize + 2);
243 Standard_Integer nbRead =
244 (Standard_Integer) fread (anMsgBuffer, 1, aFileSize, anMsgFile);
245 fclose (anMsgFile);
246 if (nbRead != aFileSize)
247 return Standard_False;
248 anMsgBuffer[aFileSize] = 0;
249 anMsgBuffer[aFileSize+1] = 0;
250
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 )
255 {
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 )
261#else
262 if ( isBigEndian )
263#endif
264 {
265 // Reverse the bytes throughout the buffer
266 for (Standard_ExtCharacter * aPtr = aUnicodeBuffer;
267 aPtr < (Standard_ExtCharacter *) &anMsgBuffer[aFileSize]; aPtr++)
268 {
269 unsigned short aWord = *aPtr;
270 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
271 }
272 }
273 return ::loadFile (aUnicodeBuffer);
274 }
275 else
276 return ::loadFile ((char*) anMsgBuffer);
277}
278
279//=======================================================================
280//function : LoadFromEnv
281//purpose :
282//=======================================================================
283void Message_MsgFile::LoadFromEnv
284 (const Standard_CString envname,
285 const Standard_CString filename,
286 const Standard_CString ext)
287{
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();
294 }
295 if (!extname || extname[0] == '\0') extname = "us";
296
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());
304 }
305 }
306 if (extname[0] != '.') filestr.AssignCat ('.');
307 filestr.AssignCat (extname);
308
309 Message_MsgFile::LoadFile (filestr.ToCString());
310}
311
312//=======================================================================
313//function : AddMsg
314//purpose : Add one message to the global table. Fails if the same keyword
315// already exists in the table
316//=======================================================================
317
318Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
319 const TCollection_ExtendedString& theMessage)
320{
321 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
322// if (aDataMap.IsBound (theKeyword))
323// return Standard_False;
324 aDataMap.Bind (theKeyword, theMessage);
325 return Standard_True;
326}
327
328//=======================================================================
329//function : getMsg
330//purpose : retrieve the message previously defined for the given keyword
331//=======================================================================
332
333const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
334{
335 TCollection_AsciiString aKey((char*)theKeyword);
336 return Msg (aKey);
337}
338
339//=======================================================================
340//function : getMsg
341//purpose : retrieve the message previously defined for the given keyword
342//=======================================================================
343
344const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
345{
346 // find message in the map
347 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
348 if (aDataMap.IsBound (theKeyword))
349 return aDataMap.Find (theKeyword);
350
351 // if not found, generate error message
fa523cdd
RL
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");
7fd59977 358 static const TCollection_AsciiString aPrefixCode ("Message_Msg_BadKeyword");
359 static TCollection_ExtendedString aFailureMessage;
fa523cdd
RL
360 if (aFailureMessage.Length() == 0) {
361 if (aDataMap.IsBound (aPrefixCode))
362 aFailureMessage = aDataMap.Find (aPrefixCode);
363 else
364 aFailureMessage = aDefPrefix;
365 }
7fd59977 366 return aFailureMessage;
367}