Commit | Line | Data |
---|---|---|
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 | ||
31 | DEFINE_BASECOLLECTION(Message_CollectionOfExtendedString, TCollection_ExtendedString) | |
32 | DEFINE_DATAMAP(Message_DataMapOfExtendedString, | |
33 | Message_CollectionOfExtendedString, | |
34 | TCollection_AsciiString, | |
35 | TCollection_ExtendedString) | |
36 | ||
37 | static Message_DataMapOfExtendedString& msgsDataMap () | |
38 | { | |
39 | static Message_DataMapOfExtendedString aDataMap; | |
40 | return aDataMap; | |
41 | } | |
42 | ||
43 | typedef 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 | ||
57 | Standard_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 | ||
88 | template <class _Char> static inline Standard_Boolean | |
89 | getString (_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 | ||
138 | template <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 | ||
199 | static 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 | ||
217 | Standard_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 | //======================================================================= | |
283 | void 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 | ||
318 | Standard_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 | ||
333 | const 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 | ||
344 | const 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 | } |