0022972: Eliminate macro definitions that has compiler-provided analogs (WNT and...
[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
96f3bacc 18#include <NCollection_Buffer.hxx>
ddf2fe8e 19#include <NCollection_DataMap.hxx>
20#include <OSD_Environment.hxx>
7fd59977 21#include <TCollection_AsciiString.hxx>
22#include <TCollection_ExtendedString.hxx>
c67cd62e 23#include <Standard_Mutex.hxx>
94708556 24#include <OSD_OpenFile.hxx>
ddf2fe8e 25
7fd59977 26#include <stdlib.h>
27#include <stdio.h>
28
ddf2fe8e 29typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
7fd59977 30
31static Message_DataMapOfExtendedString& msgsDataMap ()
32{
33 static Message_DataMapOfExtendedString aDataMap;
34 return aDataMap;
35}
36
c67cd62e 37// mutex used to prevent concurrent access to message registry
38static Standard_Mutex theMutex;
39
7fd59977 40typedef enum
41{
42 MsgFile_WaitingKeyword,
43 MsgFile_WaitingMessage,
44 MsgFile_WaitingMoreMessage,
45 MsgFile_Indefinite
46} LoadingState;
47
48//=======================================================================
49//function : Message_MsgFile
50//purpose : Load file from given directories
51// theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
52//=======================================================================
53
54Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
55 const Standard_CString theFileName)
56{
57 if ( ! theDirName || ! theFileName ) return Standard_False;
58
59 Standard_Boolean ret = Standard_True;
60 TCollection_AsciiString aDirList (theDirName);
61 // Try to load from all consecutive directories in list
62 for (int i = 1;; i++)
63 {
64 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
65 if (aFileName.IsEmpty()) break;
57c28b61 66#ifdef _WIN32
7fd59977 67 aFileName += '\\';
68#else
69 aFileName += '/';
70#endif
71 aFileName += theFileName;
72 if ( ! LoadFile (aFileName.ToCString()) )
73 ret = Standard_False;
74 }
75 return ret;
76}
77
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//=======================================================================
84
85template <class _Char> static inline Standard_Boolean
86getString (_Char *& thePtr,
87 TCollection_ExtendedString& theString,
88 Standard_Integer& theLeftSpaces)
89{
90 _Char * anEndPtr = thePtr;
91 _Char * aPtr;
92 Standard_Integer aLeftSpaces;
93
94 do
95 {
96 // Skip whitespaces in the beginning of the string
97 aPtr = anEndPtr;
98 aLeftSpaces = 0;
302f96fb 99 for (;;)
7fd59977 100 {
101 _Char aChar = * aPtr;
102 if (aChar == ' ') aLeftSpaces++;
103 else if (aChar == '\t') aLeftSpaces += 8;
104 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
105 else break;
106 aPtr++;
107 }
108
109 // Find the end of the string
110 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
111 if (anEndPtr[0] == '\n')
112 {
113 if (anEndPtr[-1] == '\r') anEndPtr--;
114 break;
115 }
116
117 } while (aPtr[0] == '!');
118
119 // form the result
120 if (aPtr == anEndPtr) return Standard_False;
121 thePtr = anEndPtr;
122 if (*thePtr)
123 *thePtr++ = '\0';
124 theString = TCollection_ExtendedString (aPtr);
125 theLeftSpaces = aLeftSpaces;
126 return Standard_True;
127}
128
129//=======================================================================
130//function : loadFile
131//purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
132//Called : from LoadFile()
133//=======================================================================
134
135template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
136{
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;
142
143 // Take strings one-by-one; comments already screened
144 while (::getString (sCurrentString, aString, aLeftSpaces))
145 {
146 Standard_Boolean isKeyword = (aString.Value(1) == '.');
147 switch (aState)
148 {
149 case MsgFile_WaitingMoreMessage:
150 if (isKeyword)
151 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
152 // Pass from here to 'case MsgFile_WaitingKeyword'
153 else
154 {
155 // Add another line to the message already in the buffer 'aMessage'
156 aMessage += '\n';
157 aLeftSpaces -= aFirstLeftSpaces;
158 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
159 aMessage += aString;
160 break;
161 }
162 case MsgFile_WaitingMessage:
163 if (isKeyword == Standard_False)
164 {
165 aMessage = aString;
166 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
167 aState = MsgFile_WaitingMoreMessage;
168 break;
169 }
170 // Pass from here to 'case MsgFile_WaitingKeyword'
171 case MsgFile_WaitingKeyword:
172 if (isKeyword)
173 {
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;
179 }
180 break;
181 default:
182 break;
183 }
184 }
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;
189}
190
191//=======================================================================
192//function : GetFileSize
193//purpose :
194//=======================================================================
195
196static Standard_Integer GetFileSize (FILE *theFile)
197{
198 if ( !theFile ) return -1;
199
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;
205
206 return (Standard_Integer) nRealFileSize;
207}
208
209//=======================================================================
210//function : LoadFile
211//purpose : Load the list of messages from a file
212//=======================================================================
213
214Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
215{
216 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
217
218 // Open the file
94708556 219 FILE *anMsgFile = OSD_OpenFile(theFileName,"rb");
96f3bacc 220 if (!anMsgFile)
221 return Standard_False;
7fd59977 222
96f3bacc 223 const Standard_Integer aFileSize = GetFileSize (anMsgFile);
224 NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
225 if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2))
7fd59977 226 {
227 fclose (anMsgFile);
228 return Standard_False;
229 }
96f3bacc 230
231 char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
232 const Standard_Integer nbRead =
233 static_cast<Standard_Integer>( fread(anMsgBuffer, 1, aFileSize, anMsgFile) );
234
7fd59977 235 fclose (anMsgFile);
236 if (nbRead != aFileSize)
237 return Standard_False;
96f3bacc 238
7fd59977 239 anMsgBuffer[aFileSize] = 0;
96f3bacc 240 anMsgBuffer[aFileSize + 1] = 0;
7fd59977 241
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 )
246 {
96f3bacc 247 Standard_ExtCharacter* aUnicodeBuffer =
248 reinterpret_cast<Standard_ExtCharacter*>(&anMsgBuffer[2]);
7fd59977 249 // Convert Unicode representation to order adopted on current platform
250#if defined(__sparc) && defined(__sun)
251 if ( isLittleEndian )
252#else
253 if ( isBigEndian )
254#endif
255 {
256 // Reverse the bytes throughout the buffer
96f3bacc 257 const Standard_ExtCharacter* const anEnd =
258 reinterpret_cast<const Standard_ExtCharacter* const>(&anMsgBuffer[aFileSize]);
259
260 for (Standard_ExtCharacter* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++)
7fd59977 261 {
96f3bacc 262 unsigned short aWord = *aPtr;
263 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
7fd59977 264 }
265 }
266 return ::loadFile (aUnicodeBuffer);
267 }
268 else
96f3bacc 269 return ::loadFile (anMsgBuffer);
7fd59977 270}
271
272//=======================================================================
273//function : LoadFromEnv
274//purpose :
275//=======================================================================
276void Message_MsgFile::LoadFromEnv
277 (const Standard_CString envname,
278 const Standard_CString filename,
279 const Standard_CString ext)
280{
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();
287 }
288 if (!extname || extname[0] == '\0') extname = "us";
289
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());
297 }
298 }
299 if (extname[0] != '.') filestr.AssignCat ('.');
300 filestr.AssignCat (extname);
301
302 Message_MsgFile::LoadFile (filestr.ToCString());
303}
304
305//=======================================================================
306//function : AddMsg
307//purpose : Add one message to the global table. Fails if the same keyword
308// already exists in the table
309//=======================================================================
310
311Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
312 const TCollection_ExtendedString& theMessage)
313{
314 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 315
316 Standard_Mutex::Sentry aSentry (theMutex);
7fd59977 317 aDataMap.Bind (theKeyword, theMessage);
318 return Standard_True;
319}
320
321//=======================================================================
322//function : getMsg
323//purpose : retrieve the message previously defined for the given keyword
324//=======================================================================
325
326const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
327{
328 TCollection_AsciiString aKey((char*)theKeyword);
329 return Msg (aKey);
330}
331
332//=======================================================================
c67cd62e 333//function : HasMsg
334//purpose :
335//=======================================================================
336
337Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
338{
339 Standard_Mutex::Sentry aSentry (theMutex);
340 return ::msgsDataMap().IsBound (theKeyword);
341}
342
343//=======================================================================
344//function : Msg
7fd59977 345//purpose : retrieve the message previously defined for the given keyword
346//=======================================================================
347
348const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
349{
350 // find message in the map
351 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 352 Standard_Mutex::Sentry aSentry (theMutex);
353
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))
357 {
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
fa523cdd 364 }
c67cd62e 365
366 return aDataMap (theKeyword);
7fd59977 367}