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 |
27 | typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString; |
7fd59977 |
28 | |
29 | static 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 |
36 | static Standard_Mutex theMutex; |
37 | |
7fd59977 |
38 | typedef 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 | |
52 | Standard_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 | |
83 | template <class _Char> static inline Standard_Boolean |
84 | getString (_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 | |
133 | template <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 | |
194 | static 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 | |
212 | Standard_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 | //======================================================================= |
278 | void 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 | |
313 | Standard_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 | |
328 | const 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 | |
339 | Standard_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 | |
350 | const 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 | } |