0023948: Wrong intersection between a surface of revolution and a plane.
[occt.git] / src / Message / Message_MsgFile.cxx
CommitLineData
b311480e 1// Created on: 2001-04-26
2// Created by: OCC Team
973c2be1 3// Copyright (c) 2001-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 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
973c2be1 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.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
7fd59977 15
16#include <Message_MsgFile.hxx>
17
ddf2fe8e 18#include <NCollection_DataMap.hxx>
19#include <OSD_Environment.hxx>
7fd59977 20#include <TCollection_AsciiString.hxx>
21#include <TCollection_ExtendedString.hxx>
c67cd62e 22#include <Standard_Mutex.hxx>
ddf2fe8e 23
7fd59977 24#include <stdlib.h>
25#include <stdio.h>
26
ddf2fe8e 27typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
7fd59977 28
29static Message_DataMapOfExtendedString& msgsDataMap ()
30{
31 static Message_DataMapOfExtendedString aDataMap;
32 return aDataMap;
33}
34
c67cd62e 35// mutex used to prevent concurrent access to message registry
36static Standard_Mutex theMutex;
37
7fd59977 38typedef enum
39{
40 MsgFile_WaitingKeyword,
41 MsgFile_WaitingMessage,
42 MsgFile_WaitingMoreMessage,
43 MsgFile_Indefinite
44} LoadingState;
45
46//=======================================================================
47//function : Message_MsgFile
48//purpose : Load file from given directories
49// theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
50//=======================================================================
51
52Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
53 const Standard_CString theFileName)
54{
55 if ( ! theDirName || ! theFileName ) return Standard_False;
56
57 Standard_Boolean ret = Standard_True;
58 TCollection_AsciiString aDirList (theDirName);
59 // Try to load from all consecutive directories in list
60 for (int i = 1;; i++)
61 {
62 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
63 if (aFileName.IsEmpty()) break;
64#ifdef WNT
65 aFileName += '\\';
66#else
67 aFileName += '/';
68#endif
69 aFileName += theFileName;
70 if ( ! LoadFile (aFileName.ToCString()) )
71 ret = Standard_False;
72 }
73 return ret;
74}
75
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//=======================================================================
82
83template <class _Char> static inline Standard_Boolean
84getString (_Char *& thePtr,
85 TCollection_ExtendedString& theString,
86 Standard_Integer& theLeftSpaces)
87{
88 _Char * anEndPtr = thePtr;
89 _Char * aPtr;
90 Standard_Integer aLeftSpaces;
91
92 do
93 {
94 // Skip whitespaces in the beginning of the string
95 aPtr = anEndPtr;
96 aLeftSpaces = 0;
302f96fb 97 for (;;)
7fd59977 98 {
99 _Char aChar = * aPtr;
100 if (aChar == ' ') aLeftSpaces++;
101 else if (aChar == '\t') aLeftSpaces += 8;
102 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
103 else break;
104 aPtr++;
105 }
106
107 // Find the end of the string
108 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
109 if (anEndPtr[0] == '\n')
110 {
111 if (anEndPtr[-1] == '\r') anEndPtr--;
112 break;
113 }
114
115 } while (aPtr[0] == '!');
116
117 // form the result
118 if (aPtr == anEndPtr) return Standard_False;
119 thePtr = anEndPtr;
120 if (*thePtr)
121 *thePtr++ = '\0';
122 theString = TCollection_ExtendedString (aPtr);
123 theLeftSpaces = aLeftSpaces;
124 return Standard_True;
125}
126
127//=======================================================================
128//function : loadFile
129//purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
130//Called : from LoadFile()
131//=======================================================================
132
133template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
134{
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;
140
141 // Take strings one-by-one; comments already screened
142 while (::getString (sCurrentString, aString, aLeftSpaces))
143 {
144 Standard_Boolean isKeyword = (aString.Value(1) == '.');
145 switch (aState)
146 {
147 case MsgFile_WaitingMoreMessage:
148 if (isKeyword)
149 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
150 // Pass from here to 'case MsgFile_WaitingKeyword'
151 else
152 {
153 // Add another line to the message already in the buffer 'aMessage'
154 aMessage += '\n';
155 aLeftSpaces -= aFirstLeftSpaces;
156 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
157 aMessage += aString;
158 break;
159 }
160 case MsgFile_WaitingMessage:
161 if (isKeyword == Standard_False)
162 {
163 aMessage = aString;
164 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
165 aState = MsgFile_WaitingMoreMessage;
166 break;
167 }
168 // Pass from here to 'case MsgFile_WaitingKeyword'
169 case MsgFile_WaitingKeyword:
170 if (isKeyword)
171 {
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;
177 }
178 break;
179 default:
180 break;
181 }
182 }
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;
187}
188
189//=======================================================================
190//function : GetFileSize
191//purpose :
192//=======================================================================
193
194static Standard_Integer GetFileSize (FILE *theFile)
195{
196 if ( !theFile ) return -1;
197
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;
203
204 return (Standard_Integer) nRealFileSize;
205}
206
207//=======================================================================
208//function : LoadFile
209//purpose : Load the list of messages from a file
210//=======================================================================
211
212Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
213{
214 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
215
216 // Open the file
217 FILE *anMsgFile = fopen (theFileName, "rb");
218 if (!anMsgFile) return Standard_False;
219
220 // Read the file into memory
221 class Buffer
222 {
223 // self-destructing buffer
224 char *myBuf;
225 public:
226 Buffer (Standard_Integer theSize) : myBuf(new char [theSize]) {}
227 ~Buffer () { delete [] myBuf; }
228 operator char* () const { return myBuf; }
229 char& operator [] (Standard_Integer theInd) { return myBuf[theInd]; }
230 };
231 Standard_Integer aFileSize = GetFileSize (anMsgFile);
232 if (aFileSize <= 0)
233 {
234 fclose (anMsgFile);
235 return Standard_False;
236 }
237 Buffer anMsgBuffer (aFileSize + 2);
238 Standard_Integer nbRead =
239 (Standard_Integer) fread (anMsgBuffer, 1, aFileSize, anMsgFile);
240 fclose (anMsgFile);
241 if (nbRead != aFileSize)
242 return Standard_False;
243 anMsgBuffer[aFileSize] = 0;
244 anMsgBuffer[aFileSize+1] = 0;
245
246 // Read the messages in the file and append them to the global DataMap
247 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
248 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
249 if ( isLittleEndian || isBigEndian )
250 {
251 Standard_ExtCharacter * aUnicodeBuffer =
252 (Standard_ExtCharacter *) &anMsgBuffer[2];
253 // Convert Unicode representation to order adopted on current platform
254#if defined(__sparc) && defined(__sun)
255 if ( isLittleEndian )
256#else
257 if ( isBigEndian )
258#endif
259 {
260 // Reverse the bytes throughout the buffer
261 for (Standard_ExtCharacter * aPtr = aUnicodeBuffer;
262 aPtr < (Standard_ExtCharacter *) &anMsgBuffer[aFileSize]; aPtr++)
263 {
264 unsigned short aWord = *aPtr;
265 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
266 }
267 }
268 return ::loadFile (aUnicodeBuffer);
269 }
270 else
271 return ::loadFile ((char*) anMsgBuffer);
272}
273
274//=======================================================================
275//function : LoadFromEnv
276//purpose :
277//=======================================================================
278void Message_MsgFile::LoadFromEnv
279 (const Standard_CString envname,
280 const Standard_CString filename,
281 const Standard_CString ext)
282{
283 Standard_CString extname = ext;
284 TCollection_AsciiString extstr;
285 if (!extname || extname[0] == '\0') {
286 OSD_Environment extenv("CSF_LANGUAGE");
287 extstr = extenv.Value();
288 extname = extstr.ToCString();
289 }
290 if (!extname || extname[0] == '\0') extname = "us";
291
292 TCollection_AsciiString filestr(filename);
293 if (envname && envname[0] != '\0') {
294 OSD_Environment envenv(envname);
295 TCollection_AsciiString envstr = envenv.Value();
296 if (envstr.Length() > 0) {
297 if (envstr.Value(envstr.Length()) != '/') filestr.Insert (1,'/');
298 filestr.Insert (1,envstr.ToCString());
299 }
300 }
301 if (extname[0] != '.') filestr.AssignCat ('.');
302 filestr.AssignCat (extname);
303
304 Message_MsgFile::LoadFile (filestr.ToCString());
305}
306
307//=======================================================================
308//function : AddMsg
309//purpose : Add one message to the global table. Fails if the same keyword
310// already exists in the table
311//=======================================================================
312
313Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
314 const TCollection_ExtendedString& theMessage)
315{
316 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 317
318 Standard_Mutex::Sentry aSentry (theMutex);
7fd59977 319 aDataMap.Bind (theKeyword, theMessage);
320 return Standard_True;
321}
322
323//=======================================================================
324//function : getMsg
325//purpose : retrieve the message previously defined for the given keyword
326//=======================================================================
327
328const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
329{
330 TCollection_AsciiString aKey((char*)theKeyword);
331 return Msg (aKey);
332}
333
334//=======================================================================
c67cd62e 335//function : HasMsg
336//purpose :
337//=======================================================================
338
339Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
340{
341 Standard_Mutex::Sentry aSentry (theMutex);
342 return ::msgsDataMap().IsBound (theKeyword);
343}
344
345//=======================================================================
346//function : Msg
7fd59977 347//purpose : retrieve the message previously defined for the given keyword
348//=======================================================================
349
350const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
351{
352 // find message in the map
353 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 354 Standard_Mutex::Sentry aSentry (theMutex);
355
356 // if message is not found, generate error message and add it to the map to minimize overhead
357 // on consequent calls with the same key
358 if (! aDataMap.IsBound(theKeyword))
359 {
360 // text of the error message can be itself defined in the map
361 static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword");
362 static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword ");
363 TCollection_AsciiString aErrorMessage = (aDataMap.IsBound(aPrefixCode) ? aDataMap(aPrefixCode) : aDefPrefix);
364 aErrorMessage += theKeyword;
365 aDataMap.Bind (theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
fa523cdd 366 }
c67cd62e 367
368 return aDataMap (theKeyword);
7fd59977 369}